├── .gitignore ├── .gitmodules ├── README ├── danceradvent ├── .gitignore ├── Makefile.PL ├── bin │ └── app.pl ├── config.yml ├── cpanfile ├── environments │ ├── development.yml │ └── production.yml ├── lib │ ├── Pod │ │ └── POM │ │ │ └── View │ │ │ └── InlineHTML.pm │ └── danceradvent.pm ├── public │ ├── 404.html │ ├── 500.html │ ├── articles │ │ ├── 2010 │ │ │ ├── 1-Dancer-1.2.pod │ │ │ ├── 10-deployment.pod │ │ │ ├── 11-Using-DBIC-with-Dancer.pod │ │ │ ├── 12-adding-a-feed-to-your-application.pod │ │ │ ├── 13-dancer-jukebox.pod │ │ │ ├── 14-starman-deployment.pod │ │ │ ├── 15-tutorial-shrinkr.pod │ │ │ ├── 16-Graph-rendering-with-dancer.pod │ │ │ ├── 17-authentication-with-twitter-oauth.pod │ │ │ ├── 18-Dancer-internals.pod │ │ │ ├── 19-writing-a-dancer-serializer.pod │ │ │ ├── 2-why-routes.pod │ │ │ ├── 20-writing-a-logger-backend.pod │ │ │ ├── 21-writing-a-dancer-session-backend.pod │ │ │ ├── 22-Dancer-with-Plack-middlewares.pod │ │ │ ├── 23-Dancer-ecosystem.pod │ │ │ ├── 24-How-to-contribute-to-Dancer.pod │ │ │ ├── 3-tutorial-dancr.pod │ │ │ ├── 4-database-connections-with-dancer-plugin-database.pod │ │ │ ├── 5-How-to-write-a-plugin.pod │ │ │ ├── 6-How-to-test-a-dancer-app.pod │ │ │ ├── 7-using-sessions-stored-in-cookies.pod │ │ │ ├── 8-rest-web-services.pod │ │ │ └── 9-ournowrimo-app.pod │ │ ├── 2011 │ │ │ ├── 1-what-a-difference-a-year-makes.pod │ │ │ ├── 10-portable-file-handling.pod │ │ │ ├── 11-dancer-2-roadmap.pod │ │ │ ├── 12-dancer-workshop-at-lpw2011.pod │ │ │ ├── 13-directory-view-and-htpasswd.pod │ │ │ ├── 14-dynamic-ajax-charts.pod │ │ │ ├── 15-sending-emails-with-dancer.pod │ │ │ ├── 16-Dancer-with-Plack-middlewares.pod │ │ │ ├── 17-plugins.pod │ │ │ ├── 18-alternative-templating-engines.pod │ │ │ ├── 19-using-d-p-redis.pod │ │ │ ├── 2-simple-crud.pod │ │ │ ├── 20-high-load-dancer-apps.pod │ │ │ ├── 21-making-rss-feeds-using-d-p-feed.pod │ │ │ ├── 22-adding-captcha-to-your-site.pod │ │ │ ├── 23-meet-the-core-team.pod │ │ │ ├── 24-thanking-contributors.pod │ │ │ ├── 3-self-contained-dancing.pod │ │ │ ├── 4-how-to-safely-store-a-password.pod │ │ │ ├── 5-easy-database-access-with-d-p-database.pod │ │ │ ├── 6-dancer-exceptions.pod │ │ │ ├── 7-building-a-blog-engine-using-dancer.pod │ │ │ ├── 8-dancer-2-or-why-I-rewrote-everything.pod │ │ │ └── 9-profiling-dancer-apps.pod │ │ ├── 2012 │ │ │ ├── 1-welcome.pod │ │ │ ├── 10-dancecard.pod │ │ │ ├── 11-simple-but-flexible-crud.pod │ │ │ ├── 12-route-handlers.pod │ │ │ ├── 13-ultisnips.pod │ │ │ ├── 14-dancer-sphinx-search-app.pod │ │ │ ├── 15-persistent-interpreter.pod │ │ │ ├── 16-dpae-redesign.pod │ │ │ ├── 17-your-own-DSL.pod │ │ │ ├── 18-crowdtilt.pod │ │ │ ├── 19-thegamecrafter.pod │ │ │ ├── 2-user-auth-with-dpae.pod │ │ │ ├── 20-Using-DBIC-with-Dancer.pod │ │ │ ├── 21-ournowrimo-app.pod │ │ │ ├── 22-tutorial-shrinkr.pod │ │ │ ├── 23-dancer-plugin-adaptor.pod │ │ │ ├── 24-night-before-christmas.pod │ │ │ ├── 3-dancer-apache-nginx-deployment.pod │ │ │ ├── 4-why-dancer2.pod │ │ │ ├── 5-dancer-session-Redis.pod │ │ │ ├── 6-dancer-core-error.pod │ │ │ ├── 7-dancer2-scoping.pod │ │ │ ├── 8-dancer2-templates.pod │ │ │ ├── 9-dancer-training-at-lpw2012.pod │ │ │ └── night-before-xmas │ │ ├── 2013 │ │ │ └── 2-hofmeir.pod │ │ ├── 2014 │ │ │ ├── 1-welcome.pod │ │ │ ├── 10-whats-in-an-appname.pod │ │ │ ├── 11-serial-serializer.pod │ │ │ ├── 12-a-test-to-remember.pod │ │ │ ├── 13-core-tests.pod │ │ │ ├── 14-plugins.pod │ │ │ ├── 15-outreach-program-for-women.pod │ │ │ ├── 16-command-line-dancing.pod │ │ │ ├── 17-auto-pages.pod │ │ │ ├── 18-method-of-setup-method.pod │ │ │ ├── 19-authentication.pod │ │ │ ├── 2-dancer2-anew.pod │ │ │ ├── 20-meddling-with-middlewares.pod │ │ │ ├── 21-middlewared-dancer.pod │ │ │ ├── 22-policy-document.pod │ │ │ ├── 23-dynamic-dispatching.pod │ │ │ ├── 24-past-and-future.pod │ │ │ ├── 3-migrating-to-dancer2.pod │ │ │ ├── 4-your-website-in-a-carton-box.pod │ │ │ ├── 5-packing-the-fat.pod │ │ │ ├── 6-static-noise-static-file-serving-in-dancer2.pod │ │ │ ├── 7-api-mashup.pod │ │ │ ├── 8-test-suite.pod │ │ │ └── 9-to_app-or-not-to_app.pod │ │ ├── 2016 │ │ │ ├── 1-another-year-of-dancing.pod │ │ │ ├── 10-easy-exceptions-and-logging.pod │ │ │ ├── 11-dancer-i18n-with-log-report.pod │ │ │ ├── 12-optimizing-dancer2-1.pod │ │ │ ├── 13-optimizing-dancer2-2.pod │ │ │ ├── 14-optimizing-dancer2-3.pod │ │ │ ├── 15-optimizing-dancer2-4.pod │ │ │ ├── 16-optimizing-dancer2-5.pod │ │ │ ├── 17-configuring-your-dancer-apps.pod │ │ │ ├── 18-asynchronous-dancer2.pod │ │ │ ├── 19-changing-your-session-id.pod │ │ │ ├── 2-state-of-the-ecosystem.pod │ │ │ ├── 20-send-as.pod │ │ │ ├── 21-exports-and-boilderplate.pod │ │ │ ├── 22-plugin-architecture.pod │ │ │ ├── 23-avoid-xss-issues-easily.pod │ │ │ ├── 24-contributing-to-dancer.pod │ │ │ ├── 3-medium-scale-dancer-part-1.pod │ │ │ ├── 4-medium-scale-dancer-part-2.pod │ │ │ ├── 5-medium-scale-dancer-part-3.pod │ │ │ ├── 6-medium-scale-dancer-part-4.pod │ │ │ ├── 7-medium-scale-dancer-part-5.pod │ │ │ ├── 8-automatic-restarts.pod │ │ │ └── 9-new-parameters.pod │ │ ├── 2018 │ │ │ ├── 13-State-of-the-Dancer.pod │ │ │ ├── 14-Dancer-Survey-Result-Recap.pod │ │ │ ├── 15-Dancer2-Plugin-Paginator.pod │ │ │ ├── 16-dancer-and-minion.pod │ │ │ ├── 17-dancer-and-email.pod │ │ │ ├── 18-customizing-dancer-cli.pod │ │ │ ├── 19-logging-with-dancer2-logger-log4perl.pod │ │ │ ├── 20-testing-dancer.pod │ │ │ ├── 21-spam-and-bot-prevention-no-CAPTCHAs.pod │ │ │ ├── 22-param-types.pod │ │ │ ├── 23-logger-console-colored.pod │ │ │ └── 24-whats-next-for-dancer.pod │ │ ├── 2020 │ │ │ ├── 13-the-twelve-days-of-dancer.pod │ │ │ ├── 14-collective-voice.pod │ │ │ ├── 15-whats-a-quarren.pod │ │ │ ├── 16-typed-route-parameters-in-dancer2.pod │ │ │ ├── 17-handlebars.pod │ │ │ ├── 18-post-processing-html-with-mojo-dom.pod │ │ │ ├── 19-one-page-apps.pod │ │ │ ├── 20-dancer2-plugin-minion.pod │ │ │ ├── 21-dancer-2fa-demo.pod │ │ │ ├── 22-asynchronous-intro.pod │ │ │ ├── 23-asynchronous-promises.pod │ │ │ └── 24-mutable-serializer.pod │ │ └── 2023 │ │ │ ├── 13-a-season-for-dancing.pod │ │ │ ├── 14-host-specific-route.pod │ │ │ ├── 15-mason-whats-old-is-new-again.pod │ │ │ ├── 16-dancer-liteblog.pod │ │ │ ├── 17-dancer-and-svelte.pod │ │ │ ├── 18-dbix-class.pod │ │ │ ├── 19-navigating-the-dancefloor.pod │ │ │ ├── 20-dancing-with-htmx.pod │ │ │ ├── 21-dancer-core-team-partnership-with-tprf.pod │ │ │ ├── 22-handlebars.pod │ │ │ ├── 23-json-api.pod │ │ │ └── 24-looking-ahead-to-2024.pod │ ├── css │ │ ├── error.css │ │ ├── mobile.css │ │ ├── prettify.css │ │ └── style.css │ ├── demo │ │ └── graph │ │ │ ├── github-graph.html │ │ │ ├── graph.js │ │ │ └── protovis-r3.2.js │ ├── dispatch.cgi │ ├── dispatch.fcgi │ ├── favicon.ico │ ├── images │ │ ├── 2011 │ │ │ ├── 13 │ │ │ │ ├── screenshot-1-590.png │ │ │ │ ├── screenshot-2-590.png │ │ │ │ └── screenshot-3-590.png │ │ │ ├── 14 │ │ │ │ └── chart-screenshot.png │ │ │ ├── 23 │ │ │ │ ├── ambs.jpg │ │ │ │ ├── bigpresh.jpg │ │ │ │ ├── dams.jpg │ │ │ │ ├── franck.jpg │ │ │ │ ├── sawyer.jpg │ │ │ │ └── sukria.jpg │ │ │ ├── simplecrud-people-edit.png │ │ │ └── simplecrud-people-list.png │ │ ├── 2012 │ │ │ ├── 14 │ │ │ │ └── search-results.png │ │ │ ├── 18 │ │ │ │ └── crowdtilt_RGB_gradient_medium.png │ │ │ └── 19 │ │ │ │ └── tgc-250x160.png │ │ ├── 2013 │ │ │ └── 2 │ │ │ │ └── HofmeirMedia.png │ │ ├── 2018 │ │ │ └── 23 │ │ │ │ ├── log-1.png │ │ │ │ ├── log-2.png │ │ │ │ └── log-3.png │ │ ├── 2020 │ │ │ ├── 20 │ │ │ │ ├── file-upload.png │ │ │ │ └── minion-dashboard.png │ │ │ └── 22 │ │ │ │ ├── 2FA_demo.png │ │ │ │ ├── 2FA_login.png │ │ │ │ └── 2FA_relogin.png │ │ ├── 2023 │ │ │ └── dancer-and-svelte │ │ │ │ └── svelte-dancer-1.png │ │ ├── calendar-bg.jpg │ │ ├── dancer_dbic.png │ │ ├── dancer_debug_01.png │ │ ├── dancer_debug_02.png │ │ ├── dancing-santa.png │ │ ├── perldancer-bg.jpg │ │ └── perldancer.jpg │ └── javascripts │ │ ├── jquery.js │ │ └── prettify.js ├── t │ ├── 001_base.t │ ├── 002_index_route.t │ └── 003_pod_pom.t └── views │ ├── archive.tt │ ├── article.tt │ ├── index-twelvedays.tt │ ├── index.tt │ ├── layouts │ ├── main.tt │ └── mobile.tt │ └── notyet.tt ├── pending ├── 23-multi-tenant-dancer.pod ├── Adding-authentication-to-your-application.pod ├── Graph-rendering-with-dancer.pod ├── dancer-for-static-web-sites.pod └── sitemap-to-be-merged-with-ecosystem.pod └── spelling.t /.gitignore: -------------------------------------------------------------------------------- 1 | build/html/*.html 2 | danceradvent/blib/ 3 | danceradvent/pm_to_blib 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "danceradvent/vendor/Dancer2"] 2 | path = danceradvent/vendor/Dancer2 3 | url = https://github.com/PerlDancer/Dancer2.git 4 | [submodule "danceradvent/vendor/Dancer-Plugin-Feed"] 5 | path = danceradvent/vendor/Dancer-Plugin-Feed 6 | url = https://github.com/PerlDancer/Dancer-Plugin-Feed.git 7 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Dancer advent calendar - The Twelve Days of Dancer! 2 | 3 | Article suggestions / volunteers to write them very much appreciated. 4 | 5 | If you have ideas or want to volunteer, find us in irc.perl.org/#dancer, or 6 | email the dancer-users mailing list, or contact cromedome@cpan.org - thanks! 7 | -------------------------------------------------------------------------------- /danceradvent/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | MYMETA.* 3 | Makefile 4 | -------------------------------------------------------------------------------- /danceradvent/Makefile.PL: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use ExtUtils::MakeMaker; 4 | 5 | WriteMakefile( 6 | NAME => 'danceradvent', 7 | AUTHOR => q{YOUR NAME }, 8 | VERSION_FROM => 'lib/danceradvent.pm', 9 | ABSTRACT => 'YOUR APPLICATION ABSTRACT', 10 | ($ExtUtils::MakeMaker::VERSION >= 6.3002 11 | ? ('LICENSE'=> 'perl') 12 | : ()), 13 | PL_FILES => {}, 14 | PREREQ_PM => { 15 | 'Dancer' => 1.2000, 16 | 'Dancer::Plugin::DebugDump' => 0, 17 | 'Dancer::Plugin::MobileDevice' => 0, 18 | 'Dancer::Plugin::Feed' => 0, 19 | 'DateTime' => 0, 20 | 'Pod::POM' => 0, 21 | 'POSIX' => 0, 22 | 'Template' => 0, 23 | 'Test::More' => 0, 24 | 'Text::Outdent' => 0, 25 | 'URI' => 0, 26 | 'YAML' => 0, 27 | 28 | # Dancer2 deps 29 | 'Capture::Tiny' => 0, 30 | 'Config::Any' => 0, 31 | 'File::Basename' => 0, 32 | 'File::Copy' => 0, 33 | 'File::Find' => 0, 34 | 'File::Spec' => 0, 35 | 'File::Temp' => 0, 36 | 'HTTP::Body' => 0, 37 | 'HTTP::Date' => 0, 38 | 'HTTP::Headers' => 0, 39 | 'HTTP::Request::Common' => 0, 40 | 'HTTP::Server::Simple::PSGI' => 0, 41 | 'JSON::XS' => 0, 42 | 'LWP::UserAgent' => 0, 43 | 'MIME::Types' => 0, 44 | 'MooX::Types::MooseLike' => 0, 45 | 'Plack' => 0, 46 | 'Scalar::Util' => 0, 47 | 'Template' => 0, 48 | 'URI' => 0, 49 | 'YAML::XS' => 0, 50 | 'YAML::Any' => 0, 51 | }, 52 | dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, 53 | clean => { FILES => 'danceradvent-*' }, 54 | ); 55 | -------------------------------------------------------------------------------- /danceradvent/bin/app.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | BEGIN { 4 | use FindBin; 5 | 6 | while ( my $libdir = glob("${FindBin::Bin}/../vendor/*/lib") ) { 7 | warn "$libdir"; 8 | unshift @INC, $libdir; 9 | } 10 | } 11 | 12 | use Dancer2; 13 | use lib 'lib'; 14 | use danceradvent; 15 | dance; 16 | -------------------------------------------------------------------------------- /danceradvent/config.yml: -------------------------------------------------------------------------------- 1 | # This is the main configuration file of your Dancer app 2 | # env-related settings should go to environments/$env.yml 3 | # all the settings in this file will be loaded at Dancer's startup. 4 | 5 | # Your application's name 6 | appname: "danceradvent" 7 | 8 | # The default layout to use for your application (located in 9 | # views/layouts/main.tt) 10 | layout: "main" 11 | 12 | # when the charset is set to UTF-8 Dancer will handle for you 13 | # all the magic of encoding and decoding. You should not care 14 | # about unicode within your app when this setting is set (recommended). 15 | charset: "UTF-8" 16 | 17 | # template engine 18 | # simple: default and very basic template engine 19 | # template_toolkit: TT 20 | 21 | template: "template_toolkit" 22 | start_year: 2010 23 | 24 | render_future: 0 25 | 26 | engines: 27 | template: 28 | template_toolkit: 29 | ENCODING: 'utf8' 30 | start_tag: '[%' 31 | end_tag: '%]' 32 | -------------------------------------------------------------------------------- /danceradvent/cpanfile: -------------------------------------------------------------------------------- 1 | requires "Dancer2"; 2 | requires "Pod::POM"; 3 | requires "Text::Outdent"; 4 | requires "Dancer::Plugin::Feed"; 5 | requires "Dancer::Plugin::MobileDevice"; 6 | -------------------------------------------------------------------------------- /danceradvent/environments/development.yml: -------------------------------------------------------------------------------- 1 | # configuration file for development environment 2 | 3 | # the logger engine to use 4 | # console: log messages to STDOUT (your console where you started the 5 | # application server) 6 | # file: log message to a file in log/ 7 | logger: "console" 8 | 9 | # the log level for this environement 10 | # core is the lowest, it shows Dancer's core log messages as well as yours 11 | # (debug, warning and error) 12 | log: "core" 13 | 14 | # shoud Dancer consider warnings as critical errors? 15 | warnings: 1 16 | 17 | # should Dancer show a stacktrace when an error is caught? 18 | show_errors: 1 19 | 20 | # auto_reload is a development and experimental feature 21 | # you should enable it by yourself if you want it 22 | # Module::Refresh is needed 23 | # 24 | # Be aware it's unstable and may cause a memory leak. 25 | # DO NOT EVER USE THAT FEATURE IN PRODUCTION 26 | # OR TINY KITTENS SHALL DIE WITH LOTS OF SUFFERING 27 | auto_reload: 0 28 | 29 | render_future: 1 30 | -------------------------------------------------------------------------------- /danceradvent/environments/production.yml: -------------------------------------------------------------------------------- 1 | # configuration file for production environment 2 | 3 | # only log warning and error messsages 4 | log: "warning" 5 | 6 | # log message to a file in logs/ 7 | logger: "file" 8 | 9 | # don't consider warnings critical 10 | warnings: 0 11 | 12 | # hide errors 13 | show_errors: 0 14 | 15 | # cache route resolution for maximum performance 16 | route_cache: 1 17 | 18 | render_future: 0 19 | -------------------------------------------------------------------------------- /danceradvent/lib/Pod/POM/View/InlineHTML.pm: -------------------------------------------------------------------------------- 1 | use strict; 2 | package Pod::POM::View::InlineHTML; 3 | use base qw(Pod::POM::View::HTML); 4 | use Text::Outdent qw/outdent expand_leading_tabs/; 5 | 6 | sub view_pod { 7 | my ($self, $pod) = @_; 8 | return q{
} 9 | . $pod->content->present($self) 10 | . q{
} 11 | } 12 | 13 | # remove space indentation, 14 | # indent with css if needed 15 | sub view_verbatim { 16 | my ($self, $text) = @_; 17 | $text = expand_leading_tabs(8, $text); 18 | $text = outdent($text); 19 | for ($text) { 20 | s/&/&/g; 21 | s//>/g; 23 | } 24 | return qq{
$text
\n\n}; 25 | } 26 | 27 | # currently this should cover all that we need 28 | # L 29 | # L 30 | # L 31 | # L 32 | # TODO: What doesn't work? Linking to sections: L or L 33 | # or L 34 | sub view_seq_link { 35 | no warnings; 36 | my ($self, $link) = @_; 37 | 38 | # L 39 | if ( $link =~ m{\A \w+ :// [^|]+ \Z}x ) { 40 | return make_href($link, $link); 41 | } 42 | 43 | if ( $link =~ m{\A mailto: [^|]+ \Z}x ) { 44 | return make_href($link, $link); 45 | } 46 | 47 | my $external = "https://metacpan.org/module/"; 48 | 49 | my ($title, $target) = split /\|/, $link, 2; 50 | $target = $title unless ($target); 51 | 52 | if ( $target =~ m{\A \w+ :// [^|]+ \Z}x ) { 53 | return make_href($target, $title); 54 | } 55 | 56 | if ( $target =~ m{\A mailto: [^|]+ \Z}x ) { 57 | return make_href($target, $title); 58 | } 59 | 60 | return make_href($external . $target, $title); 61 | } 62 | 63 | # Let's do all link handling in view_seq_link 64 | sub view_seq_text { 65 | my ($self, $text) = @_; 66 | for($text) { 67 | s/&/&/g; 68 | s//>/g; 70 | } 71 | return $text; 72 | } 73 | 74 | sub view_head_generic { 75 | my ($self, $head, $level) = @_; 76 | my $title = $head->title->present($self); 77 | my $anchor = anchorify($title); 78 | return qq(${title}\n\n) 79 | . $head->content->present($self); 80 | } 81 | 82 | sub view_head1 { 83 | my ($self, $head1) = @_; 84 | $self->view_head_generic($head1, 1); 85 | } 86 | 87 | sub view_head2 { 88 | my ($self, $head2) = @_; 89 | $self->view_head_generic($head2, 2); 90 | } 91 | 92 | sub view_head3 { 93 | my ($self, $head3) = @_; 94 | $self->view_head_generic($head3, 3); 95 | } 96 | 97 | sub view_head4 { 98 | my ($self, $head4) = @_; 99 | $self->view_head_generic($head4, 4); 100 | } 101 | 102 | sub make_href { 103 | goto &Pod::POM::View::HTML::make_href 104 | } 105 | 106 | sub htmlify { 107 | my( $heading) = @_; 108 | $heading =~ s/(\s+)/ /g; 109 | $heading =~ s/\s+\Z//; 110 | $heading =~ s/\A\s+//; 111 | # The hyphen is a disgrace to the English language. 112 | # $heading =~ s/[-"?]//g; 113 | $heading =~ s/["?]//g; 114 | $heading = lc( $heading ); 115 | return $heading; 116 | } 117 | 118 | sub anchorify { 119 | my $title = shift; 120 | $title = htmlify($title); 121 | $title =~ s/\W/_/g; 122 | return $title; 123 | } 124 | 125 | 1 126 | -------------------------------------------------------------------------------- /danceradvent/public/404.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Error 404 6 | 7 | 8 | 9 | 10 |

Error 404

11 |
12 |

Page Not Found

Sorry, this is the void.

13 |
14 | 17 | 18 | -------------------------------------------------------------------------------- /danceradvent/public/500.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Error 500 6 | 7 | 8 | 9 | 10 |

Error 500

11 |
12 |

Internal Server Error

Wooops, something went wrong

13 |
14 | 17 | 18 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2010/12-adding-a-feed-to-your-application.pod: -------------------------------------------------------------------------------- 1 | =head1 Adding a feed to your application 2 | 3 | Now that you have written a nice application with Dancer, you want to add a RSS 4 | feed to publish your articles. For this task, you can use the plugin 5 | L. 6 | 7 | The goal of this module is to provide an easy way to generate a feed in B 8 | or B. For this tutorial, we will see how we use it in the Advent Calendar 9 | application. 10 | 11 | =head2 Adding a simple feed 12 | 13 | In the advent calendar application, each article is stored in a POD file stored 14 | on disk. Each day, a new article will be visible, and should be added to the 15 | feed. We will see how to do that. 16 | 17 | First, we load the plugin in the application. 18 | 19 | use Dancer; 20 | use Dancer::Plugin::Feed; 21 | 22 | Now we need a function that will, for each day, return the list of articles, 23 | with, for each articles, if it should be visible: 24 | 25 | sub _articles { 26 | my $year = shift; 27 | my @days = qw/1..24/; 28 | my @articles; 29 | 30 | foreach my $day (@days) { 31 | if (_article_is_visible($year, $day) { 32 | push @articles, _article( $year, $day ); 33 | } 34 | } 35 | @articles = sort { $a->{day} <=> $b->{day} } @articles; 36 | return \@articles; 37 | } 38 | 39 | sub _article_is_visible { 40 | # decide if this article should be visible at this date 41 | } 42 | 43 | And of course, we need to build each article from POD, set the date of the 44 | publication, the title, the permalink, etc. 45 | 46 | sub _article { 47 | my ( $year, $day ) = @_; 48 | ... 49 | # skip if not visible 50 | return { 51 | title => $title, 52 | content => $content, 53 | link => $permalink, 54 | issued => DateTime->new( year => $year, month => 12, day => $day ), 55 | }; 56 | } 57 | 58 | And to finish, our route to return the content of the RSS feed. This route gets 59 | the B as an argument, and returns the list of articles for this year: 60 | 61 | get '/feed/:year/rss' => sub { 62 | my $articles = _articles( params->{year} ); 63 | create_feed( 64 | format => 'RSS', 65 | entries => $articles, 66 | title => 'my awesome RSS feed', 67 | ); 68 | }; 69 | 70 | That's it. The important part here is the B method. This method 71 | take a list of parameters that will be used to build the feed. 72 | 73 | =head3 How to let the user choose the format 74 | 75 | In the previous example, we've seen how to create a RSS feed. But maybe some of 76 | your users would have preferred an Atom feed, so we will let the decide what 77 | format they prefer. We only need to update our previous route for this: 78 | 79 | get '/feed/:year/:format' => sub { 80 | my $format = params->{format}; 81 | my $articles = _articles(params->{year}); 82 | create_feed( 83 | format => $format, 84 | ... 85 | ); 86 | }; 87 | 88 | Now, they can use e.g. C. 89 | 90 | 91 | =head3 Setting default values in your configuration 92 | 93 | You can of course configure the default values for your feed in the 94 | configuration. We can create this configuration: 95 | 96 | plugins: 97 | Feed: 98 | title: PerlDancer Advent Calendar 99 | copyright: PerlDancer 100 | tagline: PerlDancer advent calendar, a community effort 101 | description: this is a description for our calendar 102 | 103 | =head3 Aliases methods 104 | 105 | L export another two methods: B and 106 | B. Those method set the B argument for you. 107 | 108 | get '/feed/atom' => sub { 109 | create_atom_feed(...); 110 | }; 111 | 112 | get '/feed/rss' => sub { 113 | create_rss_feed(...); 114 | }; 115 | 116 | =head2 See also 117 | 118 | Under the hood, L uses L. 119 | 120 | =head2 Author 121 | 122 | This article has been written by Franck Cuny for the Perl Dancer Advent Calendar 123 | 2010. 124 | 125 | =head2 Copyright 126 | 127 | Copyright (C) 2010 by Franck Cuny C<< >> 128 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2010/13-dancer-jukebox.pod: -------------------------------------------------------------------------------- 1 | =head1 DancerJukebox - controlling MPD from Dancer 2 | 3 | I've been using L to play my music 4 | for a long time, it really suits my needs. It runs on a machine under my stairs 5 | and outputs music through various outputs (two soundcards, one hooked up to the 6 | amp and one to my headphones transmitter, and also Shoutcast streaming so I can 7 | listen from work), and can be controlled by a variety of clients, which need 8 | only speak a simple text-based protocol. 9 | 10 | Some time ago I wrote a jukebox-style music queueing webapp using 11 | L, which worked pretty well, but I 12 | did not enjoy coding or maintaining it. 13 | 14 | After having become familiar with Dancer, I decided it was time to re-write it 15 | using Dancer, so I sat down one evening and began. To speak to MPD, I'm using 16 | L, and I wanted to make it easy to get a connection, so I wrote up 17 | the code as a simple plugin, L, which I released to CPAN. 18 | 19 | This lead to code as simple as, for example: 20 | 21 | get '/skipnext' => sub { mpd->next; redirect '/'; }; 22 | get '/control/play/:id' => sub { mpd->play(params->{id}); redirect '/'; }; 23 | 24 | Simple and easy. 25 | 26 | This time round, I was happy enough with the code to release it, so you'll find 27 | L. The 28 | previous version was developed in a private Subversion repository, and I was 29 | never happy enough with it to release it. Whilst I found Catalyst powerful, I 30 | found myself having to work "the Catalyst way", and didn't really enjoy it; 31 | re-writing it using Dancer was actually fun. 32 | 33 | I also added a very basic "admin" section to be used from my Android phone, 34 | allowing me to skip the playing song and remove requests from the queue in case 35 | anyone requests anything silly that I don’t want played, but didn't actually use 36 | it. It only took a couple of minutes to add that feature, though! At some 37 | point, I'll tweak that to make use of L (see 38 | previous advent calendar post on this) to automatically go to a basic layout, 39 | rather than implementing that myself. 40 | 41 | =head2 Author 42 | 43 | This article has been written by David Precious for the Perl Dancer Advent 44 | Calendar 2010. 45 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2010/14-starman-deployment.pod: -------------------------------------------------------------------------------- 1 | =head1 Deployment of Dancer applications with Starman, Apache and mod_proxy 2 | 3 | =head2 Introduction 4 | 5 | I would like to show a clean and elegant deployment solution for deploying 6 | B using L. 7 | 8 | Starman is a high-performance L/L web server, written by 9 | B. PSGI is an interface between a web server and a web 10 | application, written in Perl. PSGI supports B, B and B. 11 | 12 | While PSGI is the equivalent to B in Python or B for Ruby, Starman 13 | is the equivalent to B in Python or B in Ruby, even if not 14 | 100% compatible. 15 | 16 | The B web server is a great web server, but has changed quite a bit with 17 | time. Its features and functionality is increasing, but so is its requirements. 18 | It is used in production environments to manage applications, virtual hosts and 19 | more. However, when Apache was started, it was not meant to serve Perl 20 | applications, which is why mod_perl was created. 21 | 22 | I appreciate simple applications that do one thing and do it well. This is why I 23 | prefer to use Starman which is very good at what it does. 24 | 25 | =head2 Configure 26 | 27 | First, you'll need to install Starman. I use L here. 28 | 29 | # if you use a sudo policy: 30 | sudo cpanm Starman 31 | 32 | # if you're root: 33 | cpanm Starman 34 | 35 | Once installed, you go to the application directory and start the server. 36 | 37 | cd app_dir 38 | plackup -s Starman app.pl 39 | 40 | Notice this will start the Starman web server for this specific application. 41 | That means that if you're already running a web server (either Apache or just 42 | another Starman for a different application), it will not be able to start since 43 | the port will be taken. You can specify the port number using C<-port>. 44 | 45 | Using Apache, you can run Starman as a proxy using B and add the port 46 | number to direct the requests to. 47 | 48 | To be able to use Apache with mod_proxy, you'll need to make sure the required 49 | modules are available. 50 | 51 | If you're on B or B, you can activate the Apache modules using 52 | the following command: 53 | 54 | # using sudo: 55 | sudo a2enmod proxy proxy_http cache 56 | 57 | # as root: 58 | a2enmod proxy proxy_http cache 59 | 60 | If you're using a different distribution, please consult their documentation on 61 | how to enable Apache modules. It can be as simple as installing them or 62 | including a configuration file. 63 | 64 | Now to configure Apache: 65 | 66 | 67 | ServerName example.com 68 | ServerAlias www.example.com 69 | 70 | DocumentRoot /path/to/dancer/app 71 | 72 | 73 | Order deny,allow 74 | Allow from allow 75 | 76 | 77 | ProxyPass / http://localhost:5000/ 78 | ProxyPassReverse / http://localhost:5000/ 79 | 80 | 81 | You can configuration Apache to serve static files, to ease on Starman and your 82 | application and provide faster response for these files. 83 | 84 | ProxyPass /public/favicon.ico ! 85 | 86 | Using B or B, we enable the configuration so Apache will use it: 87 | 88 | cd /etc/apache2/sites-available/ 89 | 90 | # sudo: 91 | sudo a2ensite app_dancer 92 | 93 | # root: 94 | a2ensite app_dancer 95 | 96 | And restart Apache. 97 | 98 | That's it! 99 | 100 | =head2 Conclusion 101 | 102 | This deployment method is certainly a simple solution that is scalable and can 103 | even be improved later on by replacing Apache with a lighter server like 104 | B. 105 | 106 | =head2 Author 107 | 108 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2010/24-How-to-contribute-to-Dancer.pod: -------------------------------------------------------------------------------- 1 | =head1 How to contribute to Dancer 2 | 3 | For our development projects, we rely a lot on 4 | L. Lately, more and more people started 5 | contributing to Dancer (we've got nearly 40 contributors), but not all of them 6 | are familiar with Github or git. Here is a little step-by-step guide on how to 7 | contribute. You don't need to be a Perl expert to contribute, you can provide 8 | help by correcting documentation error, adding a new recipe to our cookbook, 9 | reporting bugs, adding tests, ... 10 | 11 | =head2 The code 12 | 13 | The main repository is hosted L. Lately, 14 | we've decided to switch to a new workflow in our process. Our two main branches 15 | are: 16 | 17 | =over 4 18 | 19 | =item master 20 | 21 | =item devel 22 | 23 | =back 24 | 25 | The B branch is stable and should be in a state that allow a release 26 | at any time. The B branch is used to our development. 27 | 28 | Each new feature new to be created in a B branch 29 | that will be merged into devel when it's done. 30 | 31 | For this, we're using L, waiting for 32 | our very own L to be ready. 33 | 34 | =head2 Contributing 35 | 36 | First, go to L and 37 | click on the B button. Now, here is a little tutorial on how to fetch the 38 | repository, list the local and remote branches, and track the remote devel 39 | branch. 40 | 41 | $ git clone git@github.com:your_user/Dancer.git 42 | Initialized empty Git repository in /tmp/Dancer/.git/ 43 | remote: Counting objects: 9299, done. 44 | remote: Compressing objects: 100% (4236/4236), done. 45 | remote: Total 9299 (delta 5740), reused 8015 (delta 4862) 46 | Receiving objects: 100% (9299/9299), 1.20 MiB | 111 KiB/s, done. 47 | Resolving deltas: 100% (5740/5740), done. 48 | 49 | $ cd Dancer 50 | 51 | $ git branch -l 52 | * master 53 | 54 | $ git branch -a 55 | * master 56 | remotes/origin/HEAD -> origin/master 57 | remotes/origin/after_filter 58 | remotes/origin/devel 59 | remotes/origin/hooks 60 | remotes/origin/master 61 | remotes/origin/plack-middlewares 62 | remotes/origin/psgi-refactor 63 | remotes/origin/refactor/dtsimple-removal 64 | remotes/origin/refactoring/app 65 | 66 | $ git branch --track devel origin/devel 67 | 68 | $ git branch -l 69 | devel 70 | * master 71 | 72 | Now that you know what the purpose of each branch is, you can decide to work on 73 | master or devel (B to switch branch). 74 | 75 | =head3 Sending your patch 76 | 77 | As I've previously stated, we rely a lot on the github features and interface. 78 | So now you've written your patch. First, be sure to provide one or more tests, 79 | and to run the test suite (with B or B). If all the 80 | tests pass, you can send a pull request. For this, you go on your own fork on 81 | github (http://github.com/$user/dancer), and you click on the B 82 | button. 83 | 84 | You can at any time see all the commits done by others that have not yet been 85 | merged into one of our branches at 86 | L. 87 | 88 | =head3 Reporting and/or fixing bugs 89 | 90 | We prefer to use the github issue tracker instead of RT. So if you want to 91 | report a bug, go B. 92 | 93 | If your commit fixes a bug reported there, please add in your commit message 94 | something like 'closes gh-xxx" where xxx is the bug id. 95 | 96 | =head2 Community work 97 | 98 | There is nearly 40 different contributors to Dancer. There is a lot of plugins 99 | and engines available on CPAN and github. This is a real community effort. 100 | Thank you to everyone who have contributed so far! 101 | 102 | =head2 Author 103 | 104 | This article has been written by Franck Cuny for the Perl Dancer Advent 105 | Calendar 2010. 106 | 107 | =head2 Copyright 108 | 109 | Copyright (C) 2010 by franck cuny C<< >> 110 | 111 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2010/4-database-connections-with-dancer-plugin-database.pod: -------------------------------------------------------------------------------- 1 | =head1 Database connections with Dancer::Plugin::Database 2 | 3 | The 4 | L 5 | plugin is designed to make connecting to databases from your Dancer applications 6 | simple and painless. Connection details are stored in your application's 7 | configuration, and the connection is automatically established for you (and 8 | re-established if the connection went away). 9 | 10 | =head2 How do I use it? 11 | 12 | Using it can be as simple as: 13 | 14 | my $user = database->selectrow_hashref( 15 | 'select * from users where username = ?', 16 | undef, params->{username} 17 | ); 18 | 19 | Calling the C keyword simply returns you a connected DBI object, so 20 | you can do whatever you'd usually do with a DBI object. 21 | 22 | get '/widget/view/:id' => sub { 23 | my $sth = database->prepare( 24 | 'select * from widgets where id = ?', 25 | ); 26 | $sth->execute(params->{id}); 27 | 28 | template 'display_widget', { 29 | widget => $sth->fetchrow_hashref }; 30 | }; 31 | 32 | =head2 How do I tell it my database connection details? 33 | 34 | Configuration is simple - for instance, in C you could simply add: 35 | 36 | plugins: 37 | Database: 38 | driver: 'SQLite' 39 | database: 'foo.sqlite' 40 | 41 | Or, let's say you're using MySQL, and would like to run queries automatically 42 | when you first get a connection to the database: 43 | 44 | plugins: 45 | Database: 46 | driver: 'mysql' 47 | database: 'databasename' 48 | username: 'fred' 49 | password: 'verysecretindeed' 50 | on_connect_do: 51 | - "SET NAMES 'utf8'" 52 | - "SET CHARACTER SET 'utf8'" 53 | 54 | =head2 But what if I need to talk to multiple databases? 55 | 56 | All of the above examples assume that you only want to connect to one database. 57 | For the majority of web applications, that's often true, but there are of course 58 | plenty of people who'll want to talk to multiple databases. 59 | 60 | That's easy enough - you can define multiple named connections in your app 61 | config, and pass a name to the C keyword - for example: 62 | 63 | plugins: 64 | Database: 65 | connections: 66 | foo: 67 | driver: "SQLite" 68 | database: "foo.sqlite" 69 | bar: 70 | driver: "mysql" 71 | host: "localhost" 72 | .... 73 | 74 | The above defines two connections named C and C. You can use them 75 | simply: 76 | 77 | my $foo_dbh = database('foo'); 78 | 79 | =head2 Author 80 | 81 | This article has been written by David Precious C<< >> 82 | for the Perl Dancer Advent Calendar 2010. 83 | 84 | =head2 Copyright 85 | 86 | Copyright (C) 2010 by David Precious 87 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/1-what-a-difference-a-year-makes.pod: -------------------------------------------------------------------------------- 1 | =head1 Looking back on 2011 2 | 3 | Firstly, a big welcome to the 2011 Dancer advent calendar - last year's advent 4 | calendar was successful and well-received, hopefully this year's will be even 5 | better. You can look forward to a series of posts on getting the most from 6 | Dancer and what's happened in 2011. 7 | 8 | So, let's start by taking a look back on 2011 and see what's new in the Dancer 9 | world. 10 | 11 | =head2 Dancer workshop at LPW2011 12 | 13 | Andrew Solomon presented a hands-on training session at this year's London Perl 14 | Workshop entitled "Web development for beginners with Dancer". 15 | 16 | The workshop was popular and well received, with 20 registered attendees. 17 | 18 | A full writeup will follow later in the calendar :) 19 | 20 | =head2 Dancer ecosystem better than ever 21 | 22 | Currently, the Dancer repository on GitHub has 378 watchers and has been forked 23 | 116 times! 24 | 25 | The number of individual contributors according to GitHub is 70 - great to see 26 | such a vibrant and helpful community springing up around Dancer. Speaking of 27 | the community, our L has 87 people 28 | present at the time of writing, with many helpful volunteers providing valuable 29 | assistance. 30 | 31 | More and more Dancer-powered sites are being added to the 32 | L page. 33 | 34 | 35 | =head2 Many more plugins to make life easy 36 | 37 | Many more valuable and useful plugins have been released - a search on MetaCPAN 38 | for L gives 39 | 73 results. 40 | 41 | Later advent calendar posts will showcase some of the useful plugins which can 42 | make your life easier :) 43 | 44 | 45 | =head2 Better Unicode support - it should Just Work 46 | 47 | Unicode support has been extended and corrected, with some helpful guidance from 48 | Mike Whitaker (Penfold). With C in your application's config, 49 | using Unicode should Just Work. If you're using L, 50 | that setting will also cause UTF-8 support in your database engine to be 51 | automatically enabled (for MySQL, Postgres or SQLite), so unicode strings 52 | fetched from the database should need no special handling. 53 | 54 | =head2 Support for HTTP PATCH 55 | 56 | The HTTP 'PATCH' verb is defined in 57 | L, allowing clients to perform a 58 | partial resource modification, and is slowly being adopted by RESTful APIs etc - 59 | GitHub's V3 API being one such example. 60 | 61 | Dancer now supports PATCH requests. 62 | 63 | One caveat is that it won't work in standalone mode until 64 | L 65 | has been updated to recognise PATCH requests. A pull request addressing this 66 | has been submitted, but, at the time of writing, has not been merged: 67 | 68 | L 69 | 70 | =head2 Get hooked! 71 | 72 | Support for hooks, both defined by Dancer itself and additional hooks registered 73 | by your app or plugins, to make it possible to customise how Dancer behaves 74 | easily. 75 | 76 | =head2 Configurable XML serialising 77 | 78 | Options can now be passed to control serialisation to XML via L. 79 | 80 | =head2 send_file can send data from a scalar and set filename 81 | 82 | The L keyword can now be used to 83 | send data from a scalar and set the Content-Disposition header to suggest a 84 | filename, for example: 85 | 86 | send_file \$img_data, { filename => 'myimage.png' }; 87 | 88 | =head2 send_file supports streaming 89 | 90 | The L keyword now supports PSGI 91 | streaming, with callbacks to control what happens. 92 | 93 | =head2 Exceptions 94 | 95 | The exceptional Damien Krotkine has added a continuations-style exceptions system 96 | to provide much more powerful error handling. 97 | 98 | =head2 behind_proxy setting 99 | 100 | The behind_proxy setting tells Dancer that it is deployed behind a proxy, 101 | causing it to honour the C, C etc 102 | env vars. 103 | 104 | 105 | And, of course, many more improvements and bug fixes - thanks to the awesome 106 | community. 107 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/10-portable-file-handling.pod: -------------------------------------------------------------------------------- 1 | =head1 Portable file handling in Dancer apps 2 | 3 | If you want your app to be easily portable between different systems and 4 | different platforms, it's important not to write file-handling code in a 5 | platform-specific way. 6 | 7 | Dancer provides some utility functions in L which help you to 8 | deal with files in a portable manner. 9 | 10 | =head2 Portable file paths 11 | 12 | Depending on the platform, directory separators may vary. 13 | 14 | To get round this, L allows you to assemble paths easily: 15 | 16 | my $views_dir = Dancer::FileUtils::path(setting('appdir'), 'views'); 17 | 18 | C uses L internally. 19 | 20 | 21 | =head2 Reading file contents 22 | 23 | L provides a way to quickly retrieve the 24 | content of a file, and behaves sensibly depending on the context it was called 25 | in: 26 | 27 | # Read entire file contents into $content: 28 | my $content = Dancer::FileUtils::read_file_content($filename); 29 | 30 | # Read each line of file into @lines: 31 | my @lines = Dancer::FileUtils::read_file_content($filename); 32 | 33 | The application's C setting will be taken into account when opening the 34 | file, and will default to UTF-8 if no charset setting is present, so UTF-8 data 35 | should Just Work. 36 | 37 | L works in the same manner, but takes an 38 | open filehandle, reads the content from it, and I. 39 | 40 | 41 | =head2 Opening a file 42 | 43 | L provides a way to open a file, taking the app's 44 | C setting into account, and returns a filehandle: 45 | 46 | my $fh = open_file('<', $file) or die ...; 47 | 48 | =head2 Setting filehandle mode 49 | 50 | If you have a filehandle you've opened yourself, you can use 51 | L to apply the app's C setting (or 52 | default to UTF-8) encoding. 53 | 54 | 55 | =head1 AUTHOR 56 | 57 | David Precious 58 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/11-dancer-2-roadmap.pod: -------------------------------------------------------------------------------- 1 | =head1 Dancer 2, a Roadmap 2 | 3 | OK, before we start, let's make it clear: this article won't answer the 4 | question everyone has in mind: I. 5 | 6 | But it will give a complete overview of what has been done and what remains to 7 | be done. This should give you a good overview of where we are and will also 8 | enlight how you can help. 9 | 10 | =head2 DSL 11 | 12 | Of course the very first milestone is to have a working DSL over the new core. 13 | It has been my main target and it's almost finished. 14 | 15 | All of the keywords we have in Dancer 1 are supported at the time of this 16 | writing except C and C. All the other keywords you won't 17 | find in Dancer 2 that exist in Dancer 1 will be deprecated (like 18 | C or C for instance). 19 | 20 | I plan to finish the DSL very soon (it's my top-priority for now) so I suppose 21 | it's a matter of weekends as that's when I'm able to work on Dancer 2. 22 | 23 | =head2 Core Engines 24 | 25 | We can find several I in the Dancer distribution. Engines are specific 26 | components dedicated to handle sessions, templates, loggers and serializers. 27 | 28 | The lists below show engines we found in Dancer 1, those in bold are the one 29 | ported in Dancer 2. 30 | 31 | =over 32 | 33 | =item Logger engines 34 | 35 | Capture, B, Diag, B, Note, Null. 36 | 37 | =item Session engines 38 | 39 | Simple, B. 40 | 41 | =item Template engines 42 | 43 | Simple, B. 44 | 45 | =item Serializer engines 46 | 47 | B, B, Mutable, B, B. 48 | 49 | =back 50 | 51 | As you can see, there is still some work to do there, but it should not take 52 | too long as all the C roles used to build engines are ready. Also some of these 53 | engines are really... dumb to implement! 54 | 55 | =head2 Plugins 56 | 57 | The support for plugins is namely the C module. In Dancer 2, 58 | lots of magic happens there because a plugin in Dancer 2 is actually a DSL role 59 | that is consumed into the core DSL (which itself is built via a default DSL 60 | role). 61 | 62 | The interesting part is that all the role composition is hidden by the 63 | syntactic sugar of C, the very same syntax of Dancer 1 is 64 | possible, but behind the scene, there are C roles consumed, it's insanely 65 | powerful and elegant! (I can say that without sounding pretentious, because 66 | it's not my idea, but one of mst's ;). 67 | 68 | So, in Dancer 1, we have one plugin shipped with the core, it's 69 | C which provides a handy keyword C to define ... Ajax 70 | routes. 71 | 72 | That plugin has been ported to Dancer 2 and works as expected. 73 | 74 | =head2 Dancer::Test 75 | 76 | The C module is a helper to get Dancer tests, it provides lots of 77 | handy function to test a Dancer application. The most used one, 78 | C is implemented, but all the other helpers remain to be done. 79 | 80 | Also, due to design changes, Dancer::Test might not work exactly in the same 81 | conditions as with Dancer 1. So maybe some work here to polish it a bit more. 82 | 83 | =head2 Ecosystem 84 | 85 | The last part of the job will be quite a big challenge: we'll need to test 86 | every module on CPAN in the Dancer ecosytem with Dancer 2, see if it works and 87 | if not, patch it and send the patch to the author. 88 | 89 | This is going to be - I think - a very interesting marathon, and I think the 90 | best way to do it will be in a hackaton dedicated to the task. 91 | 92 | =head2 OK, so ... When ? 93 | 94 | Well, not for Christmas! But we can say Dancer 2 will be out in 2012, is that 95 | precise enough? No? Well, didn't I say at the beginning that I won't answer 96 | that I question? 97 | 98 | =head2 Author 99 | 100 | This article was written by Alexis Sukrieh and reviewed by David Precious, for 101 | the Perl Dancer Advent Calendar 2011. 102 | 103 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/12-dancer-workshop-at-lpw2011.pod: -------------------------------------------------------------------------------- 1 | =head1 Dancer workshop at London Perl Workshop 2011 2 | 3 | Saturday, 12 November 2011 was the 2011 London Perl Workshop, held in the 4 | Cavendish Street Campus of Westminster University. 5 | 6 | Andrew Solomon ran L 7 | entitled "Web development for beginners using Dancer", to help beginners to 8 | learn to use Dancer, Template Toolkit, learn the MVC concept, and understand 9 | structuring code for maintainability and using object orientation. 10 | 11 | In all, there were 19 attendees (in a room with only 18 terminals). 12 | 13 | Andew said that the students were very engaged, asking (and answering) valid 14 | questions until the end of the 2.5 hour class, and that almost all of them had a 15 | working web application up and running by the end of the session. 16 | 17 | Many of the students reported that they found it much simpler to get started 18 | than they'd expected. 19 | 20 | A quote from an attendee: 21 | 22 | "... if I do ever try to get something up and running in Perl, I feel like I 23 | could probably do it with Dancer" 24 | 25 | I also asked Andrew himself for his opinion, and he said: 26 | 27 | "make sure you say how much I enjoyed presenting Dancer! The way high level 28 | concepts and the actual code are aligned, I think that Dancer will 29 | give Perl a place in the 'standard' university IT curriculum before 30 | too long." 31 | 32 | All in all, it seems the workshop was well-received, helping raise the profile 33 | of Dancer and feedback from attendees confirms that Dancer is doing well at 34 | being a user-friendly framework which is easy for beginners to start working 35 | with, with an easy learning curve. 36 | 37 | 38 | =head1 AUTHOR 39 | 40 | David Precious 41 | 42 | 43 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/13-directory-view-and-htpasswd.pod: -------------------------------------------------------------------------------- 1 | =head1 Serving Files with Dancer::Plugin::DirectoryView and Dancer::Plugin::Auth::Htpasswd 2 | 3 | A while ago I was converting a simple PHP website to Dancer, and moving 4 | it from being deployed on Apache to Starman. There wasn't a lot of code, 5 | so rewriting went quickly -- but, the site used a few specific features 6 | of Apache, namely directory indexes (courtesy of 7 | L) 8 | to allow user access to directories/files on the server, and C files 9 | to password-protect some of those directories. 10 | 11 | I could just deploy the new Dancer website on Apache and keep using 12 | those goodies, but I thought that it would be nice if Dancer itself 13 | provided similar features. So, I created two plugins that do just that: 14 | L and L. 15 | Let me now show you how to use them. 16 | 17 | =head2 Directory Indexes 18 | 19 | Let's say we have a C directory under C, and we'd like to 20 | allow users to browse it and download files. Enabling directory access 21 | is as simple as including the plugin in our Dancer application: 22 | 23 | package MyWebApp; 24 | 25 | ... 26 | 27 | use Dancer::Plugin::DirectoryView; 28 | 29 | And updating the configuration file (C) to tell the plugin which 30 | directory should be made available, and at which URL: 31 | 32 | plugins: 33 | DirectoryView: 34 | url: /pub 35 | root_dir: files 36 | 37 | That's it -- now, if we launch our app and point the browser at the 38 | C URL, we'll see the contents of the directory: 39 | 40 | =for html 41 | 42 | =head2 Protecting Directories with Htpasswd Files 43 | 44 | As you might have noticed on the screenshot, there's a C 45 | directory under C. It contains some super secret data that should 46 | only be available to authorized users, so now we're going to protect it 47 | using a C file. 48 | 49 | First, let's create the C file and an user, named "alice": 50 | 51 | $ htpasswd -c htpasswd alice 52 | 53 | Once it is created, we need to put the C file in a safe location 54 | outside of the public directory, so let's create a new directory 55 | C and store the file in there. 56 | 57 | (If you're migrating from Apache and already have the C file, you 58 | just need to copy it to your Dancer application.) 59 | 60 | In our Dancer application, we include the Auth::Htpasswd plugin: 61 | 62 | package MyWebApp; 63 | 64 | ... 65 | 66 | use Dancer::Plugin::Auth::Htpasswd; 67 | 68 | Now, we need to update our configuration file and add settings for the 69 | plugin. We'll tell it to protect the C path, and to use the 70 | C file we just created: 71 | 72 | plugins: 73 | "Auth::Htpasswd": 74 | paths: 75 | "/pub/secret": 76 | realm: "Secret Files" 77 | passwd_file: passwd/htpasswd 78 | 79 | The C parameter lets us set the text that will be shown to the 80 | user in the login window displayed by the browser. 81 | 82 | Let's see if our protection works. We restart the application and try to 83 | access the C URL: 84 | 85 | =for html 86 | 87 | Great, our confidential files are safe. Only when we log in as "Alice", 88 | we'll be able to access them: 89 | 90 | =for html 91 | 92 | =head1 AUTHOR 93 | 94 | L, C<< >> 95 | 96 | 97 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/14-dynamic-ajax-charts.pod: -------------------------------------------------------------------------------- 1 | =head1 Dynamic AJAX charting with Dancer and Highcharts 2 | 3 | Let's build a real simple AJAX-based application using Dancer to feed data to 4 | L to display a real-time graph of the 5 | load average of the webserver the application is running on. 6 | 7 | We'll serve up a page with the appropriate Javascript to utilise Highcharts, 8 | polling via AJAX requests to fetch fresh data from our Dancer app regularly. 9 | 10 | First, we'll need to write a simple Dancer app to serve up the HTML, along with 11 | handling AJAX requests for the current load average. 12 | 13 | I scaffolded the app with C, then edited 14 | C to contain the following simple code: 15 | 16 | package ajaxchartingdemo; 17 | use Dancer ':syntax'; 18 | use Dancer::Plugin::Ajax; 19 | 20 | use Unix::Uptime; 21 | 22 | get '/' => sub { 23 | template 'index'; 24 | }; 25 | 26 | ajax '/getloadavg' => sub { 27 | { 28 | timestamp => time, 29 | loadavg => ( Unix::Uptime->load )[0] 30 | }; 31 | }; 32 | 33 | true; 34 | 35 | I want that hashref the C handler is returning to be serialised to JSON 36 | for me, so I edited the C file and added: 37 | 38 | serializer: 'JSON' 39 | 40 | With that done, I then had to download L 41 | into C, and edit C to contain the 42 | appropriate HTML and mostly Javascript to display the chart. 43 | 44 | I'm not going to repeat the whole of that here, as that part isn't 45 | Dancer-related, and you can L 46 | anyway, but it was simple stuff; the part which fetched the loadavg using an 47 | AJAX request (courtesy of jQuery) was as simple as: 48 | 49 | setInterval(function() { 50 | $.getJSON('/getloadavg', function(response) { 51 | var point = [ 52 | response.timestamp * 1000, 53 | response.loadavg - 0 54 | ];; 55 | series.addPoint(point, true, shiftalong); 56 | }) 57 | }, 2000); 58 | 59 | With all this done, we're ready to see what we get! 60 | 61 | During the advent calendar period, you can try it out live at: 62 | 63 | L 64 | 65 | (This will likely be discontinued in the near future, but the demo app will 66 | still be available on GitHub for you to try yourself.) 67 | 68 | You should be presented with a pretty graph, which updates automatically: 69 | 70 | =for html 71 | 72 | =head2 Getting the example app code 73 | 74 | The example application is available on GitHub: 75 | 76 | L 77 | 78 | 79 | =head1 AUTHOR 80 | 81 | David Precious, with thanks to C on IRC for the idea and C for help 82 | whilst I was hurriedly writing this post. 83 | 84 | 85 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/15-sending-emails-with-dancer.pod: -------------------------------------------------------------------------------- 1 | =head1 Sending email in Dancer using Dancer::Plugin::Email 2 | 3 | Many sites use email forms to provide a fast and comfortable feedback method. 4 | The user just types their details and message and clicks on 'Submit' button, and 5 | the details are sent to a specified email address. 6 | 7 | If you want to send email from your Dancer application, you can do so easily 8 | using the L plugin. 9 | 10 | =head2 How do I use it? 11 | 12 | L is very simple to use - in fact, there is only one 13 | function, C, which sends email with the options specified.. The C 14 | call with all options looks like: 15 | 16 | email { 17 | to => 'user@example.ru', 18 | subject => 'I found a bug in your site', 19 | msg => 'BlahBlahBlah', 20 | attachment => ['/sbin/rm'], 21 | type => 'text', 22 | headers => { 23 | "X-Mailer" => 'This fine Dancer application', 24 | "X-Accept-Language" => 'en' 25 | }, 26 | encoding => 'base64', 27 | }; 28 | 29 | =head2 Simple example 30 | 31 | 32 | use Dancer; 33 | use Dancer::Plugin::Email; 34 | 35 | post '/contact' => sub { 36 | email { 37 | to => '...', 38 | subject => '...', 39 | message => $msg, 40 | }; 41 | }; 42 | 43 | =head2 Code recipes 44 | 45 | Below we give you some code recipes to show how to use Dancer::Plugin::Email 46 | 47 | =head3 Errors handling 48 | 49 | # Handle Email Failures 50 | 51 | post '/contact' => sub { 52 | 53 | my $msg = email { 54 | to => '...', 55 | subject => '...', 56 | message => $msg, 57 | encoding => 'base64', 58 | }; 59 | 60 | warn $msg->{string} if $msg->{type} eq 'failure'; 61 | 62 | }; 63 | 64 | =head2 Adding additional email headers 65 | 66 | email { 67 | to => '...', 68 | subject => '...', 69 | message => $msg, 70 | headers => { 71 | "X-Mailer" => 'This fine Dancer application', 72 | "X-Accept-Language" => 'en' 73 | } 74 | }; 75 | 76 | =head2 Sending text and HTML multi-part messages 77 | 78 | email { 79 | to => '...', 80 | subject => '...', 81 | type => 'multi', # must be set to "multi" to send both parts 82 | message => { 83 | text => $txt, 84 | html => $html, 85 | }, 86 | }; 87 | 88 | =head1 AUTHOR 89 | 90 | Kindly authored by Andrew Inishev, a student participating in the Google 91 | Code-In, mentored by David Precious - thanks Andrew for your contribution! 92 | 93 | 94 | -------------------------------------------------------------------------------- /danceradvent/public/articles/2011/18-alternative-templating-engines.pod: -------------------------------------------------------------------------------- 1 | =head1 Alternative Dancer Templating Engines 2 | 3 | Dancer uses a simple model of interfacing with templating engines (based on 4 | L) and makes it very easy to add support for new 5 | engines. Thanks to this, if you're not happy with the default C engine 6 | or with L