Code Monkey home page Code Monkey logo

dancer2's Introduction

Perl Dancer logo

Dancer2 is a lightweight yet powerful web application framework written in Perl.
Tutorial · Manual · Discussion Forums · Public Wiki · Mailing List


Dancer2 is the evolution of Dancer and is based on on Moo, a lightweight object framework for Perl.

Dancer2 can optionally use XS modules for speed, but at its core remains fatpackable (via App::FatPacker), allowing you to easily deploy Dancer2 applications in environments that do not support custom installations of CPAN modules.

Dancer2 is easy and fun:

use Dancer2;
get '/' => sub { "Hello World" };
dance;

Documentation Index

Documentation for Dancer2 is split into several sections:

  • Dancer2 Tutorial

    If you've never danced before, you should start by reading our tutorial.

  • Manual

    The Dancer2::Manual is the definitive reference for Dancer2. Here you will find information on the concepts of Dancer2 application development and a comprehensive reference to the Dancer2 domain specific language.

  • Keyword Guide

    The complete list of keywords for Dancer2 is documented in the DSL Keywords guide.

  • Deployment

    For configuration examples of different deployment solutions involving Dancer2 and Plack, refer to the deployment guide.

  • Cookbook

    Specific examples of code for real-life problems and some 'tricks' for applications in Dancer2 can be found in the Cookbook

  • Configuration

    For configuration file details refer to Dancer2::Config. It is a complete list of all configuration options.

  • Plugins

    Refer to Dancer2::Plugins for a curated list of Dancer2 plugins, or search MetaCPAN for a complete list.

    For information on how to author a plugin, see "Writing the plugin" in Dancer2::Plugin.

  • Dancer2 Migration Guide

    The migration guide provides the most up-to-date instructions on how to convert a Dancer (1) based application to Dancer2.

Other Documentation

  • Core and Community Policy, and Standards of Conduct

    The Dancer core and community policy, and standards of conduct defines what constitutes acceptable behavior in our community, what behavior is considered abusive and unacceptable, and what steps will be taken to remediate inappropriate and abusive behavior. By participating in any public forum for Dancer or its community, you are agreeing to the terms of this policy.

  • GitHub Wiki

    Our wiki has community-contributed documentation, as well as other information that doesn't quite fit within this manual.

  • Contributing

    The contribution guidelines describe how to set up your development environment to contribute to the development of Dancer2, Dancer2's Git workflow, submission guidelines, and various coding standards.

  • Deprecation Policy

    The deprecation policy defines the process for removing old, broken, unused, or outdated code from the Dancer2 codebase. This policy is critical for guiding and shaping future development of Dancer2.

Security Reports

If you need to report a security vulnerability in Dancer2, send all pertinent information to [email protected], or report it via the GitHub security tool. These reports will be addressed in the earliest possible timeframe.

Support

You are welcome to join our mailing list. For subscription information, mail address and archives see http://lists.preshweb.co.uk/mailman/listinfo/dancer-users.

We are also on IRC: #dancer on irc.perl.org.

Authors

Dancer Core Team

Alberto Simões
Alexis Sukrieh
D Ruth Holloway (GeekRuthie)
Damien Krotkine
David Precious
Franck Cuny
Jason A. Crome
Mickey Nasriachi
Peter Mottram (SysPete)
Russell Jenkins
Sawyer X
Stefan Hornburg (Racke)
Yanick Champoux

Core Team Emeritus

David Golden
Steven Humphrey

Contributors

A. Sinan Unur
Abdullah Diab
Achyut Kumar Panda
Ahmad M. Zawawi
Alex Beamish
Alexander Karelas
Alexander Pankoff
Alexandr Ciornii
Andrew Beverley
Andrew Grangaard
Andrew Inishev
Andrew Solomon
Andy Jack
Ashvini V
B10m
Bas Bloemsaat
baynes
Ben Hutton
Ben Kaufman
biafra
Blabos de Blebe
Breno G. de Oliveira
cdmalon
Celogeek
Cesare Gargano
Charlie Gonzalez
chenchen000
Chi Trinh
Christian Walde
Christopher White
cloveistaken
Colin Kuskie
cym0n
Dale Gallagher
Dan Book (Grinnz)
Daniel Böhmer
Daniel Muey
Daniel Perrett
Dave Jacoby
Dave Webb
David (sbts)
David Steinbrunner
David Zurborg
Davs
Deirdre Moran
Dennis Lichtenthäler
Dinis Rebolo
dtcyganov
Elliot Holden
Emil Perhinschi
Erik Smit
Fayland Lam
ferki
Gabor Szabo
GeekRuthie
geistteufel
Gideon D'souza
Gil Magno
Glenn Fowler
Graham Knop
Gregor Herrmann
Grzegorz Rożniecki
Hobbestigrou
Hunter McMillen
ice-lenor
Ivan Bessarabov
Ivan Kruglov
JaHIY
Jakob Voss
James Aitken
James Raspass
James McCoy
Jason Lewis
Javier Rojas
Jean Stebens
Jens Rehsack
Joel Berger
Johannes Piehler
Jonathan Cast
Jonathan Scott Duff
Joseph Frazer
Julien Fiegehenn (simbabque)
Julio Fraire
Kaitlyn Parkhurst (SYMKAT)
kbeyazli
Keith Broughton
lbeesley
Lennart Hengstmengel
Ludovic Tolhurst-Cleaver
Mario Zieschang
Mark A. Stratman
Marketa Wachtlova
Masaaki Saito
Mateu X Hunter
Matt Phillips
Matt S Trout
mauke
Maurice
MaxPerl
Ma_Sys.ma
Menno Blom
Michael Kröll
Michał Wojciechowski
Mike Katasonov
Mohammad S Anwar
mokko
Nick Patch
Nick Tonkin
Nigel Gregoire
Nikita K
Nuno Carvalho
Olaf Alders
Olivier Mengué
Omar M. Othman
pants
Patrick Zimmermann
Pau Amma
Paul Clements
Paul Cochrane
Paul Williams
Pedro Bruno
Pedro Melo
Philippe Bricout
Ricardo Signes
Rick Yakubowski
Ruben Amortegui
Sakshee Vijay (sakshee3)
Sam Kington
Samit Badle
Sebastien Deseille (sdeseille)
Sergiy Borodych
Shlomi Fish
Slava Goltser
Snigdha
Steve Bertrand
Steve Dondley
Steven Humphrey
Tatsuhiko Miyagawa
Timothy Alexis Vass
Tina Müller
Tom Hukins
Upasana Shukla
Utkarsh Gupta
Vernon Lyon
Victor Adam
Vince Willems
Vincent Bachelier
xenu
Yves Orton

Author

Dancer Core Developers

Copyright and License

This software is copyright (c) 2024 by Alexis Sukrieh.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

dancer2's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dancer2's Issues

Dancer::Test missing various features

Dancer::Test in Dancer2 is missing lots of the tests provided by Dancer::Test from Dancer1 (e.g. response_content_like, response_content_is_deeply, response_is_file...

Is this by design, or just waiting for them to be added?

I'm assuming we don't want to dump useful features and break everyone's test script?

Dancer::Plugin - need "use Dancer"

The dancer plugin should not need "use Dancer", because we don't always add route.

When I try the Redis plugin, they return a redis object, no route, a simple Dancer::Plugin.

But they don't "use Dancer".

So the plugin doesn't work, should be the case for most of them.

I got this error :

Can't locate object method "dancer_app" via package "Dancer::Plugin::Redis" at vendors/Dancer/lib/Dancer/Plugin.pm line 29, line 16.

Here a sample :

#!perl

BEGIN {
    push @INC, glob("vendors/*/lib");
}


use Dancer;
use Dancer::Plugin::Redis;
set serializer => 'JSON';

get '/' => sub {
    redis->get('test');
    return {};
};

start;

I use this plugin :
git://github.com/geistteufel/Dancer-Plugin-Redis.git

is it mandatory to do "use Dancer" ? I think it's a bug.

Make a TODO test suite using Task::Dancer

If Task::Dancer is found, run a test suite that will test various plugins and other modules known to work in dancer 1. Marking it as TODO will allow them to fail and overall testing to pass.

Session destruction hooks

Hi,

It'd be neat if there were a way to run code when a session is destroyed.

Use case: I have a plugin that runs an external utility or a coderef in a forked process. The fork's STDOUT and STDERR are redirected to files, and the plugin provides facilities to report new output in these files. Because these external processes are per-user (only the user that started the fork can get its output), the path to the output files is stored in the session. When the session is destroyed, the output files are no longer necessary and so should be removed. The cleanest way to do this is to run cleanup code when a session is destroyed.

It looks like that would be fairly easy to implement in Dancer 2, but less so in Dancer (Dancer::Session::Abstract does not run any code on session destruction, destroy() is entirely implemented in the subclasses, and no Moo so no method decorators).

Dancer::Test backward compatibility issue

Dancer::Test in Dancer 1 expects to receive a hash in @_ whereas in D2 it takes an array.

It makes impossible for a test to be fully compliant with both versions when doing somehting like:

use Dancer::Test "SomeApp";

This will result in an error in Dancer 1 wiht "odd number of elements".

The following bits are necessary: (taken from Dancer::Plugin::Redis)

BEGIN {
    require Dancer::Test;
    if ( dancer_version ge '1.99' ) {
        diag "Dancer 2";
        Dancer::Test->import('TestApp');
    }
    else {
        diag "Dancer 1";
        Dancer::Test->import();
    }
}

D1 should probably be patched to accept an Array and the issue would be solved.

SessionFactory needs to pass session_* options to Session->new()

SessionFactory should be picking up session_* options (like 'session_expires') and passing them in the call to Dancer::Core::Session->new.

I can't follow how configuration is being handled so I'm not sure how that should happen. In D::C::Role::Config, I see a default being set for 'session_dir' and it looks like that might eventually flow through to the call to construct a Data::Session::Foo, but that's very hardcoded and requires Data::Session::YAML to have a corresponding property.

I think SessionFactory needs some sort of config hash property that stores any options and then translate them appropriately in create() for Dancer::Core::Session->new(). Or, SessionFactory should have individual properties for all the standard "session_*" options so that it can pass them on.

Sessions broken in Dancer2

I don't have the time to investigate further right now, but it seems that the session are not working well under Dancer 2.

the symptom I've seen is that the session is not properly created when multiple users use the webapp, I'm not sure exactly where the problem is, but I have the feeling that the code in Dancer::Core::App is bad, in the session method.

It looks like we use the same session object everywhere:

sub session {
    my ($self, $key, $value) = @_; 

    my $engine = $self->engine('session');
    my $session;

    # fetch any existing session
    my $cookie = $self->context->cookie($engine->name);
    if (defined $cookie && defined $cookie->value) {
        my $existing_session = $engine->retrieve($cookie->value);
        $session = $existing_session if defined $existing_session;
    }   

    # create a new session
    if (! defined $session) {
        $session = $engine; # <----- WTF ???
        $session->flush; # NOW?
        $self->context->response->push_header(
            'Set-Cookie' => $session->cookie->to_header);
    }   

    # want the full session?
    return $session->data if @_ == 1;

    # session accessors
    return @_ == 3
        ? $session->write($key => $value)
        : $session->read($key);
}

I really have the feeling that it's a copy-paste from Dancer1 and that it does not work well now, with Dancer2.

We need a test script that plays a lot with session, with multiple users, and we shoud make sure that:

  • there is no session leaks when more than one user uses the website
  • there is only one session created per user
  • we retreive the session successfully when a valid session id is present in dancer.session

can't do `dzil test`

Windows XP
Dist::Zilla 4.300000 and 4.300003

[DZ] attempt to add README multiple times; added by: @Filter/GatherDir (Dist::Zilla::Plugin::GatherDir line 103); @Filter/Readme (Dist::Zilla::Plugin::Readme line 39)
[DZ] attempt to add Makefile.PL multiple times; added by: @Filter/GatherDir (Dist::Zilla::Plugin::GatherDir line 103); @Filter/MakeMaker (Dist::Zilla::Plugin::MakeMaker line 198)
aborting; duplicate files would be produced at C:/strawberry514/perl/site/lib/Dist/Zilla/App/Command/test.pm line 27

(for 4.300003: "Dist::Zilla::Plugin::MakeMaker line 201")

How should Dancer 2 handle session objects regarding multiple apps?

I think I've identified an interesting backward incompatibility with Dancer 2
I'm not sure it's a bug, but I'm sure it will confuse people

In Dancer 1, everything is shared among all packages of the app (that's the main design issue we wanted to fix), so if I have Foo and Bar, they share the same session object

In Dancer 2, everything is scoped within each caller, so this isn't possible anymore (sharing the same session across multiple packages).

For instance, if my webapp is composed of two packages, Foo and Bar, there will be two sessions defined, one for Foo, one for Bar. That is on purpose, that's the main design change we wanted : poper per-app encapsulation, but now, on the user's point of view, shouldn't we provide a way to handle shared objects among apps?

I think that for the session example, it speaks for itself.

Maybe we should just clarify that in the documentation, or maybe we should provide a way to declare something "global".

Feedback welcome.

Exception hooks missing

Invalid hook name `on_handler_exception' at /home/racke/dancer2/lib/Dancer/Core/DSL.pm line 123.
Compilation failed in require at ./bin/app.pl line 3.
BEGIN failed--compilation aborted at ./bin/app.pl line 3.

The same problem with on_route_exception.

Regards
Racke

make uri_for receive a name for a route

This is something for the wishlist, not an issue itself.

Right now uri_for converts a relative path to an absolute path and attaches parameters if necessary.

In case you have to change a path in your controller, you have to search all your templates, to find where this path was used and change your templates accordingly.

My idea would be to provide a name for a route and use that name to identify the route to generate a uri. Something along the lines:

# in the controller
get '/product/:id' as 'product' => sub { ... }

# OR
get '/product/:id' called 'product' => sub { ... }


# meanwhile in the template ...
<a href="[% uri_for('product', { id => 5 }) %]">...</a>
<a href="[% uri_for('product', { id => 6, special_price => 1 }) %]">...</a> 

# ... becomes
<a href="/product/5">...</a>
<a href="/product/6?special_price=1">...</a>

The Benefit would be, that we can change the route in our controller without having to search our templates for where this route was used. Like so:

# in the controller
get '/product/:id/:special_price?' as 'product' => sub { ... }


# no change in the template is required, but now ...
<a href="[% uri_for('product', { id => 6, special_price => 1 }) %]">...</a>  

# ... would yield
<a href="/product/6/1">...</a>

The idea is, to decouple the uris in templates from routing in the controller, so that you are more flexible to change your routes. And to make uri generation more comfortable.

Unescaped left brace in regex is deprecated, passed through in regex / perl 5.17.6

Multiple instances of this problem across the codebase in 5.17.

One of these seems to fail and tries to call a method on a stringified glob :/

Fortunately, the largest problem is Dancer::Logger::Abstract , but I can't see that .pm in the git tree anywhere, so hopefully its "already solved".

Will re-attempt with a git version just in case, and the update with findings.

t/00_base/08_pod_coverage_dancer.t ............. skipped: Test::Pod::Coverage 1.08 required for testing POD coverage
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/
        (?:
            \%{ <-- HERE (.+?)}([a-z])|
            \%([a-zA-Z])
        )
    / at /ext/graft/home/webadmin/.cpanm/work/1355530664.19199/Dancer-1.3110/lib/Dancer/Logger/Abstract.pm line 132, <DATA> line 1000.
t/00_base/09_load_app.t ........................ 
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/^{{ <-- HERE \$NEXT}}$|^\d\.\d{4}(_\d{2}   |      )\d{2}.\d{2}.\d{4}$/ at t/00_base/14_changelog.t line 105.
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/^({{ <-- HERE \$NEXT}})$|^\d\.\d{4}(_\d{2}   |      )\d{2}.\d{2}.\d{4}$/ at t/00_base/14_changelog.t line 106.
t/00_base/14_changelog.t
t/01_config/06_config_api.t .................... skipped: YAML needed to run this tests
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/^\s*eval { <-- HERE ...} called at (?:[.]/)?t/01_config/06_stack_trace.t/ at t/01_config/06_stack_trace.t line 28, <DATA> line 1000.
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/^\s*eval { <-- HERE ...} called at (?:[.]/)?t/01_config/06_stack_trace.t/ at t/01_config/06_stack_trace.t line 52, <DATA> line 1000.
t/01_config/06_stack_trace.t ..................
t/02_request/13_ajax.t ......................... skipped: Test::TCP is needed to run this test
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/
        (?:
            \%{ <-- HERE (.+?)}([a-z])|
            \%([a-zA-Z])
        )
    / at /ext/graft/home/webadmin/.cpanm/work/1355530664.19199/Dancer-1.3110/lib/Dancer/Logger/Abstract.pm line 132, <DATA> line 1000.
Can't locate object method "read" via package "GLOB(0x2c3df00)" (perhaps you forgot to load "GLOB(0x2c3df00)"?) at /ext/graft/home/webadmin/.cpanm/work/1355530664.19199/Dancer-1.3110/lib/Dancer/Request.pm line 524.
# Looks like you planned 21 tests but ran 17.
# Looks like your test exited with 255 just after 17.
t/02_request/14_uploads.t ...................... 
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/\[\d+\]  warn @.+> { <-- HERE 'this' => 'that'} in/ at t/11_logger/08_serialize.t line 23, <DATA> line 1000.
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/\[\d+\]  warn @.+> { <-- HERE 'a' => 2,'b' => 1,'c' => 5,'d' => 4,'e' => 3}/ at t/11_logger/08_serialize.t line 35, <DATA> line 
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/"foo" : { <-- HERE / at t/14_serializer/01_helpers.t line 71, <DATA> line 1000.
t/14_serializer/01_helpers.t ...................

t/hooks.t

Currently, i have only 2 more tests failing. I can't fix them. I will post an issue for each.

t/hooks.t ................. 1/?
    #   Failed test 'before_file_render was called'
    #   at t/hooks.t line 82.
    #          got: '2'
    #     expected: '1'
    #   Failed test 'after_file_render was called'
    #   at t/hooks.t line 83.
    #          got: undef
    #     expected: '1'
    #   Failed test 'before_file_render was called'
    #   at t/hooks.t line 86.
    #          got: '3'
    #     expected: '2'
t/hooks.t ................. 3/?
    #   Failed test 'after_file_render was called'
    #   at t/hooks.t line 87.
    #          got: '1'
    #     expected: '2'
    # Looks like you failed 4 tests of 4.
#   Failed test 'file render hooks'
#   at t/hooks.t line 88.
# Looks like you failed 1 test of 4.

As far as I can see before_file_render gets called only once, before_file_render gets called twice etc. I have no clue what's going on.

keyword "delete" doesn't work

I have test the DELETE keyword.

"delete" is a perl core command, it's used for delete a key in a hash or an array, so we can't use it in the keywords name space.

This code give me an error :

use Dancer;
set serializer => 'JSON';

delete '/login' => sub {
return { response => 'OK' };
};

start;

sessions need a way to delete keys

There is no easy way to remove a key from the session data hash. This means sessions grow and never shrink.

Some options:

(1) Assigning "undef" to a key deletes it. Since C<session $unknown_key> returns undef, the key can be deleted instead of set to undef without changing the return value. This is the cleanest DSL (if very slightly surprising behavior).

(2) The session function could take a parameter hashref for things other than pure get/set. Either C<session foo => { clear => 1 }> or C<session { name => "foo", clear => 1 }>.

I would favor (1) because it's the prettiest and DWIMs.

session can't be use in plugin

I have made a simple test :

 register redis => sub {
     my ($name) = @_;
     $_settings ||= plugin_setting;
    session('redis' => 1);
     return _handle($name);
 };

I have this error :

Can't call method "cookie" on an undefined value at vendors/Dancer/lib/Dancer/Core/App.pm line 75, <DATA> line 16.

Most CPAN session modules won't work with Dancer 2

Anything that gets config data from dancer1's Dancer::Config or Dancer::SharedData won't work with dancer2. From going through Task::Dancer, this includes:

  • Dancer::Session::CHI
  • Dancer::Session::Cookie
  • Dancer::Session::KiokuDB
  • Dancer::Session::Memcached
  • Dancer::Session::MongoDB
  • Dancer::Session::PSGI
  • Dancer::Session::Storable

Perhaps adding the old packages for legacy support would be in order?

Support for is_behind_proxy is broken

When an application is deployed begind a proxy (starman + apache for instance) the is_behind_proxy setting is supposed to tell Dancer that it should look at FROWARDED_XXXX headers for building the hostname and the scheme of the URL.

When the frontend is on SSL, this bug makes it quite impossible to run the app:

In Dancer 2, that setting is not used, that is a major bug, because:

  • redirect is broken (when building canonical URI the https scheme is lost, for instance)
  • if request->host is used, it will return localhost which is obviously wrong here.

All the code is there, it appears that a small bit is lacking to make that actually work.

Missing keys for DSL for plugin

They is a new issue now, I have to replace this :

$_settings ||= plugin_setting;

by this

$_settings ||= config;

is it possible to add some key in the DSL to be compatible :

  • plugin_setting
  • plugin_settings

I will work on RBAC plugin, I thing they is over plugin issue, I have to investigate

Prefix undef Issue

In Dancer < 2 you could do

prefix '/something';

# Will respond to /something/url
get '/url' => sub {
    ...
};

# Turn off the prefix
prefix undef;

# Will respond to /url
get '/url' => sub {

};

In the latest Dancer2, prefix undef; gives me this:

Use of uninitialized value within @_ in join or string at Dancer/Core/Role/DSL.pm line 61.

Then trying prefix ''; to remedy this gives me this:

The value `' does not pass the type constraint check for type `DancerPrefix' at Dancer/Core/App.pm line 415

So, of course, prefix '/'; is needed to fix the problem, and then things work.

I'm not sure if this is a known issue/incompatibility, or something that should be addressed to help with backwards compatibility, but it's something that we ran into when testing out Dancer2 with a Dancer1 app.

any keyword needs explicit HTTP methods in Dancer 2

The documented behaviour of Dancer 1 is support for route handlers that would match any HTTP methods. This doesn't work with Dancer 2:

any must be given an ArrayRef of HTTP methods at /home/racke/dancer2/lib/Dancer/Core/Role/DSL.pm line 59.

Regards
Racke

execute_hooks and register_hook

Plugins have execute_hooks and register_hook. First, I think it should be the other way around (usually we execute only one hook, and we register a bunch of them). But as @bigpresh pointed out, there is no real advantage of having a plural on this methods. Therefore, I would suggest normalizing to the singular.

Remove entries from session

In order to invalidate entries in the sessions, you have to set them to undef in Dancer 1:

session user => undef;

It would be much cleaner and more obvious to say:

session->delete('user');

As we need to change the existing session engines for Dancer 2, this would be a good time
to introduce this change.

Regards
Racke

session_dir doesn't work

I have try to use Dancer-Plugin-Auth-RBAC and I face to an issue,
session_dir seems doesn't work.

I have made 3 tests :

first config :

config.yml:

session: YAML
session_dir: /tmp/test

=> test is not created, and session still store in $APPDIR/sessions

then another conf :

session: YAML
engines:
  session:
    session_dir: /tmp/test

=> still doesn't work, same as before

I try it manually :

#!/usr/bin/env perl

# PODNAME: app
# ABSTRACT: Dancer starter app

BEGIN {
    push @INC, glob("vendors/*/lib");
}

use strict;
use warnings;
use 5.014;
use Carp;

# VERSION

use Dancer 1.9999;
use Dancer::Session::YAML;

session->session_dir('/tmp/cgTokenApi');

get '/' => sub {
    to_yaml(session());
};

get '/write/:a/:b' => sub {
    session params->{a} => params->{b};
};

start;

=> I have the same issue than the RBAC plugin :

Can't call method "cookie" on an undefined value at vendors/Dancer2/lib/Dancer/Core/App.pm line 75, <DATA> line 16.

and 'session_dir' directly doesn't work, it's not a keyword.

did I miss something ?

Meta issues

I am trying to execute a 'meta->calculate_all_roles' on all of D2's classes which have meta. I get two somewhat mysterious errors.

  1. Dancer::Core::App. But it's related to the role Dancer::Core::Role::Config. Every object which consumes this role fails with the same error message. Following trace is done with Carp::Always. It's a bit long, but I believe it helps.

  2. Dancer::Core::Request gives me a shorter trace

Any ideas? I wouldn't be surprised if it's a Moo issue.

First issue (updated to current version of Moo. Probably different line numbers; essentially the same)

Use of uninitialized value $type_constraint_name in substitution (s///) 
at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 294, <DATA> line 998.

Moose::Util::TypeConstraints::normalize_type_constraint_name(undef) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 271

Moose::Util::TypeConstraints::find_or_parse_type_constraint(undef) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 260

Moose::Util::TypeConstraints::find_or_create_isa_type_constraint(undef, 
'HASH(0x80ee8d20)') called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-
threads-64int/Moose/Meta/Attribute.pm line 369

Moose::Meta::Attribute::_process_isa_option('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 296

Moose::Meta::Attribute::_process_options('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 88
        Moose::Meta::Attribute::new('Moose::Meta::Attribute', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'isa', undef, 'builder', ...) called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 114

Moose::Meta::Attribute::interpolate_class_and_new('Moose::Meta::Attribut
e', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 704

Moose::Meta::Class::_process_new_attribute('Moose::Meta::Class=HASH(0x80
e09b38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 697

Moose::Meta::Class::_process_attribute('Moose::Meta::Class=HASH(0x80e09b
38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 566

Moose::Meta::Class::add_attribute('Moose::Meta::Class=HASH(0x80e09b38)', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose.pm line 124
        Moo::HandleMoose::inject_real_metaclass_for('Dancer::Core::App') 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose/FakeMetaClass.pm 
line 8

Moo::HandleMoose::FakeMetaClass::AUTOLOAD('Moo::HandleMoose::FakeMetaCla
ss=HASH(0x801d9998)') called at t/meta.t line 38
        main::wanted() called at /usr/lib/perl5/5.14/File/Find.pm line 
781
        File::Find::_find_dir('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib', 1) called at 
/usr/lib/perl5/5.14/File/Find.pm line 569
        File::Find::_find_opt('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at 
/usr/lib/perl5/5.14/File/Find.pm line 1070
        File::Find::find('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at t/meta.t line 18
Use of uninitialized value $_[0] in pattern match (m//) at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 695, <DATA> line 998.

Moose::Util::TypeConstraints::_detect_type_constraint_union(undef) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 274

Moose::Util::TypeConstraints::find_or_parse_type_constraint(undef) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 260

Moose::Util::TypeConstraints::find_or_create_isa_type_constraint(undef, 
'HASH(0x80ee8d20)') called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-
threads-64int/Moose/Meta/Attribute.pm line 369

Moose::Meta::Attribute::_process_isa_option('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 296

Moose::Meta::Attribute::_process_options('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 88
        Moose::Meta::Attribute::new('Moose::Meta::Attribute', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'isa', undef, 'builder', ...) called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 114

Moose::Meta::Attribute::interpolate_class_and_new('Moose::Meta::Attribut
e', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 704

Moose::Meta::Class::_process_new_attribute('Moose::Meta::Class=HASH(0x80
e09b38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 697

Moose::Meta::Class::_process_attribute('Moose::Meta::Class=HASH(0x80e09b
38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 566

Moose::Meta::Class::add_attribute('Moose::Meta::Class=HASH(0x80e09b38)', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose.pm line 124
        Moo::HandleMoose::inject_real_metaclass_for('Dancer::Core::App') 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose/FakeMetaClass.pm 
line 8

Moo::HandleMoose::FakeMetaClass::AUTOLOAD('Moo::HandleMoose::FakeMetaCla
ss=HASH(0x801d9998)') called at t/meta.t line 38
        main::wanted() called at /usr/lib/perl5/5.14/File/Find.pm line 
781
        File::Find::_find_dir('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib', 1) called at 
/usr/lib/perl5/5.14/File/Find.pm line 569
        File::Find::_find_opt('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at 
/usr/lib/perl5/5.14/File/Find.pm line 1070
        File::Find::find('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at t/meta.t line 18
Use of uninitialized value $_[0] in pattern match (m//) at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 674, <DATA> line 998.

Moose::Util::TypeConstraints::_detect_parameterized_type_constraint(unde
f) called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 274

Moose::Util::TypeConstraints::find_or_parse_type_constraint(undef) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Util/TypeConstraints.pm line 260

Moose::Util::TypeConstraints::find_or_create_isa_type_constraint(undef, 
'HASH(0x80ee8d20)') called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-
threads-64int/Moose/Meta/Attribute.pm line 369

Moose::Meta::Attribute::_process_isa_option('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 296

Moose::Meta::Attribute::_process_options('Moose::Meta::Attribute', 
'config_location', 'HASH(0x80c30c80)') called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 88
        Moose::Meta::Attribute::new('Moose::Meta::Attribute', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'isa', undef, 'builder', ...) called at 
/usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Attribute.pm line 114

Moose::Meta::Attribute::interpolate_class_and_new('Moose::Meta::Attribut
e', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 704

Moose::Meta::Class::_process_new_attribute('Moose::Meta::Class=HASH(0x80
e09b38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 697

Moose::Meta::Class::_process_attribute('Moose::Meta::Class=HASH(0x80e09b
38)', 'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/i686-cygwin-threads-
64int/Moose/Meta/Class.pm line 566

Moose::Meta::Class::add_attribute('Moose::Meta::Class=HASH(0x80e09b38)', 
'config_location', 'reader', 'config_location', 'init_arg', 
'config_location', 'builder', '_build_config_location', 'isa', ...) 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose.pm line 124
        Moo::HandleMoose::inject_real_metaclass_for('Dancer::Core::App') 
called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose/FakeMetaClass.pm 
line 8

Moo::HandleMoose::FakeMetaClass::AUTOLOAD('Moo::HandleMoose::FakeMetaCla
ss=HASH(0x801d9998)') called at t/meta.t line 38
        main::wanted() called at /usr/lib/perl5/5.14/File/Find.pm line 
781
        File::Find::_find_dir('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib', 1) called at 
/usr/lib/perl5/5.14/File/Find.pm line 569
        File::Find::_find_opt('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at 
/usr/lib/perl5/5.14/File/Find.pm line 1070
        File::Find::find('HASH(0x8002bea8)', 
'/home/maurice/projects/dancer2/t/../lib') called at t/meta.t line 18

Second Issue

Can't call method "coercion" on an undefined value at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose.pm line 103, <DATA> line 998.
        Moo::HandleMoose::inject_real_metaclass_for('Dancer::Core::Request') called at /usr/lib/perl5/site_perl/5.14/Moo/HandleMoose/FakeMetaClass.pm line 8
        Moo::HandleMoose::FakeMetaClass::AUTOLOAD('Moo::HandleMoose::FakeMetaClass=HASH(0x8104b7f8)') called at t/meta.t line 38
        main::wanted() called at /usr/lib/perl5/5.14/File/Find.pm line 781
        File::Find::_find_dir('HASH(0x8002bea8)', '/home/maurice/projects/dancer2/t/../lib', 1) called at /usr/lib/perl5/5.14/File/Find.pm line 569
        File::Find::_find_opt('HASH(0x8002bea8)', '/home/maurice/projects/dancer2/t/../lib') called at /usr/lib/perl5/5.14/File/Find.pm line 1070
        File::Find::find('HASH(0x8002bea8)', '/home/maurice/projects/dancer2/t/../lib') called at t/meta.t line 18

many session IDs for one request, many session cookies

When a session engine is used, only one session ID should be generated for one client. In its current state, Dancer 2 generates many IDs instead of one.

Also the session cookie is sent multiple times, because it's sent by the session() keyword, which is stupid.

The session cookie should be sent by a hook, at the after position. That way, we garantee that it's sent everytime, only once.

Example of the issue:

HTTP/1.1 302 Found
Location: /authenticate
Server: Perl Dancer
Content-Length: 13
Content-Type: text/html; charset=UTF-8
Set-Cookie: dancer.session=355114976560460348873761201465147701; HttpOnly
Set-Cookie: dancer.session=926245184583857139746119853561137527; HttpOnly
Set-Cookie: dancer.session=39764869161177714189817938171433000; HttpOnly
Set-Cookie: dancer.session=77583565451611912592786507691088855; HttpOnly
Set-Cookie: dancer.session=25684046907983786194765900223470272; HttpOnly
Set-Cookie: dancer.session=764796311250260850500007391395537591; HttpOnly
Set-Cookie: dancer.session=928059384690246973096541376409177759; HttpOnly
Set-Cookie: dancer.session=691309106693241037108205829884430950; HttpOnly
Set-Cookie: dancer.session=630076152731522905630129386124980166; HttpOnly
Set-Cookie: dancer.session=849831249698829614157786783290693453; HttpOnly
Date: Fri, 16 Nov 2012 16:52:36 GMT

Full config merging support

I think Dancer2 would be a good time to introduce full-on config merging, rather than just the top-level.

For instance, given:

#config.yml:
foo:
    bar:
        wibble: 1
        beer: 2
baz:
    test: 3

# environments/development.yml:
foo:
    bar:
        beer: 4

... after they got merged, the end result currently would be:

foo: 
    bar: beer
baz:
    test: 3

It would be nice to walk through and merge fully.

Catalyst has some code in Catalyst::Utils we could use:
https://metacpan.org/module/Catalyst::Utils#merge_hashes-hashref-hashref-

sub merge_hashes {
    my ( $lefthash, $righthash ) = @_;

    return $lefthash unless defined $righthash;

    my %merged = %$lefthash;
    for my $key ( keys %$righthash ) {
        my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
        my $left_ref  = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
        if( $right_ref and $left_ref ) {
            $merged{ $key } = merge_hashes(
                $lefthash->{ $key }, $righthash->{ $key }
            );
        }
        else {
            $merged{ $key } = $righthash->{ $key };
        }
    }

    return \%merged;
}

There's also e.g. Hash::Merge, Hash::Merge::Simple and others on CPAN, if we were willing to add a dependency for it (it's simple enough that it's probably best to just use the above Catalyst code (with attribution, of course) assuming licences permit.

POST hang up

I have try POST:

prefix '/auth';
post '/login' => sub {
my ($user, $passwd) = (params->{user}, params->{passwd});
return _bad_auth() unless defined $user && defined $passwd;
return {
response => 'OK'
}
};

when I call this :

wget -O - --post-data='user=a' http://localhost/auth/login

it hang up, wait for something but nothing come

incompatible splat

dancer 1.3 splat returns scalar.
but dancer2 splat returns array ref.

get qr{^/dancer/(\d+)$} => sub {
my ($id) = splat;
return $id;
};

get qr{^/dancer2/(\d+)$} => sub {
my ($id) = splat;
return $id->[0];
};

permission to COMMIT

Please, can I have the right to commit, I want to contribute actively.
Thanks :)

plugin keywords - Dancer 1 vs Dancer 2

Hey

In Dancer 1 we didn't receive a self object for plugin keywords (there wasn't any self).
With Dancer 2 we have a $self as first argument.

Is this the behavior we want? (just to be sure we are breaking compatibility in one more place).

@sukria, please give me feedback asap, as this is blocking my work.

Session ID hook timing and destruction

I'm looking at what it would take to port Dancer::Session::Cookie to D2.

Here are some thing I've noticed:

(1) The session ID is set in a before hook. It should be set in the after hook after flushing happens. This would allow a session cookie to flush contents into the id and then have the id set into the cookie. I can't think of a reason why the session cookie header needs to be set during the request. (The session needs to be retrieved/created, but the outgoing header should be deferrable, I would think.)

(2) The session ID is not cleared when sessions are destroyed. Destroyed sessions should have the session value set to the empty string (and sent to the browser to clear it there). Given that, session retrieval should check that the session ID has a length rather than is defined.

plugin_setting is not test

During a test on Dancer::Plugin::Redis :
[runner:92896] error @2012-12-28 15:07:51> Route exception: Dancer::Core::DSL__WITH__Dancer::Plugin::Redis=HASH(0x7fde93d1fa58) is not defined in your redis conf, please check the doc at /Users/geistteufel/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Dancer/Core/Role/DSL.pm line 74. in /Users/geistteufel/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Dancer/Core/App.pm l. 276

That mean plugin_testing need absolutly a configuration. In Dancer1 an empty conf was returned and a default conf was used by the plugin.

dist.ini / PodWeaver

I think this travis thing is awesome. Right now we don't really use it because of broken tests.

One test that breaks is pod syntax:

#   Failed test 'POD test for blib/lib/Dancer/Core/Route.pm'
291#   at /home/travis/perl5/perlbrew/perls/5.12/lib/site_perl/5.12.4/Test/Pod.pm line 182.
292# blib/lib/Dancer/Core/Route.pm (14): Unknown directive: =attr
293# blib/lib/Dancer/Core/Route.pm (26): Unknown directive: =attr
294# blib/lib/Dancer/Core/Route.pm (38): Unknown directive: =attr
295# blib/lib/Dancer/Core/Route.pm (54): Unknown directive: =attr
296# blib/lib/Dancer/Core/Route.pm (65): Unknown directive: =attr
297# blib/lib/Dancer/Core/Route.pm (116): Unknown directive: =method
298# blib/lib/Dancer/Core/Route.pm (177): Unknown directive: =method
299# Looks like you failed 2 tests of 61.
300t/release-pod-syntax.t .... Dubious, test returned 2 (wstat 512, 0x200)

That seems unnecessary. Is there a reason why we're not using podweaver? It makes pod less redundant. For what it's worth, I like podweaver. Let me see if I can come up with a PR...

Dependencies

Below a the list of dependencies from our current META.json. (Please note that it differs from the list of explicitly mentioned prereqs in dist.ini; I believe this is because we use dzil's autoprereqs plugin.)

I wonder if we really need all of the dependencies. I believe we can at least leave out we don't need JSON and JSON:XS.

Also, I wonder why MooX::Types::MooseLike is not on the list. Should we mention it in dist.ini?

 requires
  HTTP::Server::Simple::PSGI 0
  Test::More 0.92
  Capture::Tiny 0.12
  File::Find 0
  File::Temp 0.22
  Test::Fatal 0
  HTTP::Body 0
  Template 0
  YAML::Any 0
runtime
 suggests
  Fcntl 0
  YAML::Any 0
 requires
  Template::Tiny 0
  Encode 0
  MooX::Types::MooseLike::Base 0
  Test::Builder 0
  HTTP::Headers 0
  Config::Any 0
  warnings 0
  base 0
  File::Copy 0
  IO::File 0
  Data::Dumper 0
  HTTP::Body 0
  vars 0
  MooX::Types::MooseLike::Numeric 0
  strict 0
  Test::More 0
  Scalar::Util 0
  URI::Escape 0
  Fcntl 0
  File::Spec 0
  Moo::Role 0
  POSIX 0
  HTTP::Server::Simple::PSGI 0
  URI 0
  Moo 0.009014
  MIME::Types 0
  Template 0
  Exporter 5.57
  HTTP::Date 0
  Carp 0
  JSON 0
  File::Basename 0
  Cwd 0
 recommends
  Test::More 0
  Test::Builder 0
  Plack::Request 0
  JSON::XS 0
configure
 requires
  ExtUtils::MakeMaker 6.30
  Module::Build 0.3601
build
 requires
  Module::Build 0.3601

Add support for additional configuration files

Applications often require local configuration settings (e.g., database connection info, support email address, etc.) which should neither propagate back into the master copy of the app nor preclude updating the local copy when the master adds new config settings. To allow for this, Dancer should support a configuration loading order along the lines of:

config.yml, config.local,yml, [environment].yml, [environment].local.yml

In each case, the .local version would be merged into the overall configuration, being merged in essentially the same way that [environment].yml is currently overlaid onto config.yml.

Ideally, this could be generalized even further, allowing users to insert an arbitrary number of additional configuration files at any point in the config loading order, thus allowing configuration of a large number of plugins (both general Dancer plugins and app-specific plugins) without having to resort to putting everything into a single monolithic configuration.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.