Codebase list libdancer2-perl / b6e09c6
Imported Upstream version 0.09+dfsg Nuno Carvalho 10 years ago
120 changed file(s) with 2631 addition(s) and 2239 deletion(s). Raw diff Collapse all Expand all
1717 "Dancer Core Developers"
1818 ],
1919 "dist_name" => "Dancer2",
20 "dist_version" => "0.07",
20 "dist_version" => "0.09",
2121 "license" => "perl",
2222 "module_name" => "Dancer2",
2323 "recommends" => {
3535 "recursive_test_files" => 1,
3636 "requires" => {
3737 "Carp" => 0,
38 "Class::Load" => 0,
3839 "Config::Any" => 0,
3940 "Cwd" => 0,
4041 "Data::Dumper" => 0,
6061 "List::Util" => 0,
6162 "MIME::Base64" => "3.13",
6263 "MIME::Types" => 0,
63 "Module::Runtime" => 0,
6464 "Moo" => "1.003000",
6565 "Moo::Role" => 0,
6666 "MooX::Types::MooseLike" => "0.16",
7070 "Pod::Simple::Search" => 0,
7171 "Pod::Simple::SimpleTree" => 0,
7272 "Pod::Usage" => 0,
73 "Role::Tiny" => "1.003000",
7374 "Scalar::Util" => 0,
7475 "Template" => 0,
7576 "Template::Tiny" => 0,
7980 "URI::Escape" => 0,
8081 "YAML::Any" => 0,
8182 "constant" => 0,
82 "lib" => 0,
8383 "overload" => 0,
8484 "parent" => 0,
8585 "perl" => "5.00503",
9797 "HTTP::Body" => 0,
9898 "HTTP::Request::Common" => 0,
9999 "HTTP::Server::Simple::PSGI" => 0,
100 "IO::Handle" => 0,
101 "IPC::Open3" => 0,
100102 "Test::Fatal" => 0,
101103 "Test::MockTime" => 0,
102104 "Test::More" => "0.92",
103 "Test::Script" => "1.05",
105 "Test::Script" => 0,
104106 "Test::TCP" => "1.13",
105107 "YAML" => 0,
106108 "YAML::Any" => 0,
107 "blib" => 0,
109 "lib" => 0,
108110 "utf8" => 0,
109111 "vars" => 0
110112 }
0 0.09 2013-09-02 00:12:58 Asia/Jerusalem
1
2 [ ENHANCEMENTS ]
3 * Rewite DSL keyword engine (Mickey Nasriachi)
4 * Require minimum Role::Tiny 1.003000 (Alberto Simões)
5 * GH#382: Move Request attributes to params, and fix serializers
6 behavior (Russell Jenkins)
7 * GH#406: Replace Dancer2::ModuleLoader with Class::Load
8 (Alberto Simões, Sawyer X)
9 * GH#329: Remove 'load_app' DSL keyword. Remove reference to
10 'load' as well. (Sawyer X)
11 * GH#412: Autopages are now called properly with correct MIME.
12 (Alberto Simões)
13
14 [ DOCUMENTATION ]
15 * GH#390: minor cookbook documentation fixes (Russell Jenkins)
16 * GH#392: remove support to auto_reload and suggest alternative
17 in Dancer2::Cookbook (Ahmad M. Zawawi)
18 * GH#397,407: Miscellaneous documentation fixes (Andrew Solomon)
19 * Documentation cleanups (Alex Beamish)
20
21 [ BUG FIXES ]
22 * When compiling route regex object with prefix, add the closing anchor
23 (Mickey Nasriachi)
24 * GH#386: honor log level defined in config file (Alberto Simões)
25 * GH#396,409: Miscellaneous bug fixes (Russell Jenkins)
26 * GH#403: Fix forward behavior (Russell Jenkins)
27
28 0.08 2013-08-18 15:22:45 Asia/Jerusalem
29
30 [ ENHANCEMENTS ]
31 * GH#352: Define content_type as a property for serializers. (Franck Cuny)
32 * Cleanup duplicate HTTP status code between Core::Error and Core::HTTP
33 (Russel Jenkins)
34 * GH#363: Move core methods to Dancer2::Core (Alberto Simões)
35 * GH#362: Serializers documentation and test cleanup. (Franck Cuny)
36 * Refactoring of the engine method. (Franck Cuny)
37 * Misc. code cleanup. (Russel Jenkins)
38 * GH#280: Remove the unused ':syntax' importing tag (Sawyer X)
39 * Display startup info only if environment is "development" (Franck Cuny)
40 * Move postponed_hooks to server from runner (Sawyer X)
41 * Provide easier access to global runner (Sawyer X)
42 * Bunch of code cleanups which also includes speed boost (Sawyer X)
43 * More immutability in the runner class and config role (Sawyer X)
44
45 [ BUG FIXES ]
46 * GH#85, GH#354: Fix autopages, especially in subdirs
47 (Stefan Hornburg, Alberto Simões)
48 * GH#365: Fix serializer settings (Steven Humphrey)
49 * GH#333: callerstack for logger was too short (Alberto Simões)
50 * GH#369: Move request deserialization from Dispatcher to Content & Request
51 (Russell Jenkins)
52
53 [ DOCUMENTATION ]
54 * GH#192: Documentation the current usage of middlewares using
55 Plack::Builder (Sawyer X)
56 * GH#195, GH#197, GH#372: Multiple apps with Plack::Builder (Sawyer X)
57 * GH#348: Documentation of Role::Logger (Franck Cuny)
58 * GH#350: Move part of README.md to GitGuide.md (Franck Cuny)
59 * GH#353: Documentation of Role::Serializer (Alberto Simões, Franck Cuny)
60 * Misc. minor documentation tweak (Alberto Simões, Franck Cuny)
61
062 0.07 2013-08-04 01:14:59 Asia/Jerusalem
163
264 [ ENHANCEMENTS ]
0 # Git Guide
1
2 This guide will help you to set up your environment to be able to work
3 on the Dancer2's repository.
4
5 ## Contributing
6
7 This guide has been written to help anyone interested in contributing
8 to the development of Dancer2.
9
10 First of all - thank you for your interest in the project! It's the
11 community of helpful contributors who've helped Dancer grow
12 phenomenally. Without the community we wouldn't be where we are today!
13
14 Please read this guide before contributing to Dancer2, to avoid wasted
15 effort and maximizing the chances of your contributions being used.
16
17 There are many ways to contribute to the project. Dancer2 is a young
18 yet active project and any kind of help is very much appreciated!
19
20 ### Documentation
21
22 We value documentation very much, but it's difficult to keep it
23 up-to-date. If you find a typo or an error in the documentation
24 please do let us know - ideally by submitting a patch (pull request)
25 with your fix or suggestion (see
26 [Patch Submission](#environment-and-patch-submission)).
27
28 ### Code
29
30 You can write extensions (plugins) for Dancer2 extending core
31 functionality or contribute to Dancer2's core code, see
32 [Patch Submission](#environment-and-patch-submission) below.
33
34 ## General Development Guidelines
35
36 This section lists high-level recommendations for developing Dancer2,
37 for more detailed guidelines, see [Coding Guidelines](#coding-guidelines)
38 below.
39
40 ### Quality Assurance
41
42 Dancer2 should be able to install for all Perl versions since 5.8, on
43 any platform for which Perl exists. We focus mainly on GNU/Linux (any
44 distribution), *BSD and Windows (native and Cygwin).
45
46 We should avoid regressions as much as possible and keep backwards
47 compatibility in mind when refactoring. Stable releases should not
48 break functionality and new releases should provide an upgrade path
49 and upgrade tips such as warning the user about deprecated
50 functionality.
51
52 ### Quality Supervision
53
54 We can measure our quality using the
55 [CPAN testers platform](http://www.cpantesters.org).
56
57 A good way to help the project is to find a failing build log on the
58 [CPAN testers](http://www.cpantesters.org/distro/D/Dancer2.html).
59
60 If you find a failing test report, feel free to report it as a
61 [GitHub issue](http://github.com/PerlDancer/Dancer2/issues).
62
63 ### Reporting Bugs
64
65 We prefer to have all our bug reports on GitHub, in the
66 [issues section](http://github.com/PerlDancer/Dancer2/issues).
67
68 Please make sure the bug you're reporting does not yet exist. In doubt
69 please ask on IRC.
70
71 ## Environment and Patch Submission
72
73 ### Set up a development environment
74
75 If you want to submit a patch for Dancer2, you need git and very
76 likely also [_Dist::Zilla_](https://metacpan.org/module/Dist::Zilla).
77 We also recommend perlbrew (see below) or,
78 alternatively, [_App::Plenv_](https://github.com/tokuhirom/plenv))
79 to test and develop Dancer2 on a recent
80 version of perl. We also suggest
81 [_App::cpanminus_](https://metacpan.org/module/App::cpanminus)
82 to quickly and comfortably install perl modules.
83
84 In the following sections we provide tips for the installation of some
85 of these tools together with Dancer. Please also see the documentation
86 that comes with these tools for more info.
87
88 #### Perlbrew tips (Optional)
89
90 Install perlbrew for example with
91
92 $ cpanm App::perlbrew
93
94 Check which perls are available
95
96 $ perlbrew available
97
98 It should list the available perl versions, like this (incomplete) list:
99
100 perl-5.17.1
101 perl-5.16.0
102 perl-5.14.2
103 perl-5.12.4
104 ...
105
106 Then go on and install a version inside Perlbrew. We recommend you
107 give a name to the installation (`--as` option), as well as compiling
108 without the tests (`--n` option) to speed it up.
109
110 $ perlbrew install -n perl-5.14.2 --as dancer_development -j 3
111
112 Wait a while, and it should be done. Switch to your new Perl with:
113
114 $ perlbrew switch dancer_development
115
116 Now you are using the fresh Perl, you can check it with:
117
118 $ which perl
119
120 Install cpanm on your brewed version of perl.
121
122 $ perlbrew install-cpanm
123
124
125 ### Install various dependencies (required)
126
127 Install Dist::Zilla
128
129 $ cpanm Dist::Zilla
130
131 ### Get Dancer2 sources
132
133 Get the Dancer sources from github (for a more complete git workflow
134 see below):
135
136 Clone your fork to have a local copy using the following command:
137
138 $ git clone git://github.com/perldancer/Dancer2.git
139
140 The Dancer2 sources come with a `dist.ini`. That's the configuration
141 files for _Dist::Zilla_, so that it knows how to build Dancer2. Let's
142 use dist.ini to install additional `Dist::Zilla` plugins which are
143 not yet installed on your system (or perl installation):
144
145 $ dzil authordeps | cpanm -n
146
147 That should install a bunch of stuff. Now that _Dist::Zilla_ is up and
148 running, you should install the dependencies required by Dancer2:
149
150 $ dzil listdeps | cpanm -n
151
152 When that is done, you're good to go! You can use dzil to build and test Dancer2:
153
154 $ dzil build
155 $ dzil test
156
157
158 ### Patch Submission (Github workflow)
159
160 The Dancer2 development team uses GitHub to collaborate. We greatly
161 appreciate contributions submitted via GitHub, as it makes tracking
162 these contributions and applying them much, much easier. This gives
163 your contribution a much better chance of being integrated into
164 Dancer2 quickly!
165
166 **NOTE:** unlike before, we no longer use the _devel_ branch. All
167 active development is performed in the _master_ branch. Therefore, all
168 your contribution work should be done in a fork of the _master_
169 branch.
170
171 Here is the workflow for submitting a patch:
172
173 1. Fork the repository: http://github.com/PerlDancer/Dancer2 and click "Fork";
174
175 2. Clone your fork to have a local copy using the following command:
176
177 $ git clone git://github.com/myname/Dancer2.git
178
179 3. As a contributor, you should **always** work on the `master` branch of
180 your clone.
181
182 $ git remote add upstream https://github.com/PerlDancer/Dancer2.git
183 $ git fetch upstream
184
185 This will create a local branch in your clone named _master_ and
186 that will track the official _master_ branch. That way, if you have
187 more or less commits than the upstream repo, you'll be immediately
188 notified by git.
189
190 4. You want to isolate all your commits in a _topic_ branch, this
191 will make the reviewing much easier for the core team and will
192 allow you to continue working on your clone without worrying about
193 different commits mixing together.
194
195 To do that, first create a local branch to build your pull request:
196
197 # you should be in master here
198 $ git checkout -b pr/$name
199
200 Now you have created a local branch named _pr/$name_ where _$name_
201 is the name you want (it should describe the purpose of the pull
202 request you're preparing).
203
204 In that branch, do all the commits you need (the more the better)
205 and when done, push the branch to your fork:
206
207 # ... commits ...
208 git push origin pr/$name
209
210 You are now ready to send a pull request.
211
212 5. Send a _pull request_ via the GitHub interface. Make sure your pull
213 request is based on the _pr/$name_ branch you've just pushed, so
214 that it incorporates the appropriate commits only.
215
216 It's also a good idea to summarize your work in a report sent to
217 the users mailing list (see below), in order to make sure the team
218 is aware of it.
219
220 You could also notify the core team on IRC, on `irc.perl.org`,
221 channel `#dancer` or via [web client](http://www.perldancer.org/irc).
222
223 6. When the core team reviews your pull request, it will either accept
224 (and then merge into _master_) or refuse your request.
225
226 If it's refused, try to understand the reasons explained by the
227 team for the denial. Most of the time, communicating with the core
228 team is enough to understand what the mistake was. Above all,
229 please don't be offended.
230
231 If your pull-request is merged into _master_, then all you have to
232 do is to remove your local and remote _pr/$name_ branch:
233
234 $ git checkout master
235 $ git branch -D pr/$name
236 $ git push origin :pr/$name
237
238 And then, of course, you need to sync your local devel branch with
239 the upstream:
240
241 $ git pull upstream master
242 $ git push origin master
243
244 You're now ready to start working on a new pull request!
00 AUTHORS
11 Build.PL
22 Changes
3 GitGuide.md
34 LICENSE
45 MANIFEST
56 META.json
5455 lib/Dancer2/Logger/Note.pm
5556 lib/Dancer2/Logger/Null.pm
5657 lib/Dancer2/Manual.pod
57 lib/Dancer2/ModuleLoader.pm
5858 lib/Dancer2/Plugin.pm
5959 lib/Dancer2/Plugin/Ajax.pm
6060 lib/Dancer2/Plugins.pod
7070 lib/Dancer2/Test.pm
7171 lib/Dancer2/Tutorial.pod
7272 script/dancer2
73 t/.multiserver.t.swp
7473 t/00-compile.t
7574 t/00-report-prereqs.t
7675 t/ajax_plugin.t
8382 t/app/t2/.dancer
8483 t/app/t2/config.yml
8584 t/app/t2/lib/App3.pm
85 t/auto_page.t
8686 t/charset_server.t
8787 t/config.t
8888 t/config.yml
114114 t/factory.t
115115 t/file_utils.t
116116 t/forward.t
117 t/forward_before_hook.t
117118 t/forward_test_tcp.t
118119 t/handler_file.t
119120 t/hooks.t
120121 t/http_methods.t
122 t/http_status.t
121123 t/lib/App1.pm
122124 t/lib/App2.pm
123125 t/lib/DancerPlugin.pm
133135 t/logger.t
134136 t/logger_console.t
135137 t/mime.t
136 t/path_info.t
137138 t/plugin_import.t
138139 t/plugin_multiple_apps.t
139140 t/plugin_register.t
161162 t/session_forward.t
162163 t/session_lifecycle.t
163164 t/session_object.t
165 t/sessions/Uf-CrAAAdd2mP4pJAIBzc6YRqXnqeNUt.yml
166 t/sessions/Uf-GsQAANz9OaEztWyuSfJKegUq3zD1V.yml
167 t/sessions/Uf-HDgAAPPOeyDGX2_IgTLzBg6iBsFkk.yml
168 t/sessions/Uf-HTQAAQOJeFHxmhw1aJKUgIkGseaAI.yml
169 t/sessions/Uf-HZwAAQt4cqJPl7UTFUrFyY7yxYVj_.yml
170 t/sessions/Uf-IdQAAVEongozIzRJZUxy_Q8DMaruI.yml
164171 t/sessions/Uf1awwAAXkpPhxmXEwjwLY6Oi5Dv7A8v.yml
165172 t/sessions/Uf1znwAABxlBOUOk04NfndOLhVZzIrnf.yml
173 t/sessions/Uf61DgAAYKLILb8PdeeQV3tryrC08MvP.yml
174 t/sessions/Uf6vLQAAA33ysk1AfxuFaITEMXY72bEI.yml
175 t/sessions/Uf6vlwAACre4MIFKa-Jghj6q1OT9sQb_.yml
176 t/sessions/Uf6xeQAAKIXcote58neJ3jjOIjbBlhKX.yml
177 t/sessions/Uf6zmAAASb6UqmpBspaQIEzjLIs51E19.yml
166178 t/sessions/UfRHAQAAJsw_2oX_cvBqBtXlgre8vFiy.yml
167179 t/sessions/UfRXcwAAO8pp0bx4QwYmc73cP7UKMyY7.yml
168180 t/sessions/UfUFzgAAOBHtc6NRAKAH4xNgo-GIz8VA.yml
169181 t/sessions/Ufqu9QAAQfNUOjyt9fa2al5-bFlpbWfR.yml
182 t/sessions/UgEFqgAAa4HX5JbR80YhN9rOBcGLG-zH.yml
183 t/sessions/UgnzfAAAX0Gcwelnex1vHnakFt7dZMGb.yml
184 t/sessions/UhC04wAAcgtUmse-OjZCvj1H0_I5z0pU.yml
185 t/sessions/UhC_EwAAHS4yKDNKIO8DV-5B47BS6vWY.yml
186 t/sessions/UhaF7gAAYpJSGqByK67_SB3A9mGE5l-s.yml
187 t/sessions/UhaGNQAAaDVWUzXrQaDQAaQPzLEWV-4v.yml
188 t/sessions/UiMfZwAAc4FCMkrxKK-kKyuTIDuVupPc.yml
189 t/sessions/UiMnAgAAahCCdaVhPiVfrcwyo6sxcp7z.yml
190 t/sessions/UiNDXgAAJ0mTXspthJgvbjShFZfcyPR3.yml
191 t/sessions/UiNIAwAAbVyD4lIKW93APRO9IDd1K5BJ.yml
192 t/sessions/UiNIHQAAb3tN7zHtZJRZY7ULJVTc4-bo.yml
170193 t/shared_engines.t
171194 t/special_import.t
172195 t/splat.t
210233 t/types.t
211234 t/uri_for.t
212235 t/vars.t
236 t/views/auto_page.tt
237 t/views/folder/page.tt
213238 t/views/index.tt
214239 t/views/layouts/main.tt
215240 t/views/tokens.tt
4545 },
4646 "requires" : {
4747 "Carp" : "0",
48 "Class::Load" : "0",
4849 "Config::Any" : "0",
4950 "Cwd" : "0",
5051 "Data::Dumper" : "0",
7071 "List::Util" : "0",
7172 "MIME::Base64" : "3.13",
7273 "MIME::Types" : "0",
73 "Module::Runtime" : "0",
7474 "Moo" : "1.003000",
7575 "Moo::Role" : "0",
7676 "MooX::Types::MooseLike" : "0.16",
8080 "Pod::Simple::Search" : "0",
8181 "Pod::Simple::SimpleTree" : "0",
8282 "Pod::Usage" : "0",
83 "Role::Tiny" : "1.003000",
8384 "Scalar::Util" : "0",
8485 "Template" : "0",
8586 "Template::Tiny" : "0",
8990 "URI::Escape" : "0",
9091 "YAML::Any" : "0",
9192 "constant" : "0",
92 "lib" : "0",
9393 "overload" : "0",
9494 "parent" : "0",
9595 "perl" : "5.00503",
9797 "warnings" : "0"
9898 },
9999 "suggests" : {
100 "Class::Load::XS" : "0",
100101 "Fcntl" : "0",
101102 "YAML::Any" : "0"
102103 }
110111 "HTTP::Body" : "0",
111112 "HTTP::Request::Common" : "0",
112113 "HTTP::Server::Simple::PSGI" : "0",
114 "IO::Handle" : "0",
115 "IPC::Open3" : "0",
113116 "Test::Fatal" : "0",
114117 "Test::MockTime" : "0",
115118 "Test::More" : "0.92",
116 "Test::Script" : "1.05",
119 "Test::Script" : "0",
117120 "Test::TCP" : "1.13",
118121 "YAML" : "0",
119122 "YAML::Any" : "0",
120 "blib" : "0",
123 "lib" : "0",
121124 "utf8" : "0",
122125 "vars" : "0"
123126 }
126129 "provides" : {
127130 "Dancer2" : {
128131 "file" : "lib/Dancer2.pm",
129 "version" : "0.07"
132 "version" : "0.09"
130133 },
131134 "Dancer2::Config" : {
132135 "file" : "lib/Dancer2/Config.pod",
133 "version" : "0.07"
136 "version" : "0.09"
134137 },
135138 "Dancer2::Cookbook" : {
136139 "file" : "lib/Dancer2/Cookbook.pod",
137 "version" : "0.07"
140 "version" : "0.09"
138141 },
139142 "Dancer2::Core" : {
140143 "file" : "lib/Dancer2/Core.pm",
141 "version" : "0.07"
144 "version" : "0.09"
142145 },
143146 "Dancer2::Core::App" : {
144147 "file" : "lib/Dancer2/Core/App.pm",
145 "version" : "0.07"
148 "version" : "0.09"
146149 },
147150 "Dancer2::Core::Context" : {
148151 "file" : "lib/Dancer2/Core/Context.pm",
149 "version" : "0.07"
152 "version" : "0.09"
150153 },
151154 "Dancer2::Core::Cookie" : {
152155 "file" : "lib/Dancer2/Core/Cookie.pm",
153 "version" : "0.07"
156 "version" : "0.09"
154157 },
155158 "Dancer2::Core::DSL" : {
156159 "file" : "lib/Dancer2/Core/DSL.pm",
157 "version" : "0.07"
160 "version" : "0.09"
158161 },
159162 "Dancer2::Core::Dispatcher" : {
160163 "file" : "lib/Dancer2/Core/Dispatcher.pm",
161 "version" : "0.07"
164 "version" : "0.09"
162165 },
163166 "Dancer2::Core::Error" : {
164167 "file" : "lib/Dancer2/Core/Error.pm",
165 "version" : "0.07"
168 "version" : "0.09"
166169 },
167170 "Dancer2::Core::Factory" : {
168171 "file" : "lib/Dancer2/Core/Factory.pm",
169 "version" : "0.07"
172 "version" : "0.09"
170173 },
171174 "Dancer2::Core::HTTP" : {
172175 "file" : "lib/Dancer2/Core/HTTP.pm",
173 "version" : "0.07"
176 "version" : "0.09"
174177 },
175178 "Dancer2::Core::Hook" : {
176179 "file" : "lib/Dancer2/Core/Hook.pm",
177 "version" : "0.07"
180 "version" : "0.09"
178181 },
179182 "Dancer2::Core::MIME" : {
180183 "file" : "lib/Dancer2/Core/MIME.pm",
181 "version" : "0.07"
184 "version" : "0.09"
182185 },
183186 "Dancer2::Core::Request" : {
184187 "file" : "lib/Dancer2/Core/Request.pm",
185 "version" : "0.07"
188 "version" : "0.09"
186189 },
187190 "Dancer2::Core::Request::Upload" : {
188191 "file" : "lib/Dancer2/Core/Request/Upload.pm",
189 "version" : "0.07"
192 "version" : "0.09"
190193 },
191194 "Dancer2::Core::Response" : {
192195 "file" : "lib/Dancer2/Core/Response.pm",
193 "version" : "0.07"
196 "version" : "0.09"
194197 },
195198 "Dancer2::Core::Role::Config" : {
196199 "file" : "lib/Dancer2/Core/Role/Config.pm",
197 "version" : "0.07"
200 "version" : "0.09"
198201 },
199202 "Dancer2::Core::Role::DSL" : {
200203 "file" : "lib/Dancer2/Core/Role/DSL.pm",
201 "version" : "0.07"
204 "version" : "0.09"
202205 },
203206 "Dancer2::Core::Role::Engine" : {
204207 "file" : "lib/Dancer2/Core/Role/Engine.pm",
205 "version" : "0.07"
208 "version" : "0.09"
206209 },
207210 "Dancer2::Core::Role::Handler" : {
208211 "file" : "lib/Dancer2/Core/Role/Handler.pm",
209 "version" : "0.07"
212 "version" : "0.09"
210213 },
211214 "Dancer2::Core::Role::Headers" : {
212215 "file" : "lib/Dancer2/Core/Role/Headers.pm",
213 "version" : "0.07"
216 "version" : "0.09"
214217 },
215218 "Dancer2::Core::Role::Hookable" : {
216219 "file" : "lib/Dancer2/Core/Role/Hookable.pm",
217 "version" : "0.07"
220 "version" : "0.09"
218221 },
219222 "Dancer2::Core::Role::Logger" : {
220223 "file" : "lib/Dancer2/Core/Role/Logger.pm",
221 "version" : "0.07"
224 "version" : "0.09"
222225 },
223226 "Dancer2::Core::Role::Serializer" : {
224227 "file" : "lib/Dancer2/Core/Role/Serializer.pm",
225 "version" : "0.07"
228 "version" : "0.09"
226229 },
227230 "Dancer2::Core::Role::Server" : {
228231 "file" : "lib/Dancer2/Core/Role/Server.pm",
229 "version" : "0.07"
232 "version" : "0.09"
230233 },
231234 "Dancer2::Core::Role::SessionFactory" : {
232235 "file" : "lib/Dancer2/Core/Role/SessionFactory.pm",
233 "version" : "0.07"
236 "version" : "0.09"
234237 },
235238 "Dancer2::Core::Role::SessionFactory::File" : {
236239 "file" : "lib/Dancer2/Core/Role/SessionFactory/File.pm",
237 "version" : "0.07"
240 "version" : "0.09"
238241 },
239242 "Dancer2::Core::Role::StandardResponses" : {
240243 "file" : "lib/Dancer2/Core/Role/StandardResponses.pm",
241 "version" : "0.07"
244 "version" : "0.09"
242245 },
243246 "Dancer2::Core::Role::Template" : {
244247 "file" : "lib/Dancer2/Core/Role/Template.pm",
245 "version" : "0.07"
248 "version" : "0.09"
246249 },
247250 "Dancer2::Core::Route" : {
248251 "file" : "lib/Dancer2/Core/Route.pm",
249 "version" : "0.07"
252 "version" : "0.09"
250253 },
251254 "Dancer2::Core::Runner" : {
252255 "file" : "lib/Dancer2/Core/Runner.pm",
253 "version" : "0.07"
256 "version" : "0.09"
254257 },
255258 "Dancer2::Core::Server::PSGI" : {
256259 "file" : "lib/Dancer2/Core/Server/PSGI.pm",
257 "version" : "0.07"
260 "version" : "0.09"
258261 },
259262 "Dancer2::Core::Server::Standalone" : {
260263 "file" : "lib/Dancer2/Core/Server/Standalone.pm",
261 "version" : "0.07"
264 "version" : "0.09"
262265 },
263266 "Dancer2::Core::Session" : {
264267 "file" : "lib/Dancer2/Core/Session.pm",
265 "version" : "0.07"
268 "version" : "0.09"
266269 },
267270 "Dancer2::Core::Time" : {
268271 "file" : "lib/Dancer2/Core/Time.pm",
269 "version" : "0.07"
272 "version" : "0.09"
270273 },
271274 "Dancer2::Core::Types" : {
272275 "file" : "lib/Dancer2/Core/Types.pm",
273 "version" : "0.07"
276 "version" : "0.09"
274277 },
275278 "Dancer2::FileUtils" : {
276279 "file" : "lib/Dancer2/FileUtils.pm",
277 "version" : "0.07"
280 "version" : "0.09"
278281 },
279282 "Dancer2::Handler::AutoPage" : {
280283 "file" : "lib/Dancer2/Handler/AutoPage.pm",
281 "version" : "0.07"
284 "version" : "0.09"
282285 },
283286 "Dancer2::Handler::File" : {
284287 "file" : "lib/Dancer2/Handler/File.pm",
285 "version" : "0.07"
288 "version" : "0.09"
286289 },
287290 "Dancer2::Logger::Capture" : {
288291 "file" : "lib/Dancer2/Logger/Capture.pm",
289 "version" : "0.07"
292 "version" : "0.09"
290293 },
291294 "Dancer2::Logger::Capture::Trap" : {
292295 "file" : "lib/Dancer2/Logger/Capture/Trap.pm",
293 "version" : "0.07"
296 "version" : "0.09"
294297 },
295298 "Dancer2::Logger::Console" : {
296299 "file" : "lib/Dancer2/Logger/Console.pm",
297 "version" : "0.07"
300 "version" : "0.09"
298301 },
299302 "Dancer2::Logger::Diag" : {
300303 "file" : "lib/Dancer2/Logger/Diag.pm",
301 "version" : "0.07"
304 "version" : "0.09"
302305 },
303306 "Dancer2::Logger::File" : {
304307 "file" : "lib/Dancer2/Logger/File.pm",
305 "version" : "0.07"
308 "version" : "0.09"
306309 },
307310 "Dancer2::Logger::Note" : {
308311 "file" : "lib/Dancer2/Logger/Note.pm",
309 "version" : "0.07"
312 "version" : "0.09"
310313 },
311314 "Dancer2::Logger::Null" : {
312315 "file" : "lib/Dancer2/Logger/Null.pm",
313 "version" : "0.07"
316 "version" : "0.09"
314317 },
315318 "Dancer2::Manual" : {
316319 "file" : "lib/Dancer2/Manual.pod",
317 "version" : "0.07"
318 },
319 "Dancer2::ModuleLoader" : {
320 "file" : "lib/Dancer2/ModuleLoader.pm",
321 "version" : "0.07"
320 "version" : "0.09"
322321 },
323322 "Dancer2::Plugin" : {
324323 "file" : "lib/Dancer2/Plugin.pm",
325 "version" : "0.07"
324 "version" : "0.09"
326325 },
327326 "Dancer2::Plugin::Ajax" : {
328327 "file" : "lib/Dancer2/Plugin/Ajax.pm",
329 "version" : "0.07"
328 "version" : "0.09"
330329 },
331330 "Dancer2::Plugins" : {
332331 "file" : "lib/Dancer2/Plugins.pod",
333 "version" : "0.07"
332 "version" : "0.09"
334333 },
335334 "Dancer2::Serializer::Dumper" : {
336335 "file" : "lib/Dancer2/Serializer/Dumper.pm",
337 "version" : "0.07"
336 "version" : "0.09"
338337 },
339338 "Dancer2::Serializer::JSON" : {
340339 "file" : "lib/Dancer2/Serializer/JSON.pm",
341 "version" : "0.07"
340 "version" : "0.09"
342341 },
343342 "Dancer2::Serializer::YAML" : {
344343 "file" : "lib/Dancer2/Serializer/YAML.pm",
345 "version" : "0.07"
344 "version" : "0.09"
346345 },
347346 "Dancer2::Session::Simple" : {
348347 "file" : "lib/Dancer2/Session/Simple.pm",
349 "version" : "0.07"
348 "version" : "0.09"
350349 },
351350 "Dancer2::Session::YAML" : {
352351 "file" : "lib/Dancer2/Session/YAML.pm",
353 "version" : "0.07"
352 "version" : "0.09"
354353 },
355354 "Dancer2::Template::Implementation::ForkedTiny" : {
356355 "file" : "lib/Dancer2/Template/Implementation/ForkedTiny.pm",
357 "version" : "0.07"
356 "version" : "0.09"
358357 },
359358 "Dancer2::Template::Simple" : {
360359 "file" : "lib/Dancer2/Template/Simple.pm",
361 "version" : "0.07"
360 "version" : "0.09"
362361 },
363362 "Dancer2::Template::TemplateToolkit" : {
364363 "file" : "lib/Dancer2/Template/TemplateToolkit.pm",
365 "version" : "0.07"
364 "version" : "0.09"
366365 },
367366 "Dancer2::Template::Tiny" : {
368367 "file" : "lib/Dancer2/Template/Tiny.pm",
369 "version" : "0.07"
368 "version" : "0.09"
370369 },
371370 "Dancer2::Test" : {
372371 "file" : "lib/Dancer2/Test.pm",
373 "version" : "0.07"
372 "version" : "0.09"
374373 },
375374 "Dancer2::Tutorial" : {
376375 "file" : "lib/Dancer2/Tutorial.pod",
377 "version" : "0.07"
376 "version" : "0.09"
378377 }
379378 },
380379 "release_status" : "stable",
387386 "url" : "https://github.com/PerlDancer/Dancer2"
388387 }
389388 },
390 "version" : "0.07"
389 "version" : "0.09"
391390 }
392391
99 HTTP::Body: 0
1010 HTTP::Request::Common: 0
1111 HTTP::Server::Simple::PSGI: 0
12 IO::Handle: 0
13 IPC::Open3: 0
1214 Module::Build: 0.3601
1315 Test::Fatal: 0
1416 Test::MockTime: 0
1517 Test::More: 0.92
16 Test::Script: 1.05
18 Test::Script: 0
1719 Test::TCP: 1.13
1820 YAML: 0
1921 YAML::Any: 0
20 blib: 0
22 lib: 0
2123 utf8: 0
2224 vars: 0
2325 configure_requires:
3335 provides:
3436 Dancer2:
3537 file: lib/Dancer2.pm
36 version: 0.07
38 version: 0.09
3739 Dancer2::Config:
3840 file: lib/Dancer2/Config.pod
39 version: 0.07
41 version: 0.09
4042 Dancer2::Cookbook:
4143 file: lib/Dancer2/Cookbook.pod
42 version: 0.07
44 version: 0.09
4345 Dancer2::Core:
4446 file: lib/Dancer2/Core.pm
45 version: 0.07
47 version: 0.09
4648 Dancer2::Core::App:
4749 file: lib/Dancer2/Core/App.pm
48 version: 0.07
50 version: 0.09
4951 Dancer2::Core::Context:
5052 file: lib/Dancer2/Core/Context.pm
51 version: 0.07
53 version: 0.09
5254 Dancer2::Core::Cookie:
5355 file: lib/Dancer2/Core/Cookie.pm
54 version: 0.07
56 version: 0.09
5557 Dancer2::Core::DSL:
5658 file: lib/Dancer2/Core/DSL.pm
57 version: 0.07
59 version: 0.09
5860 Dancer2::Core::Dispatcher:
5961 file: lib/Dancer2/Core/Dispatcher.pm
60 version: 0.07
62 version: 0.09
6163 Dancer2::Core::Error:
6264 file: lib/Dancer2/Core/Error.pm
63 version: 0.07
65 version: 0.09
6466 Dancer2::Core::Factory:
6567 file: lib/Dancer2/Core/Factory.pm
66 version: 0.07
68 version: 0.09
6769 Dancer2::Core::HTTP:
6870 file: lib/Dancer2/Core/HTTP.pm
69 version: 0.07
71 version: 0.09
7072 Dancer2::Core::Hook:
7173 file: lib/Dancer2/Core/Hook.pm
72 version: 0.07
74 version: 0.09
7375 Dancer2::Core::MIME:
7476 file: lib/Dancer2/Core/MIME.pm
75 version: 0.07
77 version: 0.09
7678 Dancer2::Core::Request:
7779 file: lib/Dancer2/Core/Request.pm
78 version: 0.07
80 version: 0.09
7981 Dancer2::Core::Request::Upload:
8082 file: lib/Dancer2/Core/Request/Upload.pm
81 version: 0.07
83 version: 0.09
8284 Dancer2::Core::Response:
8385 file: lib/Dancer2/Core/Response.pm
84 version: 0.07
86 version: 0.09
8587 Dancer2::Core::Role::Config:
8688 file: lib/Dancer2/Core/Role/Config.pm
87 version: 0.07
89 version: 0.09
8890 Dancer2::Core::Role::DSL:
8991 file: lib/Dancer2/Core/Role/DSL.pm
90 version: 0.07
92 version: 0.09
9193 Dancer2::Core::Role::Engine:
9294 file: lib/Dancer2/Core/Role/Engine.pm
93 version: 0.07
95 version: 0.09
9496 Dancer2::Core::Role::Handler:
9597 file: lib/Dancer2/Core/Role/Handler.pm
96 version: 0.07
98 version: 0.09
9799 Dancer2::Core::Role::Headers:
98100 file: lib/Dancer2/Core/Role/Headers.pm
99 version: 0.07
101 version: 0.09
100102 Dancer2::Core::Role::Hookable:
101103 file: lib/Dancer2/Core/Role/Hookable.pm
102 version: 0.07
104 version: 0.09
103105 Dancer2::Core::Role::Logger:
104106 file: lib/Dancer2/Core/Role/Logger.pm
105 version: 0.07
107 version: 0.09
106108 Dancer2::Core::Role::Serializer:
107109 file: lib/Dancer2/Core/Role/Serializer.pm
108 version: 0.07
110 version: 0.09
109111 Dancer2::Core::Role::Server:
110112 file: lib/Dancer2/Core/Role/Server.pm
111 version: 0.07
113 version: 0.09
112114 Dancer2::Core::Role::SessionFactory:
113115 file: lib/Dancer2/Core/Role/SessionFactory.pm
114 version: 0.07
116 version: 0.09
115117 Dancer2::Core::Role::SessionFactory::File:
116118 file: lib/Dancer2/Core/Role/SessionFactory/File.pm
117 version: 0.07
119 version: 0.09
118120 Dancer2::Core::Role::StandardResponses:
119121 file: lib/Dancer2/Core/Role/StandardResponses.pm
120 version: 0.07
122 version: 0.09
121123 Dancer2::Core::Role::Template:
122124 file: lib/Dancer2/Core/Role/Template.pm
123 version: 0.07
125 version: 0.09
124126 Dancer2::Core::Route:
125127 file: lib/Dancer2/Core/Route.pm
126 version: 0.07
128 version: 0.09
127129 Dancer2::Core::Runner:
128130 file: lib/Dancer2/Core/Runner.pm
129 version: 0.07
131 version: 0.09
130132 Dancer2::Core::Server::PSGI:
131133 file: lib/Dancer2/Core/Server/PSGI.pm
132 version: 0.07
134 version: 0.09
133135 Dancer2::Core::Server::Standalone:
134136 file: lib/Dancer2/Core/Server/Standalone.pm
135 version: 0.07
137 version: 0.09
136138 Dancer2::Core::Session:
137139 file: lib/Dancer2/Core/Session.pm
138 version: 0.07
140 version: 0.09
139141 Dancer2::Core::Time:
140142 file: lib/Dancer2/Core/Time.pm
141 version: 0.07
143 version: 0.09
142144 Dancer2::Core::Types:
143145 file: lib/Dancer2/Core/Types.pm
144 version: 0.07
146 version: 0.09
145147 Dancer2::FileUtils:
146148 file: lib/Dancer2/FileUtils.pm
147 version: 0.07
149 version: 0.09
148150 Dancer2::Handler::AutoPage:
149151 file: lib/Dancer2/Handler/AutoPage.pm
150 version: 0.07
152 version: 0.09
151153 Dancer2::Handler::File:
152154 file: lib/Dancer2/Handler/File.pm
153 version: 0.07
155 version: 0.09
154156 Dancer2::Logger::Capture:
155157 file: lib/Dancer2/Logger/Capture.pm
156 version: 0.07
158 version: 0.09
157159 Dancer2::Logger::Capture::Trap:
158160 file: lib/Dancer2/Logger/Capture/Trap.pm
159 version: 0.07
161 version: 0.09
160162 Dancer2::Logger::Console:
161163 file: lib/Dancer2/Logger/Console.pm
162 version: 0.07
164 version: 0.09
163165 Dancer2::Logger::Diag:
164166 file: lib/Dancer2/Logger/Diag.pm
165 version: 0.07
167 version: 0.09
166168 Dancer2::Logger::File:
167169 file: lib/Dancer2/Logger/File.pm
168 version: 0.07
170 version: 0.09
169171 Dancer2::Logger::Note:
170172 file: lib/Dancer2/Logger/Note.pm
171 version: 0.07
173 version: 0.09
172174 Dancer2::Logger::Null:
173175 file: lib/Dancer2/Logger/Null.pm
174 version: 0.07
176 version: 0.09
175177 Dancer2::Manual:
176178 file: lib/Dancer2/Manual.pod
177 version: 0.07
178 Dancer2::ModuleLoader:
179 file: lib/Dancer2/ModuleLoader.pm
180 version: 0.07
179 version: 0.09
181180 Dancer2::Plugin:
182181 file: lib/Dancer2/Plugin.pm
183 version: 0.07
182 version: 0.09
184183 Dancer2::Plugin::Ajax:
185184 file: lib/Dancer2/Plugin/Ajax.pm
186 version: 0.07
185 version: 0.09
187186 Dancer2::Plugins:
188187 file: lib/Dancer2/Plugins.pod
189 version: 0.07
188 version: 0.09
190189 Dancer2::Serializer::Dumper:
191190 file: lib/Dancer2/Serializer/Dumper.pm
192 version: 0.07
191 version: 0.09
193192 Dancer2::Serializer::JSON:
194193 file: lib/Dancer2/Serializer/JSON.pm
195 version: 0.07
194 version: 0.09
196195 Dancer2::Serializer::YAML:
197196 file: lib/Dancer2/Serializer/YAML.pm
198 version: 0.07
197 version: 0.09
199198 Dancer2::Session::Simple:
200199 file: lib/Dancer2/Session/Simple.pm
201 version: 0.07
200 version: 0.09
202201 Dancer2::Session::YAML:
203202 file: lib/Dancer2/Session/YAML.pm
204 version: 0.07
203 version: 0.09
205204 Dancer2::Template::Implementation::ForkedTiny:
206205 file: lib/Dancer2/Template/Implementation/ForkedTiny.pm
207 version: 0.07
206 version: 0.09
208207 Dancer2::Template::Simple:
209208 file: lib/Dancer2/Template/Simple.pm
210 version: 0.07
209 version: 0.09
211210 Dancer2::Template::TemplateToolkit:
212211 file: lib/Dancer2/Template/TemplateToolkit.pm
213 version: 0.07
212 version: 0.09
214213 Dancer2::Template::Tiny:
215214 file: lib/Dancer2/Template/Tiny.pm
216 version: 0.07
215 version: 0.09
217216 Dancer2::Test:
218217 file: lib/Dancer2/Test.pm
219 version: 0.07
218 version: 0.09
220219 Dancer2::Tutorial:
221220 file: lib/Dancer2/Tutorial.pod
222 version: 0.07
221 version: 0.09
223222 recommends:
224223 CGI::Deurl::XS: 0
225224 Crypt::URandom: 0
233232 URL::Encode::XS: 0
234233 requires:
235234 Carp: 0
235 Class::Load: 0
236236 Config::Any: 0
237237 Cwd: 0
238238 Data::Dumper: 0
258258 List::Util: 0
259259 MIME::Base64: 3.13
260260 MIME::Types: 0
261 Module::Runtime: 0
262261 Moo: 1.003000
263262 Moo::Role: 0
264263 MooX::Types::MooseLike: 0.16
268267 Pod::Simple::Search: 0
269268 Pod::Simple::SimpleTree: 0
270269 Pod::Usage: 0
270 Role::Tiny: 1.003000
271271 Scalar::Util: 0
272272 Template: 0
273273 Template::Tiny: 0
277277 URI::Escape: 0
278278 YAML::Any: 0
279279 constant: 0
280 lib: 0
281280 overload: 0
282281 parent: 0
283282 perl: 5.00503
287286 bugtracker: https://github.com/PerlDancer/Dancer2/issues
288287 homepage: http://perldancer.org/
289288 repository: https://github.com/PerlDancer/Dancer2
290 version: 0.07
289 version: 0.09
2525 "NAME" => "Dancer2",
2626 "PREREQ_PM" => {
2727 "Carp" => 0,
28 "Class::Load" => 0,
2829 "Config::Any" => 0,
2930 "Cwd" => 0,
3031 "Data::Dumper" => 0,
5051 "List::Util" => 0,
5152 "MIME::Base64" => "3.13",
5253 "MIME::Types" => 0,
53 "Module::Runtime" => 0,
5454 "Moo" => "1.003000",
5555 "Moo::Role" => 0,
5656 "MooX::Types::MooseLike" => "0.16",
6060 "Pod::Simple::Search" => 0,
6161 "Pod::Simple::SimpleTree" => 0,
6262 "Pod::Usage" => 0,
63 "Role::Tiny" => "1.003000",
6364 "Scalar::Util" => 0,
6465 "Template" => 0,
6566 "Template::Tiny" => 0,
6970 "URI::Escape" => 0,
7071 "YAML::Any" => 0,
7172 "constant" => 0,
72 "lib" => 0,
7373 "overload" => 0,
7474 "parent" => 0,
7575 "strict" => 0,
8383 "HTTP::Body" => 0,
8484 "HTTP::Request::Common" => 0,
8585 "HTTP::Server::Simple::PSGI" => 0,
86 "IO::Handle" => 0,
87 "IPC::Open3" => 0,
8688 "Test::Fatal" => 0,
8789 "Test::MockTime" => 0,
8890 "Test::More" => "0.92",
89 "Test::Script" => "1.05",
91 "Test::Script" => 0,
9092 "Test::TCP" => "1.13",
9193 "YAML" => 0,
9294 "YAML::Any" => 0,
93 "blib" => 0,
95 "lib" => 0,
9496 "utf8" => 0,
9597 "vars" => 0
9698 },
97 "VERSION" => "0.07",
99 "VERSION" => "0.09",
98100 "test" => {
99101 "TESTS" => "t/*.t t/roles/*.t t/route-pod-coverage/*.t t/template_tiny/*.t"
100102 }
00 # Dancer2
11
2 [![Build Status](https://travis-ci.org/PerlDancer/Dancer2.png?branch=devel)](https://travis-ci.org/PerlDancer/Dancer2)
2 [![Build Status](https://travis-ci.org/PerlDancer/Dancer2.png?branch=master)](https://travis-ci.org/PerlDancer/Dancer2)
33
44 Dancer2 is the new generation lightweight web-framework for Perl.
55
3030 * [Builds status on Travis](https://travis-ci.org/PerlDancer/Dancer2)
3131 * [Our Mailing List](http://list.perldancer.org/cgi-bin/listinfo/dancer-users)
3232 * [Follow us on Twitter](https://twitter.com/perldancer)
33 * [Find us on irc.per.org #dancer](irc://irc.perl.org/#dancer)
33 * [Find us on irc.perl.org #dancer](irc://irc.perl.org/#dancer)
3434 * [The Advent Calendar](http://advent.perldancer.org/)
35 * [Contribution/Git Guide](https://github.com/PerlDancer/Dancer2/blob/master/GitGuide.md)
3536
36 ## Contributing
37 ## Available Plugins
3738
38 This guide has been written to help anyone interested in contributing
39 to the development of Dancer2.
40
41 First of all - thank you for your interest in the project! It's the
42 community of helpful contributors who've helped Dancer grow
43 phenomenally. Without the community we wouldn't be where we are today!
44
45 Please read this guide before contributing to Dancer2, to avoid wasted
46 effort and maximizing the chances of your contributions being used.
47
48 There are many ways to contribute to the project. Dancer2 is a young
49 yet active project and any kind of help is very much appreciated!
50
51 ### Documentation
52
53 We value documentation very much, but it's difficult to keep it
54 up-to-date. If you find a typo or an error in the documentation
55 please do let us know - ideally by submitting a patch (pull request)
56 with your fix or suggestion (see
57 [Patch Submission](#environment-and-patch-submission)).
58
59 ### Code
60
61 You can write extensions (plugins) for Dancer2 extending core
62 functionality or contribute to Dancer2's core code, see
63 [Patch Submission](#environment-and-patch-submission) below.
64
65 ## General Development Guidelines
66
67 This section lists high-level recommendations for developing Dancer2,
68 for more detailed guidelines, see [Coding Guidelines](#coding-guidelines)
69 below.
70
71 ### Quality Assurance
72
73 Dancer2 should be able to install for all Perl versions since 5.8, on
74 any platform for which Perl exists. We focus mainly on GNU/Linux (any
75 distribution), *BSD and Windows (native and Cygwin).
76
77 We should avoid regressions as much as possible and keep backwards
78 compatibility in mind when refactoring. Stable releases should not
79 break functionality and new releases should provide an upgrade path
80 and upgrade tips such as warning the user about deprecated
81 functionality.
82
83 ### Quality Supervision
84
85 We can measure our quality using the
86 [CPAN testers platform](http://www.cpantesters.org).
87
88 A good way to help the project is to find a failing build log on the
89 [CPAN testers](http://www.cpantesters.org/distro/D/Dancer2.html).
90
91 If you find a failing test report, feel free to report it as a
92 [GitHub issue](http://github.com/PerlDancer/Dancer2/issues).
93
94 ### Reporting Bugs
95
96 We prefer to have all our bug reports on GitHub, in the
97 [issues section](http://github.com/PerlDancer/Dancer2/issues).
98
99 Please make sure the bug you're reporting does not yet exist. In doubt
100 please ask on IRC.
101
102 ## Environment and Patch Submission
103
104 ### Set up a development environment
105
106 If you want to submit a patch for Dancer2, you need git and very
107 likely also [_Dist::Zilla_](https://metacpan.org/module/Dist::Zilla).
108 We also recommend perlbrew (see below) or,
109 alternatively, [_App::Plenv_](https://github.com/tokuhirom/plenv))
110 to test and develop Dancer2 on a recent
111 version of perl. We also suggest
112 [_App::cpanminus_](https://metacpan.org/module/App::cpanminus)
113 to quickly and comfortably install perl modules.
114
115 In the following sections we provide tips for the installation of some
116 of these tools together with Dancer. Please also see the documentation
117 that comes with these tools for more info.
118
119 #### Perlbrew tips (Optional)
120
121 Install perlbrew for example with
122
123 $ cpanm App::perlbrew
124
125 Check which perls are available
126
127 $ perlbrew available
128
129 It should list the available perl versions, like this (incomplete) list:
130
131 perl-5.17.1
132 perl-5.16.0
133 perl-5.14.2
134 perl-5.12.4
135 ...
136
137 Then go on and install a version inside Perlbrew. We recommend you
138 give a name to the installation (`--as` option), as well as compiling
139 without the tests (`--n` option) to speed it up.
140
141 $ perlbrew install -n perl-5.14.2 --as dancer_development -j 3
142
143 Wait a while, and it should be done. Switch to your new Perl with:
144
145 $ perlbrew switch dancer_development
146
147 Now you are using the fresh Perl, you can check it with:
148
149 $ which perl
150
151 Install cpanm on your brewed version of perl.
152
153 $ perlbrew install-cpanm
39 | Name | CPAN | GitHub |
40 |------|-------|--------|
41 | Dancer2::Session::Cookie | [Link](https://metacpan.org/module/Dancer2::Session::Cookie) | [Link](https://github.com/dagolden/dancer2-session-cookie) |
42 | Dancer2::Plugin::Syntax::GetPost | [Link](https://metacpan.org/module/Dancer2::Plugin::Syntax::GetPost) | [Link](https://github.com/dagolden/dancer2-plugin-syntax-getpost) |
43 | Dancer2::Plugin::BrowserDetect | [Link](https://metacpan.org/module/Dancer2::Plugin::BrowserDetect) | |
44 | Dancer2::Plugin::RoutePodCoverage | [Link](https://metacpan.org/module/Dancer2::Plugin::RoutePodCoverage) | [Link](https://github.com/drebolo/Dancer2-Plugin-RoutePodCoverage) |
45 | Dancer2::Plugin::Auth::Tiny | [Link](https://metacpan.org/module/Dancer2::Plugin::Auth::Tiny) | [Link](https://metacpan.org/module/Dancer2::Plugin::Auth::Tiny) |
46 | Dancer2::Plugin::Queue::MongoDB | [Link](https://metacpan.org/module/Dancer2::Plugin::Queue::MongoDB) | [Link](https://github.com/dagolden/dancer2-plugin-queue-mongodb) |
47 | Dancer2::Plugin::Paginator | [Link](https://metacpan.org/module/Dancer2::Plugin::Paginator) | [Link](https://github.com/blabos/Dancer2-Plugin-Paginator) |
48 | Dancer2::Plugin::Deferred | [Link](https://metacpan.org/module/Dancer2::Plugin::Deferred) | [Link](https://github.com/dagolden/dancer2-plugin-deferred) |
49 | Dancer2::Plugin::Adapter | [Link](https://metacpan.org/module/Dancer2::Plugin::Adapter) | [Link](https://github.com/dagolden/dancer2-plugin-adapter) |
50 | Dancer2::Plugin::DBIC | [Link](https://metacpan.org/module/Dancer2::Plugin::DBIC) | [Link](https://github.com/ironcamel/Dancer2-Plugin-DBIC) |
51 | Dancer2::Plugin::REST | [Link](https://metacpan.org/module/Dancer2::Plugin::REST) | [Link](https://github.com/yanick/Dancer2-Plugin-REST) |
52 | Dancer2::Plugin::Emailesque | [Link](https://metacpan.org/module/Dancer2::Plugin::Emailesque) | [Link](https://github.com/ambs/Dancer2-Plugin-Emailesque) |
53 | Dancer2::Plugin::Cache::CHI | [Link](https://metacpan.org/module/Dancer2::Plugin::Cache::CHI) | [Link](https://github.com/yanick/Dancer2-Plugin-Cache-CHI) |
54 | Dancer2::Plugin::Queue | [Link](https://metacpan.org/module/Dancer2::Plugin::Queue) | [Link](https://github.com/dagolden/dancer2-plugin-queue) |
55 | Dancer2::Plugin::Database | [Link](https://metacpan.org/module/Dancer2::Plugin::Database) | [Link](https://github.com/bigpresh/Dancer-Plugin-Database/tree/master/Dancer2) |
56 | Dancer2::Plugin::Feed | [Link](https://metacpan.org/module/Dancer2::Plugin::Feed) | [Link](https://github.com/hobbestigrou/Dancer2-Plugin-Feed) |
15457
15558
156 ### Install various dependencies (required)
59 ## Templates engines
15760
158 Install Dist::Zilla
159
160 $ cpanm Dist::Zilla
161
162 ### Get Dancer2 sources
163
164 Get the Dancer sources from github (for a more complete git workflow
165 see below):
166
167 Clone your fork to have a local copy using the following command:
168
169 $ git clone git://github.com/perldancer/Dancer2.git
170
171 The Dancer2 sources come with a `dist.ini`. That's the configuration
172 files for _Dist::Zilla_, so that it knows how to build Dancer2. Let's
173 use dist.ini to install additional `Dist::Zilla` plugins which are
174 not yet installed on your system (or perl installation):
175
176 $ dzil authordeps | cpanm -n
177
178 That should install a bunch of stuff. Now that _Dist::Zilla_ is up and
179 running, you should install the dependencies required by Dancer2:
180
181 $ dzil listdeps | cpanm -n
182
183 When that is done, you're good to go! You can use dzil to build and test Dancer2:
184
185 $ dzil build
186 $ dzil test
187
188
189 ### Patch Submission (Github workflow)
190
191 The Dancer2 development team uses GitHub to collaborate. We greatly
192 appreciate contributions submitted via GitHub, as it makes tracking
193 these contributions and applying them much, much easier. This gives
194 your contribution a much better chance of being integrated into
195 Dancer2 quickly!
196
197 To help us achieve high-quality, stable releases, git-flow workflow is
198 used to handle pull-requests, that means contributors must work on
199 their _devel_ branch rather than on their _master_. (Master should
200 be touched only by the core dev team when preparing a release to CPAN;
201 all ongoing development happens in branches which are merged to the
202 _devel_ branch.)
203
204 Here is the workflow for submitting a patch:
205
206 1. Fork the repository: http://github.com/PerlDancer/Dancer2 and click "Fork";
207
208 2. Clone your fork to have a local copy using the following command:
209
210 $ git clone git://github.com/myname/Dancer2.git
211
212 3. As a contributor, you should **always** work on the `devel` branch of
213 your clone (`master` is used only for building releases).
214
215 $ git remote add upstream https://github.com/PerlDancer/Dancer2.git
216 $ git fetch upstream
217 $ git checkout -b devel upstream/devel
218
219 This will create a local branch in your clone named _devel_ and
220 that will track the official _devel_ branch. That way, if you have
221 more or less commits than the upstream repo, you'll be immediately
222 notified by git.
223
224 4. You want to isolate all your commits in a _topic_ branch, this
225 will make the reviewing much easier for the core team and will
226 allow you to continue working on your clone without worrying about
227 different commits mixing together.
228
229 To do that, first create a local branch to build your pull request:
230
231 # you should be in devel here
232 $ git checkout -b pr/$name
233
234 Now you have created a local branch named _pr/$name_ where _$name_
235 is the name you want (it should describe the purpose of the pull
236 request you're preparing).
237
238 In that branch, do all the commits you need (the more the better)
239 and when done, push the branch to your fork:
240
241 # ... commits ...
242 git push origin pr/$name
243
244 You are now ready to send a pull request.
245
246 5. Send a _pull request_ via the GitHub interface. Make sure your pull
247 request is based on the _pr/$name_ branch you've just pushed, so
248 that it incorporates the appropriate commits only.
249
250 It's also a good idea to summarize your work in a report sent to
251 the users mailing list (see below), in order to make sure the team
252 is aware of it.
253
254 You could also notify the core team on IRC, on `irc.perl.org`,
255 channel `#dancer` or via [web client](http://www.perldancer.org/irc).
256
257 6. When the core team reviews your pull request, it will either accept
258 (and then merge into _devel_) or refuse your request.
259
260 If it's refused, try to understand the reasons explained by the
261 team for the denial. Most of the time, communicating with the core
262 team is enough to understand what the mistake was. Above all,
263 please don't be offended.
264
265 If your pull-request is merged into _devel_, then all you have to
266 do is to remove your local and remote _pr/$name_ branch:
267
268 $ git checkout devel
269 $ git branch -D pr/$name
270 $ git push origin :pr/$name
271
272 And then, of course, you need to sync your local devel branch with
273 the upstream:
274
275 $ git pull upstream devel
276 $ git push origin devel
277
278 You're now ready to start working on a new pull request!
279
280 ## Plugins
281
282 | Name | Type | Link |
283 | ------------- |-------------| ------|
284 | Dancer2::Session::Cookie | Session | [CPAN](https://metacpan.org/module/Dancer2::Session::Cookie) |
285 | Dancer2::Plugin::Syntax::GetPost | Syntactic sugar | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Syntax::GetPost) |
286 | Dancer2::Plugin::BrowserDetect | | [CPAN](https://metacpan.org/module/Dancer2::Plugin::BrowserDetect) |
287 | Dancer2::Plugin::RoutePodCoverage | Test | [CPAN](https://metacpan.org/module/Dancer2::Plugin::RoutePodCoverage) |
288 | Dancer2::Plugin::Auth::Tiny | Auth | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Auth::Tiny) |
289 | Dancer2::Plugin::Queue::MongoDB | Queue | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Queue::MongoDB) |
290 | Dancer2::Plugin::Paginator | Pagination | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Paginator) |
291 | Dancer2::Plugin::Deferred | Flash Message | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Deferred) |
292 | Dancer2::Plugin::Adapter | Class Wrapper | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Adapter) |
293 | Dancer2::Plugin::DBIC | Database | [CPAN](https://metacpan.org/module/Dancer2::Plugin::DBIC) |
294 | Dancer2::Plugin::REST | API | [CPAN](https://metacpan.org/module/Dancer2::Plugin::REST) |
295 | Dancer2::Plugin::Emailesque | Email | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Emailesque) |
296 | Dancer2::Plugin::Cache::CHI | Cache | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Cache::CHI) |
297 | Dancer2::Plugin::Queue | Queue | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Queue) |
298 | Dancer2::Plugin::Database | Database | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Database) |
299 | Dancer2::Plugin::Feed | Feed | [CPAN](https://metacpan.org/module/Dancer2::Plugin::Feed) |
61 | Name | CPAN | GitHub |
62 |------|------|--------|
63 | Dancer2::Template::Xslate | [Link](https://metacpan.org/module/Dancer2::Template::Xslate) | [Link](https://github.com/rsimoes/Dancer2-Template-Xslate) |
64 | Dancer2::Template::MojoTemplate | [Link](https://metacpan.org/module/Dancer2::Template::MojoTemplate) | [Link](https://github.com/VeroLom/Dancer2-Template-MojoTemplate) |
65 | Dancer2::Template::Caribou | [Link](https://metacpan.org/module/Dancer2::Template::Caribou) | [Link](https://github.com/yanick/Dancer2-Template-Caribou) |
30066
30167
30268 ## License
1010
1111 =head1 VERSION
1212
13 version 0.07
13 version 0.09
1414
1515 =head1 DESCRIPTION
1616
344344
345345 =back
346346
347 =head3 auto_reload (boolean)
348
349 Requires L<Module::Refresh> and L<Clone>.
350
351 If set to true, Dancer2 will reload the route handlers whenever the file where
352 they are defined is changed. This is very useful in development environment but
353 B<should not be enabled in production>. Enabling this flag in production yields
354 a major negative effect on performance because of L<Module::Refresh>.
355
356 When this flag is set, you don't have to restart your webserver whenever you
357 make a change in a route handler.
358
359 Note that L<Module::Refresh> only operates on files in C<%INC>, so if the script
360 your Dancer2 app is started from changes, even with auto_reload enabled, you will
361 still not see the changes reflected until you start your app.
362
363347 =head2 Logger engine
364348
365349 The logger must be configured in a separate C<engines> section, like so:
1010
1111 =head1 VERSION
1212
13 version 0.07
13 version 0.09
1414
1515 =head1 DESCRIPTION
1616
1717 A quick-start guide with examples to get you up and running with the Dancer2 web
1818 framework.
19
20 =encoding utf8
1921
2022 =head1 BEGINNER'S DANCE
2123
5456 =head2 Starting a Dancer2 project
5557
5658 The first simple example is fine for trivial projects, but for anything more
57 complex, you'll want a more maintainable solution - enter the C<dancer> helper
59 complex, you'll want a more maintainable solution - enter the C<dancer2> helper
5860 script, which will build the framework of your application with a single
5961 command:
6062
61 $ dancer -a mywebapp
63 $ dancer2 -a mywebapp
6264 + mywebapp
65 + mywebapp/bin
66 + mywebapp/bin/app.pl
6367 + mywebapp/config.yml
6468 + mywebapp/environments
6569 + mywebapp/environments/development.yml
6872 + mywebapp/views/index.tt
6973 + mywebapp/views/layouts
7074 + mywebapp/views/layouts/main.tt
71 + mywebapp/mywebapp.pl
75 + mywebapp/MANIFEST.SKIP
7276 + mywebapp/lib
77 mywebapp/lib/
7378 + mywebapp/lib/mywebapp.pm
7479 + mywebapp/public
7580 + mywebapp/public/css
7681 + mywebapp/public/css/style.css
7782 + mywebapp/public/css/error.css
7883 + mywebapp/public/images
84 + mywebapp/public/500.html
7985 + mywebapp/public/404.html
8086 + mywebapp/public/dispatch.fcgi
8187 + mywebapp/public/dispatch.cgi
82 + mywebapp/public/500.html
83 + mywebapp/Makefile.PL
88 + mywebapp/public/javascripts
89 + mywebapp/public/javascripts/jquery.js
8490 + mywebapp/t
8591 + mywebapp/t/002_index_route.t
8692 + mywebapp/t/001_base.t
93 + mywebapp/Makefile.PL
8794
8895 As you can see, it creates a directory named after the name of the app, along
8996 with a configuration file, a views directory (where your templates and layouts
169176 a request is passed to the appropriate route.
170177
171178 hook before => sub {
172 var note => 'Hi there';
173 request->path('/foo/oversee')
179 forward '/foo/oversee', { note => 'Hi there' };
174180 };
175181
176182 get '/foo/*' => sub {
177183 my ($match) = splat; # 'oversee';
178 vars->{note}; # 'Hi there'
179 };
180
181 The above declares a before filter which uses C<var> to set a variable which
182 will later be available within the route handler, then amends the path of the
183 request to C</foo/oversee>; this means that, whatever path was requested, it
184 will be treated as though the path requested was C</foo/oversee>.
184 params->{note}; # 'Hi there'
185 };
186
187 The above declares a before filter which uses C<forward> to do an internal
188 redirect to C</foo/oversee> with an additional paramater C<note>.
185189
186190 See also the L<hook|Dancer2/hook> hook keyword.
187191
263267 admin section, and want to maintain this in a different package:
264268
265269 package myapp;
266 use Dancer2 ':syntax';
270 use Dancer2;
267271 use myapp::admin;
268272
269273 prefix undef;
273277 1;
274278
275279 package myapp::admin;
276 use Dancer2 ':syntax';
280 use Dancer2;
277281
278282 prefix '/admin';
279283
364368
365369 When you're done with your session, you can destroy it:
366370
367 session->destroy
371 context->destroy_session
368372
369373 =head2 Sessions and logging in
370374
374378 This can easily be handled with a before filter to check their session:
375379
376380 hook before => sub {
377
378 if (! session('user') && request->path_info !~ m{^/login}) {
379 var requested_path => request->path_info;
380 request->path_info('/login');
381 if (! session('user') && request->dispatch_path !~ m{^/login}) {
382 forward '/login', { requested_path => request->dispatch_path };
381383 }
382384 };
383385
384386 get '/login' => sub {
385387 # Display a login page; the original URL they requested is available as
386 # vars->{requested_path}, so could be put in a hidden field in the form
387 template 'login', { path => vars->{requested_path} };
388 # params->{'requested_path'}, so could be put in a hidden field in the form
389 template 'login', { path => params->{'requested_path'} };
388390 };
389391
390392 post '/login' => sub {
657659 Use Dancer2 instead. Without any ado, magic or too big jumps, you can use the
658660 values from config.yml and some additional default values:
659661
660 # bin/script1.pl
661 use Dancer2 ':syntax';
662 print "template:".config->{template}."\n"; #simple
663 print "log:".config->{log}."\n"; #undef
662 # bin/script1.pl
663 use Dancer2;
664 print "template:".config->{template}."\n"; #simple
665 print "log:".config->{log}."\n"; #undef
664666
665667 Note that config->{log} should result undef error on a default scaffold since
666668 you did not load the environment and in the default scaffold log is defined in
670672 One way to do so, is to tell Dancer2 where the webapp lives. From there Dancer2
671673 deducts where the config.yml file is (typically $webapp/config.yml).
672674
673 # bin/script2.pl
674 use FindBin;
675 use Cwd qw/realpath/;
676 use Dancer2 ':syntax';
677
678 #tell the Dancer2 where the app lives
679 my $appdir=realpath( "$FindBin::Bin/..");
680
681 Dancer2::Config::setting('appdir',$appdir);
682 Dancer2::Config::load();
683
684 #getter
685 print "environment:".config->{environment}."\n"; #development
686 print "log:".config->{log}."\n"; #value from development environment
675 # bin/script2.pl
676 use FindBin;
677 use Cwd qw/realpath/;
678 use Dancer2;
679
680 #tell the Dancer2 where the app lives
681 my $appdir=realpath( "$FindBin::Bin/..");
682
683 Dancer2::Config::setting('appdir',$appdir);
684 Dancer2::Config::load();
685
686 #getter
687 print "environment:".config->{environment}."\n"; #development
688 print "log:".config->{log}."\n"; #value from development environment
687689
688690 By default Dancer2 loads development environment (typically
689691 $webapp/environment/development.yml). In contrast to the example before, you
693695 could just as well hand over a simple path for the app if you like that better,
694696 e.g.:
695697
696 Dancer2::Config::setting('appdir','/path/to/app/dir');
698 Dancer2::Config::setting('appdir','/path/to/app/dir');
697699
698700 If you want to load an environment other than the default, try this:
699701
700 # bin/script2.pl
701 use Dancer2 ':syntax';
702
703 #tell the Dancer2 where the app lives
704 Dancer2::Config::setting('appdir','/path/to/app/dir');
705
706 #which environment to load
707 config->{environment}='production';
708
709 Dancer2::Config::load();
710
711 #getter
712 print "log:".config->{log}."\n"; #has value from production environment
702 # bin/script2.pl
703 use Dancer2;
704
705 #tell the Dancer2 where the app lives
706 Dancer2::Config::setting('appdir','/path/to/app/dir');
707
708 #which environment to load
709 config->{environment}='production';
710
711 Dancer2::Config::load();
712
713 #getter
714 print "log:".config->{log}."\n"; #has value from production environment
713715
714716 By the way, you not only get values, you can also set values straightforward
715717 like we do above with config->{environment}='production'. Of course, this value
785787
786788 get '/hello/:name' => sub {
787789 # this structure will be returned to the client as
788 # {"name":"$name"}
790 # {"name":"$name"}
789791 return {name => params->{name}};
790792 };
791793
809811
810812 The content of the error will be serialized using the appropriate serializer.
811813
812 =head2 Deploying your Dancer2 applications
813
814 For examples on deploying your Dancer2 applications (including standalone, behind
815 proxy/load-balancing software, and using common web servers including Apache to
816 run via CGI/FastCGI etc, see L<Dancer2::Deployment>.
817
818814 =head1 DANCER ON THE STAGE: DEPLOYMENT
819815
820816 =head2 Running stand-alone
832828
833829 This option can be useful for small personal web apps or internal apps, but if
834830 you want to make your app available to the world, it probably won't suit you.
831
832 =head2 Auto Reloading with Plack and Shotgun
833
834 To edit your files without the need to restart the webserver on each file change,
835 simply start your Dancer2 app using L<plackup> and L<Plack::Loader::Shotgun>:
836
837 $ plackup -L Shotgun bin/app.pl
838 HTTP::Server::PSGI: Accepting connections at http://0:5000/
839
840 Point your browser at it. Files can now be changed in your favorite editor and
841 the browser needs to be refreshed to see the saved changes.
842
843 Please note that this is not recommended for production for performance reasons. This is the Dancer2 replacement solution of the old Dancer experimental C<auto_reload> option.
835844
836845 =head2 CGI and Fast-CGI
837846
894903
895904 You can use the same technique to deploy with FastCGI, by just changing the line:
896905
897 AddHandler cgi-script .cgi
906 AddHandler cgi-script .cgi
898907
899908 By:
900909
901 AddHandler fastcgi-script .fcgi
910 AddHandler fastcgi-script .fcgi
902911
903912 Of course remember to update your rewrite rules, if you have set any:
904913
987996
988997 =head2 Plack middlewares
989998
990 If you deploy with Plack and use some Plack middlewares, you can enable them
991 directly from Dancer2's configuration files.
992
993 =head3 Generic middlewares
994
995 To enable middlewares in Dancer2, you just have to set the plack_middlewares
996 setting like the following:
997
998 set plack_middlewares => [
999 [ 'SomeMiddleware' => [ qw(some options for somemiddleware) ]],
1000 ];
1001
1002 For instance, if you want to enable L<Plack::Middleware::Debug> in your Dancer2
1003 application, all you have to do is to set C<plack_middlewares> like that:
1004
1005 set plack_middlewares => [
1006 [ 'Debug' => [ 'panels' => [qw(DBITrace Memory Timer)] ] ],
1007 ];
1008
1009 Of course, you can also put this configuration into your config file, or
1010 even in your environment configuration files:
1011
1012 # environments/development.yml
1013 ...
1014 plack_middlewares:
1015 -
1016 - Debug # first element of the array is the name of the middleware
1017 -
1018 - panels # following elements are the configuration ofthe middleware
1019 -
1020 - DBITrace
1021 - Memory
1022 - Timer
999 If you want to use Plack middlewares, you need to enable them using
1000 L<Plack::Builder> as such:
1001
1002 # in app.psgi or any other handler
1003 use Dancer2;
1004 use MyWebApp;
1005 use Plack::Builder;
1006
1007 builder {
1008 enable 'Deflator';
1009 enable 'Session', store => 'File';
1010 enable 'Debug', panels => [ qw<DBITrace Memory Timer> ];
1011 dance;
1012 };
1013
1014 The nice thing about this setup is that it will work seamlessly through Plack
1015 or through the internal web server.
1016
1017 # load dev web server (without middlewares)
1018 perl -Ilib app.psgi
1019
1020 # load plack web server (with middlewares)
1021 plackup -I lib app.psgi
1022
1023 You do not need to provide different files for either server.
10231024
10241025 =head3 Path-based middlewares
10251026
10261027 If you want to setup a middleware for a specific path, you can do that using
1027 C<plack_middlewares_map>. You'll need L<Plack::App::URLMap> to do that.
1028
1029 plack_middlewares_map:
1030 '/': ['Debug']
1031 '/timer': ['Timer'],
1028 L<Plack::Builder> which uses L<Plack::App::URLMap>:
1029
1030 # in your app.psgi or any other handler
1031 use Dancer2;
1032 use MyWebApp;
1033 use Plack::Builder;
1034
1035 my $special_handler = sub { ... };
1036
1037 builder {
1038 mount '/' => dance,
1039 mount '/special' => $special_handler,
1040 };
10321041
10331042 =head3 Running on Perl webservers with plackup
10341043
10671076 Accept-Encoding HTTP request header.
10681077
10691078 Enable it as you would enable any Plack middleware. First you need to install
1070 L<Plack::Middleware::Deflater>, then in the configuration file (usually
1071 F<environments/development.yml>), add these lines:
1072
1073 plack_middlewares:
1074 -
1075 - Plack::Middleware::Deflater
1076 - ...
1077
1078 These lines tell Dancer2 to add L<Plack::Middleware::Deflater> to the list of
1079 middlewares to pass to L<Plack::Builder>, when wrapping the Dancer2 app. The
1080 syntax is :
1081
1082 =over
1083
1084 =item *
1085
1086 as key: the name of the Plack middleware to use
1087
1088 =item *
1089
1090 as value: the options to pass it as a list. In our case, there is no option to
1091 specify to L<Plack::Middleware::Deflater>, so we use an empty YAML list.
1092
1093 =back
1079 L<Plack::Middleware::Deflater>, then in the handler (usually F<app.psgi>) edit
1080 it to use L<Plack::Builder>, as described above:
1081
1082 use Dancer2;
1083 use MyWebApp;
1084 use Plack::Builder;
1085
1086 builder {
1087 enable 'Deflator';
1088 dance;
1089 };
10941090
10951091 To test if content compression works, trace the HTTP request and response
10961092 before and after enabling this middleware. Among other things, you should
10991095
11001096 =head3 Running multiple apps with Plack::Builder
11011097
1102 You can use Plack::Builder to mount multiple Dancer2 applications on
1098 You can use L<Plack::Builder> to mount multiple Dancer2 applications on
11031099 a L<PSGI> webserver like L<Starman>.
11041100
11051101 Start by creating a simple app.psgi file:
11061102
1107 use Dancer2 ':syntax';
1103 use OurWiki; # first app
1104 use OurForum; # second app
11081105 use Plack::Builder;
11091106
1110 setting apphandler => 'PSGI';
1111
1112 my $app1 = sub {
1113 my $env = shift;
1114 local $ENV{DANCER_APPDIR} = '/Users/franck/tmp/app1';
1115 load_app "app1";
1116 Dancer2::App->set_running_app('app1');
1117 setting appdir => '/Users/franck/tmp/app1';
1118 Dancer2::Config->load;
1119 my $request = Dancer2::Request->new( env => $env );
1120 Dancer2->dance($request);
1121 };
1122
1123 my $app2 = sub {
1124 my $env = shift;
1125 local $ENV{DANCER_APPDIR} = '/Users/franck/tmp/app2';
1126 load_app "app2";
1127 Dancer2::App->set_running_app('app2');
1128 setting appdir => '/Users/franck/tmp/app2';
1129 Dancer2::Config->load;
1130 my $request = Dancer2::Request->new( env => $env );
1131 Dancer2->dance($request);
1132 };
1133
11341107 builder {
1135 mount "/app1" => builder {$app1};
1136 mount "/app2" => builder {$app2};
1108 mount '/wiki' => OurWiki->dance,
1109 mount '/forum' => OurForum->dance,
11371110 };
11381111
11391112 and now use L<Starman>
11401113
11411114 plackup -a app.psgi -s Starman
1115
1116 Currently this still demands the same appdir for both (default circumstance)
1117 but in a future version this will be easier to change while staying very
1118 simple to mount.
11421119
11431120 =head3 Running from Apache with Plack
11441121
00 # ABSTRACT: encapsulation of Dancer2 packages
1
21 package Dancer2::Core::App;
32 {
4 $Dancer2::Core::App::VERSION = '0.07';
5 }
6
7
8 use strict;
9 use warnings;
3 $Dancer2::Core::App::VERSION = '0.09';
4 }
105
116 use Moo;
127 use File::Spec;
149 use Carp 'croak';
1510
1611 use Dancer2::FileUtils 'path', 'read_file_content';
12 use Dancer2::Core;
1713 use Dancer2::Core::Types;
1814 use Dancer2::Core::Route;
1915 use Dancer2::Core::Hook;
2218 with 'Dancer2::Core::Role::Hookable';
2319 with 'Dancer2::Core::Role::Config';
2420
25 sub supported_hooks {
26 qw/
27 core.app.before_request
28 core.app.after_request
29 core.app.route_exception
30 core.error.before
31 core.error.after
32 core.error.init
33 /;
34 }
35
21 has postponed_hooks => (
22 is => 'ro',
23 isa => HashRef,
24 default => sub { {} },
25 );
3626
3727 has plugins => (
3828 is => 'rw',
4030 default => sub { [] },
4131 );
4232
43 # FIXME not needed anymore, I suppose...
44 sub api_version {2}
45
46
47 sub register_plugin {
48 my ( $self, $plugin ) = @_;
49 Dancer2::core_debug("Registered $plugin");
50 push @{ $self->plugins }, $plugin;
51 }
52
53 around BUILDARGS => sub {
54 my $orig = shift;
55 my ( $class, %args ) = @_;
56 $args{postponed_hooks} ||= {};
57 return $class->$orig(%args);
58 };
59
60
6133 has server => (
6234 is => 'rw',
6335 isa => ConsumerOf ['Dancer2::Core::Role::Server'],
6436 weak_ref => 1,
6537 );
6638
67
6839 has runner_config => (
6940 is => 'ro',
7041 isa => HashRef,
7142 default => sub { {} },
7243 );
73
7444
7545 has default_config => (
7646 is => 'ro',
7949 builder => '_build_default_config',
8050 );
8151
82 sub _build_default_config {
83 my ($self) = @_;
84
85 return {
86 %{ $self->runner_config },
87 template => 'Tiny',
88 route_handlers => {
89 File => {
90 public_dir => $ENV{DANCER_PUBLIC}
91 || path( $self->location, 'public' )
92 },
93 AutoPage => 1,
94 },
95 };
96 }
97
98 # This method overrides the default one from Role::Config
99
100 sub settings {
101 my ($self) = @_;
102 +{ %{ Dancer2->runner->config }, %{ $self->config } };
103 }
104
105 sub engine {
106 my ( $self, $name ) = @_;
107
108 my $e = $self->engines->{$name}
109 || croak "No '$name' engine defined";
110
111 return $e;
112 }
113
114 sub session {
115 my ( $self, $key, $value ) = @_;
116
117 # shortcut reads if no session exists, so we don't
118 # instantiate sessions for no reason
119 if ( @_ == 2 ) {
120 return unless $self->context->has_session;
121 }
122
123 my $session = $self->context->session;
124 croak "No session available, a session engine needs to be set"
125 if !defined $session;
126
127 # return the session object if no key
128 return $session if @_ == 1;
129
130 # read if a key is provided
131 return $session->read($key) if @_ == 2;
132
133 # write to the session or delete if value is undef
134 if ( defined $value ) {
135 $session->write( $key => $value );
136 }
137 else {
138 $session->delete($key);
139 }
140 }
141
142 sub template {
143 my ($self) = shift;
144 my $template = $self->engines->{'template'};
145
146 my $content = $template->process(@_);
147
148 return $content;
149 }
150
151 sub hook_candidates {
152 my ($self) = @_;
153
154 my @engines;
155 for my $e ( @{ $self->supported_engines } ) {
156 my $engine = eval { $self->engine($e) };
157 push @engines, $engine if defined $engine;
158 }
159
160 my @route_handlers;
161 for my $handler_name ( keys %{ $self->route_handlers } ) {
162 my $handler = $self->route_handlers->{$handler_name};
163 push @route_handlers, $handler
164 if blessed($handler) && $handler->can('supported_hooks');
165 }
166
167 # TODO : get the list of all plugins registered
168 my @plugins = @{ $self->plugins };
169
170 ( @route_handlers, @engines, @plugins );
171 }
172
173 sub all_hook_aliases {
174 my ($self) = @_;
175
176 my $aliases = $self->hook_aliases;
177 for my $plugin ( @{ $self->plugins } ) {
178 $aliases = { %{$aliases}, %{ $plugin->hook_aliases }, };
179 }
180
181 return $aliases;
182 }
183
184 has postponed_hooks => (
185 is => 'ro',
52 has route_handlers => (
53 is => 'rw',
54 isa => ArrayRef,
55 default => sub { [] },
56 );
57
58 has name => (
59 is => 'ro',
60 isa => Str,
61 );
62
63 # holds a context whenever a request is processed
64 has context => (
65 is => 'rw',
66 isa => Maybe [ InstanceOf ['Dancer2::Core::Context'] ],
67 trigger => sub {
68 my ( $self, $ctx ) = @_;
69 $self->_init_for_context($ctx),;
70 for my $type ( @{ $self->supported_engines } ) {
71 my $engine = $self->engine($type) or next;
72 defined($ctx) ? $engine->context($ctx) : $engine->clear_context;
73 }
74 },
75 );
76
77 has prefix => (
78 is => 'rw',
79 isa => Maybe [Dancer2Prefix],
80 predicate => 1,
81 coerce => sub {
82 my ($prefix) = @_;
83 return undef if defined($prefix) and $prefix eq "/";
84 return $prefix;
85 },
86 );
87
88 # routes registry, stored by method:
89 has routes => (
90 is => 'rw',
18691 isa => HashRef,
187 default => sub { {} },
92 default => sub {
93 { get => [],
94 head => [],
95 post => [],
96 put => [],
97 del => [],
98 options => [],
99 };
100 },
188101 );
189102
190103 # add_hook will add the hook to the first "hook candidate" it finds that support
252165 return $self->$orig(@_);
253166 };
254167
255 sub mime_type {
256 my ($self) = @_;
257 my $runner = Dancer2->runner;
258
259 if ( exists( $self->config->{default_mime_type} ) ) {
260 $runner->mime_type->default( $self->config->{default_mime_type} );
261 }
262 else {
263 $runner->mime_type->reset_default;
264 }
265 $runner->mime_type;
266 }
267
268 sub log {
269 my $self = shift;
270 my $level = shift;
271
272 my $logger = $self->engine('logger')
273 or croak "No logger defined";
274
275 $logger->$level(@_);
276 }
277
278 # XXX I think this should live on the context or response - but
279 # we don't currently have backwards links - weak_ref should make
280 # those completely doable.
281 # -- mst
282
283 sub send_file {
284 my ( $self, $path, %options ) = @_;
285 my $env = $self->context->env;
286
287 ( $options{'streaming'} && !$env->{'psgi.streaming'} )
288 and croak "Streaming is not supported on this server.";
289
290 ( exists $options{'content_type'} )
291 and $self->context->response->header(
292 'Content-Type' => $options{content_type} );
293
294 ( exists $options{filename} )
295 and $self->context->response->header( 'Content-Disposition' =>
296 "attachment; filename=\"$options{filename}\"" );
297
298 # if we're given a SCALAR reference, we're going to send the data
299 # pretending it's a file (on-the-fly file sending)
300 ( ref($path) eq 'SCALAR' )
301 and return $$path;
302
303 my $conf = {};
304 $conf->{app} = $self;
305 my $file_handler = Dancer2::Core::Factory->create(
306 Handler => 'File',
307 %$conf,
308 postponed_hooks => $self->postponed_hooks,
309 public_dir => ( $options{system_path} ? File::Spec->rootdir : undef ),
310 );
311
312 if ( $self->route_handlers->{File} ) {
313 for my $h ( keys %{ $self->route_handlers->{File}->hooks } ) {
314 my $hooks = $self->route_handlers->{File}->hooks->{$h};
315 $file_handler->replace_hook( $h, $hooks );
316 }
317 }
318
319 $self->context->request->path_info($path);
320 return $file_handler->code->( $self->context, $self->prefix );
321
322 # TODO Streaming support
323 }
324
325
326 sub BUILD {
327 my ($self) = @_;
328 $self->init_route_handlers();
329 $self->_init_hooks();
168 sub _build_default_config {
169 my ($self) = @_;
170
171 return {
172 %{ $self->runner_config },
173 template => 'Tiny',
174 route_handlers => [
175 [ File => {
176 public_dir => $ENV{DANCER_PUBLIC}
177 || path( $self->location, 'public' )
178 }
179 ],
180 [ AutoPage => 1 ],
181 ],
182 };
330183 }
331184
332185 sub _init_hooks {
374227 );
375228 }
376229
230 sub _init_for_context {
231 my ($self) = @_;
232
233 return if !defined $self->context;
234 return if !defined $self->context->request;
235
236 $self->context->request->is_behind_proxy(1)
237 if $self->setting('behind_proxy');
238 }
239
240 sub supported_hooks {
241 qw/
242 core.app.before_request
243 core.app.after_request
244 core.app.route_exception
245 core.error.before
246 core.error.after
247 core.error.init
248 /;
249 }
250
251 # FIXME not needed anymore, I suppose...
252 sub api_version {2}
253
254 sub register_plugin {
255 my ( $self, $plugin ) = @_;
256 Dancer2::Core::debug("Registered $plugin");
257 push @{ $self->plugins }, $plugin;
258 }
259
260 # This method overrides the default one from Role::Config
261 sub settings {
262 my ($self) = @_;
263 +{ %{ Dancer2->runner->config }, %{ $self->config } };
264 }
265
266 sub engine {
267 my ( $self, $name ) = @_;
268
269 croak "Engine '$name' is not supported."
270 if !grep { $_ eq $name } @{ $self->supported_engines };
271
272 return $self->engines->{$name};
273 }
274
275 sub session {
276 my ( $self, $key, $value ) = @_;
277
278 # shortcut reads if no session exists, so we don't
279 # instantiate sessions for no reason
280 if ( @_ == 2 ) {
281 return unless $self->context->has_session;
282 }
283
284 my $session = $self->context->session;
285 croak "No session available, a session engine needs to be set"
286 if !defined $session;
287
288 # return the session object if no key
289 return $session if @_ == 1;
290
291 # read if a key is provided
292 return $session->read($key) if @_ == 2;
293
294 # write to the session or delete if value is undef
295 if ( defined $value ) {
296 $session->write( $key => $value );
297 }
298 else {
299 $session->delete($key);
300 }
301 }
302
303 sub template {
304 my ($self) = shift;
305 my $template = $self->engine('template');
306
307 my $content = $template->process(@_);
308
309 return $content;
310 }
311
312 sub hook_candidates {
313 my ($self) = @_;
314
315 my @engines;
316 for my $e ( @{ $self->supported_engines } ) {
317 my $engine = $self->engine($e) or next;
318 push @engines, $engine;
319 }
320
321 my @route_handlers;
322 for my $handler ( @{ $self->route_handlers } ) {
323 my $handler_code = $handler->{handler};
324 push @route_handlers, $handler_code
325 if blessed($handler_code) && $handler_code->can('supported_hooks');
326 }
327
328 # TODO : get the list of all plugins registered
329 my @plugins = @{ $self->plugins };
330
331 ( @route_handlers, @engines, @plugins );
332 }
333
334 sub all_hook_aliases {
335 my ($self) = @_;
336
337 my $aliases = $self->hook_aliases;
338 for my $plugin ( @{ $self->plugins } ) {
339 $aliases = { %{$aliases}, %{ $plugin->hook_aliases }, };
340 }
341
342 return $aliases;
343 }
344
345 sub mime_type {
346 my ($self) = @_;
347 my $runner = Dancer2->runner;
348
349 if ( exists( $self->config->{default_mime_type} ) ) {
350 $runner->mime_type->default( $self->config->{default_mime_type} );
351 }
352 else {
353 $runner->mime_type->reset_default;
354 }
355 $runner->mime_type;
356 }
357
358 sub log {
359 my ( $self, $level, @args ) = @_;
360
361 my $logger = $self->engine('logger')
362 or croak "No logger defined";
363
364 $logger->$level(@args);
365 }
366
367 # XXX I think this should live on the context or response - but
368 # we don't currently have backwards links - weak_ref should make
369 # those completely doable.
370 # -- mst
371
372 sub send_file {
373 my ( $self, $path, %options ) = @_;
374 my $env = $self->context->env;
375
376 ( $options{'streaming'} && !$env->{'psgi.streaming'} )
377 and croak "Streaming is not supported on this server.";
378
379 ( exists $options{'content_type'} )
380 and $self->context->response->header(
381 'Content-Type' => $options{content_type} );
382
383 ( exists $options{filename} )
384 and $self->context->response->header( 'Content-Disposition' =>
385 "attachment; filename=\"$options{filename}\"" );
386
387 # if we're given a SCALAR reference, we're going to send the data
388 # pretending it's a file (on-the-fly file sending)
389 ( ref($path) eq 'SCALAR' )
390 and return $$path;
391
392 my $conf = {};
393 $conf->{app} = $self;
394 my $file_handler = Dancer2::Core::Factory->create(
395 Handler => 'File',
396 %$conf,
397 postponed_hooks => $self->postponed_hooks,
398 public_dir => ( $options{system_path} ? File::Spec->rootdir : undef ),
399 );
400
401 # List shouldn't be too long, so we use 'grep' instead of 'first'
402 if ( my ($handler) =
403 grep { $_->{name} eq 'File' } @{ $self->route_handlers } )
404 {
405 for my $h ( keys %{ $handler->{handler}->hooks } ) {
406 my $hooks = $handler->{handler}->hooks->{$h};
407 $file_handler->replace_hook( $h, $hooks );
408 }
409 }
410
411 $self->context->request->path_info($path);
412 return $file_handler->code->( $self->context, $self->prefix );
413
414 # TODO Streaming support
415 }
416
417
418 sub BUILD {
419 my ($self) = @_;
420 $self->init_route_handlers();
421 $self->_init_hooks();
422 }
423
377424 sub finish {
378425 my ($self) = @_;
379426 $self->register_route_handlers;
380427 $self->compile_hooks;
381428 }
382429
383 has route_handlers => (
384 is => 'rw',
385 isa => HashRef,
386 default => sub { {} },
387 );
388
389430 sub init_route_handlers {
390431 my ($self) = @_;
391432
392433 my $handlers_config = $self->config->{route_handlers};
393 for my $handler_name ( keys %{$handlers_config} ) {
394 my $config = $handlers_config->{$handler_name};
434 for my $handler_data ( @{$handlers_config} ) {
435 my ( $handler_name, $config ) = @{$handler_data};
395436 $config = {} if !ref($config);
396437 $config->{app} = $self;
438
397439 my $handler = Dancer2::Core::Factory->create(
398440 Handler => $handler_name,
399441 %$config,
400442 postponed_hooks => $self->postponed_hooks,
401443 );
402 $self->route_handlers->{$handler_name} = $handler;
444
445 push @{ $self->route_handlers },
446 { name => $handler_name,
447 handler => $handler,
448 };
403449 }
404450 }
405451
406452 sub register_route_handlers {
407453 my ($self) = @_;
408 for my $handler_name ( keys %{ $self->route_handlers } ) {
409 my $handler = $self->route_handlers->{$handler_name};
410 $handler->register($self);
454 for my $handler ( @{ $self->{route_handlers} } ) {
455 my $handler_code = $handler->{handler};
456 $handler_code->register($self);
411457 }
412458 }
413459
435481 }
436482 }
437483
438 has name => (
439 is => 'ro',
440 isa => Str,
441 );
442
443 # holds a context whenever a request is processed
444 has context => (
445 is => 'rw',
446 isa => Maybe [ InstanceOf ['Dancer2::Core::Context'] ],
447 trigger => sub {
448 my ( $self, $ctx ) = @_;
449 $self->_init_for_context($ctx),;
450 for my $type ( @{ $self->supported_engines } ) {
451 my $engine = $self->engines->{$type}
452 or next;
453 defined($ctx) ? $engine->context($ctx) : $engine->clear_context;
454 }
455 },
456 );
457
458 sub _init_for_context {
459 my ($self) = @_;
460
461 return if !defined $self->context;
462 return if !defined $self->context->request;
463
464 $self->context->request->is_behind_proxy(1)
465 if $self->setting('behind_proxy');
466 }
467
468 has prefix => (
469 is => 'rw',
470 isa => Maybe [Dancer2Prefix],
471 predicate => 1,
472 coerce => sub {
473 my ($prefix) = @_;
474 return undef if defined($prefix) and $prefix eq "/";
475 return $prefix;
476 },
477 );
478
479
480484 sub lexical_prefix {
481485 my ( $self, $prefix, $cb ) = @_;
482486 undef $prefix if $prefix eq '/';
502506 if $e;
503507 }
504508
505 # routes registry, stored by method:
506 has routes => (
507 is => 'rw',
508 isa => HashRef,
509 default => sub {
510 { get => [],
511 head => [],
512 post => [],
513 put => [],
514 del => [],
515 options => [],
516 };
517 },
518 );
519
520
521509 sub add_route {
522510 my ( $self, %route_attrs ) = @_;
523511
529517 push @{ $self->routes->{$method} }, $route;
530518 }
531519
532
533520 sub route_exists {
534521 my ( $self, $route ) = @_;
535522
540527 }
541528 return 0;
542529 }
543
544530
545531 sub routes_regexps_for {
546532 my ( $self, $method ) = @_;
559545
560546 =head1 VERSION
561547
562 version 0.07
548 version 0.09
563549
564550 =head1 DESCRIPTION
565551
00 package Dancer2::Core::Context;
11 {
2 $Dancer2::Core::Context::VERSION = '0.07';
2 $Dancer2::Core::Context::VERSION = '0.09';
33 }
44
55 # ABSTRACT: handles everything proper to a request's context.
1515
1616
1717 has app => (
18 is => 'rw',
19 isa => InstanceOf ['Dancer2::Core::App'],
20 weak_ref => 1,
18 is => 'rw',
19 isa => InstanceOf ['Dancer2::Core::App'],
20 weak_ref => 1,
21 predicate => 1,
2122 );
2223
2324
4041
4142 sub _build_request {
4243 my ($self) = @_;
43 my $req = Dancer2::Core::Request->new( env => $self->env );
44 if ( defined $self->app && defined $self->app->config->{serializer} ) {
45 $req->serializer( $self->app->config->{serializer} );
46 }
44
45 # If we have an app, get the serialization engine
46 my $engine = $self->app->engine('serializer')
47 if $self->has_app;
48
49 my $req = Dancer2::Core::Request->new(
50 env => $self->env,
51 $engine ? ( serializer => $engine ) : (),
52 );
53
54 # Log deserialization errors
55 $self->app->log(
56 core => "Failed to deserialize the request : " . $engine->error )
57 if ( $engine && $engine->has_error );
58
4759 return $req;
4860 }
4961
7587 default => sub {
7688 my $self = shift;
7789 my $resp = Dancer2::Core::Response->new;
78 $resp->serializer( $self->app->config->{serializer} )
79 if $self->app->config->{serializer};
90 if ( $self->has_app ) {
91 my $engine = $self->app->engine('serializer');
92 $resp->serializer($engine) if $engine;
93 }
8094 return $resp;
8195 },
8296 );
111125 $destination = $self->request->uri_for( $destination, {}, 1 );
112126 }
113127
128 $self->response->halt;
114129 $self->response->redirect( $destination, $status );
115130 }
116131
201216
202217 =head1 VERSION
203218
204 version 0.07
219 version 0.09
205220
206221 =head1 ATTRIBUTES
207222
11
22 package Dancer2::Core::Cookie;
33 {
4 $Dancer2::Core::Cookie::VERSION = '0.07';
4 $Dancer2::Core::Cookie::VERSION = '0.09';
55 }
66 use Moo;
77 use URI::Escape;
114114
115115 =head1 VERSION
116116
117 version 0.07
117 version 0.09
118118
119119 =head1 SYNOPSIS
120120
11
22 package Dancer2::Core::DSL;
33 {
4 $Dancer2::Core::DSL::VERSION = '0.07';
4 $Dancer2::Core::DSL::VERSION = '0.09';
55 }
66
77 use Moo;
8 use Carp;
9 use Class::Load 'load_class';
810 use Dancer2::Core::Hook;
911 use Dancer2::Core::Error;
1012 use Dancer2::FileUtils;
11 use Dancer2::ModuleLoader;
12 use Carp;
1313
1414 with 'Dancer2::Core::Role::DSL';
1515
1818 # the flag means : 1 = is global, 0 = is not global. global means can be
1919 # called from anywhere. not global means must be called from within a route
2020 # handler
21 [ [ any => 1 ],
22 [ app => 1 ],
23 [ captures => 0 ],
24 [ config => 1 ],
25 [ content_type => 0 ],
26 [ context => 0 ],
27 [ cookie => 0 ],
28 [ cookies => 0 ],
29 [ dance => 1 ],
30 [ dancer_app => 1 ],
31 [ dancer_version => 1 ],
32 [ dancer_major_version => 1 ],
33 [ debug => 1 ],
34 [ del => 1 ],
35 [ dirname => 1 ],
36 [ dsl => 1 ],
37 [ engine => 1 ],
38 [ error => 1 ],
39 [ false => 1 ],
40 [ forward => 0 ],
41 [ from_dumper => 1 ],
42 [ from_json => 1 ],
43 [ from_yaml => 1 ],
44 [ get => 1 ],
45 [ halt => 0 ],
46 [ header => 0 ],
47 [ headers => 0 ],
48 [ hook => 1 ],
49 [ info => 1 ],
50 [ load_app => 1 ],
51 [ log => 1 ],
52 [ mime => 1 ],
53 [ options => 1 ],
54 [ param => 0 ],
55 [ params => 0 ],
56 [ pass => 0 ],
57 [ patch => 1 ],
58 [ path => 1 ],
59 [ post => 1 ],
60 [ prefix => 1 ],
61 [ push_header => 0 ],
62 [ put => 1 ],
63 [ redirect => 0 ],
64 [ request => 0 ],
65 [ response => 0 ],
66 [ runner => 1 ],
67 [ send_error => 0 ],
68 [ send_file => 0 ],
69 [ session => 0 ],
70 [ set => 1 ],
71 [ setting => 1 ],
72 [ splat => 0 ],
73 [ start => 1 ],
74 [ status => 0 ],
75 [ template => 0 ],
76 [ to_dumper => 1 ],
77 [ to_json => 1 ],
78 [ to_yaml => 1 ],
79 [ true => 1 ],
80 [ upload => 0 ],
81 [ uri_for => 0 ],
82 [ var => 0 ],
83 [ vars => 0 ],
84 [ warning => 1 ],
85 ];
21 { any => { is_global => 1 },
22 app => { is_global => 1 },
23 captures => { is_global => 0 },
24 config => { is_global => 1 },
25 content_type => { is_global => 0 },
26 context => { is_global => 0 },
27 cookie => { is_global => 0 },
28 cookies => { is_global => 0 },
29 dance => { is_global => 1 },
30 dancer_app => { is_global => 1 },
31 dancer_version => { is_global => 1 },
32 dancer_major_version => { is_global => 1 },
33 debug => { is_global => 1 },
34 del => { is_global => 1 },
35 dirname => { is_global => 1 },
36 dsl => { is_global => 1 },
37 engine => { is_global => 1 },
38 error => { is_global => 1 },
39 false => { is_global => 1 },
40 forward => { is_global => 0 },
41 from_dumper => { is_global => 1 },
42 from_json => { is_global => 1 },
43 from_yaml => { is_global => 1 },
44 get => { is_global => 1 },
45 halt => { is_global => 0 },
46 header => { is_global => 0 },
47 headers => { is_global => 0 },
48 hook => { is_global => 1 },
49 info => { is_global => 1 },
50 log => { is_global => 1 },
51 mime => { is_global => 1 },
52 options => { is_global => 1 },
53 param => { is_global => 0 },
54 params => { is_global => 0 },
55 pass => { is_global => 0 },
56 patch => { is_global => 1 },
57 path => { is_global => 1 },
58 post => { is_global => 1 },
59 prefix => { is_global => 1 },
60 push_header => { is_global => 0 },
61 put => { is_global => 1 },
62 redirect => { is_global => 0 },
63 request => { is_global => 0 },
64 response => { is_global => 0 },
65 runner => { is_global => 1 },
66 send_error => { is_global => 0 },
67 send_file => { is_global => 0 },
68 session => { is_global => 0 },
69 set => { is_global => 1 },
70 setting => { is_global => 1 },
71 splat => { is_global => 0 },
72 start => { is_global => 1 },
73 status => { is_global => 0 },
74 template => { is_global => 0 },
75 to_dumper => { is_global => 1 },
76 to_json => { is_global => 1 },
77 to_yaml => { is_global => 1 },
78 true => { is_global => 1 },
79 upload => { is_global => 0 },
80 uri_for => { is_global => 0 },
81 var => { is_global => 0 },
82 vars => { is_global => 0 },
83 warning => { is_global => 1 },
84 };
8685 }
8786
8887 sub dancer_app { shift->app }
129128 $self->app->add_hook(
130129 Dancer2::Core::Hook->new( name => $name, code => $code ) );
131130 }
132
133 sub load_app {
134 my ( $self, $app_name, %options ) = @_;
135
136 # set the application
137 my ( $res, $error ) = Dancer2::ModuleLoader->load($app_name);
138 $res or croak "Unable to load application \"$app_name\" : $error";
139
140 croak "$app_name is not a Dancer2 application"
141 if !$app_name->can('dancer_app');
142 my $app = $app_name->dancer_app;
143
144 # FIXME not working yet
145 }
146
147131
148132 sub prefix {
149133 my $app = shift->app;
392376
393377 =head1 VERSION
394378
395 version 0.07
379 version 0.09
396380
397381 =head1 FUNCTIONS
398382
11
22 package Dancer2::Core::Dispatcher;
33 {
4 $Dancer2::Core::Dispatcher::VERSION = '0.07';
4 $Dancer2::Core::Dispatcher::VERSION = '0.09';
55 }
66 use Moo;
77 use Encode;
6565 or next ROUTE;
6666
6767 $context->request->_set_route_params($match);
68
69 if ( $context->request->has_serializer ) {
70 $context->request->deserialize;
71 if ( $context->request->serializer->has_error ) {
72 $app->log( "core" => "Failed to deserialize the request : "
73 . $context->request->serializer->error );
74 }
75 }
7668
7769 # if the request has been altered by a before filter, we should not continue
7870 # with this route handler, we should continue to walk through the
125117
126118 # pass the baton if the response says so...
127119 if ( $response->has_passed ) {
120
121 ## A previous route might have used splat, failed
122 ## this needs to be cleaned from the request.
123 if ( exists $context->request->{_params}{splat} ) {
124 delete $context->request->{_params}{splat};
125 }
126
128127 $response->has_passed(0); # clear for the next round
129128 next ROUTE;
130129 }
177176 environment => Dancer2->runner->environment,
178177 location => Dancer2->runner->location,
179178 runner_config => Dancer2->runner->config,
180 postponed_hooks => Dancer2->runner->postponed_hooks,
179 postponed_hooks => Dancer2->runner->server->postponed_hooks,
181180 api_version => 2,
182181 );
183182
203202
204203 =head1 VERSION
205204
206 version 0.07
205 version 0.09
207206
208207 =head1 SYNOPSIS
209208
11
22 package Dancer2::Core::Error;
33 {
4 $Dancer2::Core::Error::VERSION = '0.07';
4 $Dancer2::Core::Error::VERSION = '0.09';
55 }
66 use Moo;
77 use Carp;
88 use Dancer2::Core::Types;
9 use Dancer2::Core::HTTP;
910 use Data::Dumper;
1011 use Dancer2::FileUtils 'path';
11
12
13 my %error_title = (
14 400 => "Bad Request",
15 401 => "Unauthorized",
16 402 => "Payment Required",
17 403 => "Forbidden",
18 404 => "Not Found",
19 405 => "Method Not Allowed",
20 406 => "Not Acceptable",
21 407 => "Proxy Authentication Required",
22 408 => "Request Timeout",
23 409 => "Conflict",
24 410 => "Gone",
25 411 => "Length Required",
26 412 => "Precondition Failed",
27 413 => "Request Entity Too Large",
28 414 => "Request-URI Too Long",
29 415 => "Unsupported Media Type",
30 416 => "Requested Range Not Satisfiable",
31 417 => "Expectation Failed",
32 418 => "I'm a teapot",
33 420 => "Enhance Your Calm",
34 422 => "Unprocessable Entity",
35 423 => "Locked",
36 424 => "Failed Dependency",
37 424 => "Method Failure",
38 425 => "Unordered Collection",
39 426 => "Upgrade Required",
40 428 => "Precondition Required",
41 429 => "Too Many Requests",
42 431 => "Request Header Fields Too Large",
43 444 => "No Response",
44 449 => "Retry With",
45 450 => "Blocked by Windows Parental Controls ",
46 451 => "Unavailable For Legal Reasons ",
47 451 => "Redirect",
48 494 => "Request Header Too Large ",
49 495 => "Cert Error",
50 496 => "No Cert ",
51 497 => "HTTP to HTTPS",
52 499 => "Client Closed Request",
53 500 => "Internal Server Error",
54 501 => "Not Implemented",
55 502 => "Bad Gateway",
56 503 => "Service Unavailable",
57 504 => "Gateway Timeout",
58 505 => "HTTP Version Not Supported",
59 506 => "Variant Also Negotiates ",
60 507 => "Insufficient Storage ",
61 508 => "Loop Detected ",
62 509 => "Bandwidth Limit Exceeded ",
63 510 => "Not Extended",
64 511 => "Network Authentication Required ",
65 598 => "Network read timeout error ",
66 599 => "Network connect timeout error ",
67 );
6812
6913
7014 has show_errors => (
10044 sub _build_title {
10145 my ($self) = @_;
10246 my $title = 'Error ' . $self->status;
103 $title .= ' - ' . $error_title{ $self->status }
104 if $error_title{ $self->status };
47 if ( my $msg = Dancer2::Core::HTTP->status_message( $self->status ) ) {
48 $title .= ' - ' . $msg;
49 }
10550
10651 return $title;
10752 }
503448
504449 =head1 VERSION
505450
506 version 0.07
451 version 0.09
507452
508453 =head1 SYNOPSIS
509454
11
22 package Dancer2::Core::Factory;
33 {
4 $Dancer2::Core::Factory::VERSION = '0.07';
4 $Dancer2::Core::Factory::VERSION = '0.09';
55 }
66 use strict;
77 use warnings;
88
9 use Dancer2::ModuleLoader;
9 use Dancer2::Core;
10 use Class::Load 'try_load_class';
1011 use Carp 'croak';
1112
1213 sub create {
1314 my ( $class, $type, $name, %options ) = @_;
1415
15 $type = _camelize($type);
16 $name = _camelize($name);
16 $type = Dancer2::Core::camelize($type);
17 $name = Dancer2::Core::camelize($name);
1718 my $component_class = "Dancer2::${type}::${name}";
1819
19 my ( $ok, $error ) = Dancer2::ModuleLoader->require($component_class);
20 if ( !$ok ) {
21 croak "Unable to load class for $type component $name: $error";
22 }
20 my ( $ok, $error ) = try_load_class($component_class);
21 $ok or croak "Unable to load class for $type component $name: $error";
2322
2423 return $component_class->new(%options);
25 }
26
27 sub _camelize {
28 my ($value) = @_;
29
30 my $camelized = '';
31 for my $word ( split /_/, $value ) {
32 $camelized .= ucfirst($word);
33 }
34 return $camelized;
3524 }
3625
3726 1;
4635
4736 =head1 VERSION
4837
49 version 0.07
38 version 0.09
5039
5140 =head1 AUTHOR
5241
11
22 package Dancer2::Core::HTTP;
33 {
4 $Dancer2::Core::HTTP::VERSION = '0.07';
4 $Dancer2::Core::HTTP::VERSION = '0.09';
55 }
66
77 use strict;
1010 my $HTTP_CODES = {
1111
1212 # informational
13 # 100 => 'Continue', # only on HTTP 1.1
14 # 101 => 'Switching Protocols', # only on HTTP 1.1
13 100 => 'Continue', # only on HTTP 1.1
14 101 => 'Switching Protocols', # only on HTTP 1.1
15 102 => 'Processing', # WebDAV; RFC 2518
1516
16 # processed codes
17 # processed
1718 200 => 'OK',
1819 201 => 'Created',
1920 202 => 'Accepted',
20
21 # 203 => 'Non-Authoritative Information', # only on HTTP 1.1
21 203 => 'Non-Authoritative Information', # only on HTTP 1.1
2222 204 => 'No Content',
2323 205 => 'Reset Content',
2424 206 => 'Partial Content',
25 207 => 'Multi-Status', # WebDAV; RFC 4918
26 208 => 'Already Reported', # WebDAV; RFC 5842
27
28 # 226 => 'IM Used' # RFC 3229
2529
2630 # redirections
2731 301 => 'Moved Permanently',
2832 302 => 'Found',
33 303 => '303 See Other', # only on HTTP 1.1
34 304 => 'Not Modified',
35 305 => '305 Use Proxy', # only on HTTP 1.1
36 306 => 'Switch Proxy',
37 307 => 'Temporary Redirect', # only on HTTP 1.1
2938
30 # 303 => '303 See Other', # only on HTTP 1.1
31 304 => 'Not Modified',
32
33 # 305 => '305 Use Proxy', # only on HTTP 1.1
34 306 => 'Switch Proxy',
35
36 # 307 => '307 Temporary Redirect', # on HTTP 1.1
39 # 308 => 'Permanent Redirect' # approved as experimental RFC
3740
3841 # problems with request
3942 400 => 'Bad Request',
5457 415 => 'Unsupported Media Type',
5558 416 => 'Requested Range Not Satisfiable',
5659 417 => 'Expectation Failed',
60 418 => "I'm a teapot", # RFC 2324
61
62 # 419 => 'Authentication Timeout', # not in RFC 2616
63 420 => 'Enhance Your Calm',
64 422 => 'Unprocessable Entity',
65 423 => 'Locked',
66 424 => 'Failed Dependency', # Also used for 'Method Failure'
67 425 => 'Unordered Collection',
68 426 => 'Upgrade Required',
69 428 => 'Precondition Required',
70 429 => 'Too Many Requests',
71 431 => 'Request Header Fields Too Large',
72 444 => 'No Response',
73 449 => 'Retry With',
74 450 => 'Blocked by Windows Parental Controls',
75 451 => 'Unavailable For Legal Reasons',
76 451 => 'Redirect',
77 494 => 'Request Header Too Large',
78 495 => 'Cert Error',
79 496 => 'No Cert',
80 497 => 'HTTP to HTTPS',
81 499 => 'Client Closed Request',
5782
5883 # problems with server
5984 500 => 'Internal Server Error',
6287 503 => 'Service Unavailable',
6388 504 => 'Gateway Timeout',
6489 505 => 'HTTP Version Not Supported',
90 506 => 'Variant Also Negotiates',
91 507 => 'Insufficient Storage',
92 508 => 'Loop Detected',
93 509 => 'Bandwidth Limit Exceeded',
94 510 => 'Not Extended',
95 511 => 'Network Authentication Required',
96 598 => 'Network read timeout error',
97 599 => 'Network connect timeout error',
6598 };
6699
67100 for my $code ( keys %$HTTP_CODES ) {
77110
78111 sub status {
79112 my ( $class, $status ) = @_;
113 return if !defined $status;
80114 return $status if $status =~ /^\d+/;
81115 if ( exists $HTTP_CODES->{$status} ) {
82116 return $HTTP_CODES->{$status};
83117 }
84118 return undef;
119 }
120
121
122 sub status_message {
123 my ( $class, $status ) = @_;
124 return if !defined $status;
125 my $code = $class->status($status);
126 return if !defined $code || !exists $HTTP_CODES->{$code};
127 return $HTTP_CODES->{$code};
85128 }
86129
87130 1;
96139
97140 =head1 VERSION
98141
99 version 0.07
142 version 0.09
100143
101144 =head1 FUNCTIONS
102145
112155 received, else it will try to find the appropriate alias and return the correct
113156 status.
114157
158 =head2 status_message(status_code)
159
160 Dancer2::Core::HTTP->status_message(200); # returns 'OK'
161
162 Dancer2::Core::HTTP->status_message('error'); # returns 'Internal Server Error'
163
164 Returns the HTTP status message for the given status code.
165
115166 =head1 AUTHOR
116167
117168 Dancer Core Developers
00 # ABSTRACT: Manipulate hooks with Dancer2
11 package Dancer2::Core::Hook;
22 {
3 $Dancer2::Core::Hook::VERSION = '0.07';
3 $Dancer2::Core::Hook::VERSION = '0.09';
44 }
55 use Moo;
66 use Dancer2::Core::Types;
5252
5353 =head1 VERSION
5454
55 version 0.07
55 version 0.09
5656
5757 =head1 SYNOPSIS
5858
11
22 package Dancer2::Core::MIME;
33 {
4 $Dancer2::Core::MIME::VERSION = '0.07';
4 $Dancer2::Core::MIME::VERSION = '0.09';
55 }
66
77 use strict;
9292
9393 =head1 VERSION
9494
95 version 0.07
95 version 0.09
9696
9797 =head1 SYNOPSIS
9898
00 package Dancer2::Core::Request::Upload;
11 {
2 $Dancer2::Core::Request::Upload::VERSION = '0.07';
2 $Dancer2::Core::Request::Upload::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Class representing file upload requests
100100
101101 =head1 VERSION
102102
103 version 0.07
103 version 0.09
104104
105105 =head1 DESCRIPTION
106106
00 package Dancer2::Core::Request;
11 {
2 $Dancer2::Core::Request::VERSION = '0.07';
2 $Dancer2::Core::Request::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Interface for accessing incoming requests
142142 isa => Num,
143143 );
144144
145 # Private 'read-only' attributes for request params. See the params()
146 # method for the public interface.
147 #
148 # _body_params, _query_params and _route_params have setter methods that
149 # decode byte string to characters before setting; If you know you have
150 # decoded (character) params, such as output from a deserializer, you can
151 # set these directly in the request object hash to avoid the decode op.
152
153 has _params => (
154 is => 'lazy',
155 isa => HashRef,
156 builder => '_build_params',
157 predicate => '_has_params',
158 );
159
160 has _body_params => (
161 is => 'ro',
162 isa => Maybe(HashRef),
163 default => sub {undef},
164 );
165
166 sub _set_body_params {
167 my ( $self, $params ) = @_;
168 $self->{_body_params} = _decode($params);
169 $self->_build_params();
170 }
171
172 has _query_params => (
173 is => 'ro',
174 isa => Maybe(HashRef),
175 default => sub {undef},
176 );
177
178 sub _set_query_params {
179 my ( $self, $params ) = @_;
180 $self->{_query_params} = _decode($params);
181 $self->_build_params();
182 }
183
184 has _route_params => (
185 is => 'ro',
186 isa => HashRef,
187 default => sub { {} },
188 );
189
190 sub _set_route_params {
191 my ( $self, $params ) = @_;
192 $self->{_route_params} = _decode($params);
193 $self->_build_params();
194 }
195
145196
146197 has uploads => (
147198 is => 'rw',
148199 isa => HashRef,
149 );
150
151 # Really needed? as we have is_ajax() ...
152 has ajax => (
153 is => 'rw',
154 isa => Bool,
155200 );
156201
157202 has body_is_parsed => (
228273 sub deserialize {
229274 my $self = shift;
230275
231 return unless $self->serializer;
276 return unless $self->has_serializer;
232277
233278 # Content-Type may contain additional parameters
234279 # (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7)
242287 unless grep { $self->method eq $_ } qw/ PUT POST PATCH /;
243288
244289 # try to deserialize
290 my $body = $self->_read_to_end();
245291 my $data = $self->serializer->deserialize( $self->body );
246292 return if !defined $data;
247293
294 # Set _body_params directly rather than using the setter. Deserializiation
295 # returns characters and skipping the decode op in the setter ensures
296 # that numerical data "stays" numerical; decoding an SV that is an IV
297 # converts that to a PVIV. Some serializers are picky (JSON)..
248298 $self->{_body_params} = $data;
249
250 # TODO surely there is a better way
251 $self->{params} = {
252 %{ $self->{params} || {} },
253 %$data,
254 };
299 $self->_build_params();
255300
256301 return $data;
257302 }
279324
280325 $self->{_chunk_size} = 4096;
281326 $self->{_read_position} = 0;
282 $self->{_body_params} = undef;
283 $self->{_query_params} = undef;
284 $self->{_route_params} = {};
285327
286328 $self->_init_request_headers();
287329
289331 HTTP::Body->new( $self->content_type, $self->content_length );
290332 $self->{_http_body}->cleanup(1);
291333
292 $self->_build_params();
334 $self->data; # Deserialize body
335 $self->_params(); # Decode query and body prams
293336 $self->_build_uploads();
294
295 $self->{ajax} = $self->is_ajax;
296337 }
297338
298339
319360 $new_request->method( $options->{method} );
320361 }
321362
322 $new_request->{params} = $new_params;
323 $new_request->_set_body_params( $self->{_body_params} );
324 $new_request->_set_query_params( $self->{_query_params} );
325 $new_request->_set_route_params( $self->{_route_params} );
326 $new_request->{_params_are_decoded} = 1;
327 $new_request->{body} = $self->body;
328 $new_request->{headers} = $self->headers;
363 # Copy params (these are already decoded)
364 $new_request->{_params} = $new_params;
365 $new_request->{_body_params} = $self->{_body_params};
366 $new_request->{_query_params} = $self->{_query_params};
367 $new_request->{_route_params} = $self->{_route_params};
368 $new_request->{body} = $self->body;
369 $new_request->{headers} = $self->headers;
329370
330371 return $new_request;
331372 }
335376 my ( $self, $context, $url, $params, $options ) = @_;
336377 my $new_request = $self->make_forward_to( $url, $params, $options );
337378
338 return Dancer2->runner->server->dispatcher->dispatch(
379 my $new_response = Dancer2->runner->server->dispatcher->dispatch(
339380 $new_request->env,
340381 $new_request,
341382 $context,
342383 );
384 $new_response->halt;
385 $context->response($new_response);
386 return $new_response;
343387 }
344388
345389 sub _merge_params {
390434 }
391435
392436
437 sub dispatch_path {
438 my $self = shift;
439
440 my $path = $self->path;
441
442 # Want $self->base->path, without needing the URI object,
443 # and trim any trailing '/'.
444 my $base = '';
445 $base .= $self->script_name if defined $self->script_name;
446 $base =~ s|/+$||;
447
448 # Remove base from front of path.
449 $path =~ s|^(\Q$base\E)?||;
450 $path =~ s|^/+|/|;
451
452 # PSGI spec notes that '' should be considered '/'
453 $path = '/' if $path eq '';
454 return $path;
455 }
456
457
393458 sub uri_for {
394459 my ( $self, $part, $params, $dont_escape ) = @_;
395460
409474
410475 sub params {
411476 my ( $self, $source ) = @_;
412 my @caller = caller;
413
414 if ( not $self->{_params_are_decoded} ) {
415 $self->{params} = _decode( $self->{params} );
416 $self->{_body_params} = _decode( $self->{_body_params} );
417 $self->{_query_params} = _decode( $self->{_query_params} );
418 $self->{_route_params} = _decode( $self->{_route_params} );
419 $self->{_params_are_decoded} = 1;
420 }
421
422 return %{ $self->{params} } if wantarray && @_ == 1;
423 return $self->{params} if @_ == 1;
477
478 return %{ $self->_params } if wantarray && @_ == 1;
479 return $self->_params if @_ == 1;
424480
425481 if ( $source eq 'query' ) {
426 return %{ $self->{_query_params} } if wantarray;
427 return $self->{_query_params};
482 return %{ $self->_query_params || {} } if wantarray;
483 return $self->_query_params;
428484 }
429485 elsif ( $source eq 'body' ) {
430 return %{ $self->{_body_params} } if wantarray;
431 return $self->{_body_params};
486 return %{ $self->_body_params || {} } if wantarray;
487 return $self->_body_params;
432488 }
433489 if ( $source eq 'route' ) {
434 return %{ $self->{_route_params} } if wantarray;
435 return $self->{_route_params};
490 return %{ $self->_route_params } if wantarray;
491 return $self->_route_params;
436492 }
437493 else {
438494 croak "Unknown source params \"$source\".";
488544 return ( ref($res) eq 'ARRAY' ) ? @$res : $res;
489545 }
490546
491 # TODO : move these into attributes
492 sub _set_route_params {
493 my ( $self, $params ) = @_;
494 $self->{_route_params} = $params;
495 $self->_build_params();
496 }
497
498 sub _set_body_params {
499 my ( $self, $params ) = @_;
500 $self->{_body_params} = $params;
501 $self->_build_params();
502 }
503
504 sub _set_query_params {
505 my ( $self, $params ) = @_;
506 $self->{_query_params} = $params;
507 $self->_build_params();
508 }
509
510547 sub _build_params {
511548 my ($self) = @_;
512549
513550 # params may have been populated by before filters
514551 # _before_ we get there, so we have to save it first
515 my $previous = $self->{params} || {};
552 my $previous = $self->_has_params ? $self->_params : {};
516553
517554 # now parse environement params...
518555 $self->_parse_get_params();
519 if ( $self->{body_is_parsed} ) {
556 if ( $self->body_is_parsed ) {
520557 $self->{_body_params} ||= {};
521558 }
522559 else {
524561 }
525562
526563 # and merge everything
527 $self->{params} = {
528 %$previous, %{ $self->{_query_params} },
529 %{ $self->{_route_params} }, %{ $self->{_body_params} },
564 $self->{_params} = {
565 %$previous, %{ $self->_query_params || {} },
566 %{ $self->_route_params }, %{ $self->_body_params || {} },
530567 };
531568
532569 }
542579
543580 sub _parse_post_params {
544581 my ($self) = @_;
545 return $self->{_body_params} if defined $self->{_body_params};
582 return $self->_body_params if defined $self->_body_params;
546583
547584 my $body = $self->_read_to_end();
548 $self->{_body_params} = $self->{_http_body}->param;
585 $self->_set_body_params( $self->{_http_body}->param );
549586 }
550587
551588 sub _parse_get_params {
552589 my ($self) = @_;
553 return $self->{_query_params} if defined $self->{_query_params};
554
555 $self->{_query_params} = {};
590 return $self->_query_params if defined $self->{_query_params};
591
592 my $query_params = {};
556593
557594 my $source = $self->env->{QUERY_STRING};
558595 return if !defined $source || $source eq '';
559596
560597 if ($XS_PARSE_QUERY_STRING) {
561 return $self->{_query_params} =
562 CGI::Deurl::XS::parse_query_string($source) || {};
598 $self->_set_query_params( CGI::Deurl::XS::parse_query_string($source)
599 || {} );
600 return $self->_query_params;
563601 }
564602
565603 foreach my $token ( split /[&;]/, $source ) {
570608 $val = $self->_url_decode($val);
571609
572610 # looking for multi-value params
573 if ( exists $self->{_query_params}{$key} ) {
574 my $prev_val = $self->{_query_params}{$key};
611 if ( exists $query_params->{$key} ) {
612 my $prev_val = $query_params->{$key};
575613 if ( ref($prev_val) && ref($prev_val) eq 'ARRAY' ) {
576 push @{ $self->{_query_params}{$key} }, $val;
614 push @{ $query_params->{$key} }, $val;
577615 }
578616 else {
579 $self->{_query_params}{$key} = [ $prev_val, $val ];
617 $query_params->{$key} = [ $prev_val, $val ];
580618 }
581619 }
582620
583621 # simple value param (first time we see it)
584622 else {
585 $self->{_query_params}{$key} = $val;
623 $query_params->{$key} = $val;
586624 }
587625 }
588 return $self->{_query_params};
626 $self->_set_query_params($query_params);
627 return $self->_query_params;
589628 }
590629
591630 sub _read_to_end {
727766
728767 =head1 VERSION
729768
730 version 0.07
769 version 0.09
731770
732771 =head1 SYNOPSIS
733772
811850 It also accepts the C<body_is_parsed> boolean flag, if the new request object should
812851 not parse request body.
813852
814 #still exists?
815 =method init()
816
817 Used internally to define some default values and parse parameters.
818
819 #doesn't exist anymore?
820 =method new_for_request($method, $path, $params, $body, $headers)
821
822 An alternate constructor convienient for test scripts which creates a request
823 object with the given arguments.
824
825 #still exists?
826 =method Vars
827
828 Alias to the C<params> accessor, for backward-compatibility with C<CGI> interface.
829
830853 =head2 address()
831854
832855 Return the IP address of the client.
939962
940963 <link rel="stylesheet" href="[% request.uri_base %]/css/style.css" />
941964
965 =head2 dispatch_path()
966
967 The part of the C<path> after C<base>. This is the path used
968 for dispatching the request to routes.
969
942970 =head2 uri_for(path, params)
943971
944972 Constructs a URI from the base and the passed path. If params (hashref) is
11
22 package Dancer2::Core::Response;
33 {
4 $Dancer2::Core::Response::VERSION = '0.07';
4 $Dancer2::Core::Response::VERSION = '0.09';
55 }
66
77 use strict;
213213
214214 =head1 VERSION
215215
216 version 0.07
216 version 0.09
217217
218218 =head1 ATTRIBUTES
219219
0 # ABSTRACT: Config role for Dancer2 core objects
01 package Dancer2::Core::Role::Config;
12 {
2 $Dancer2::Core::Role::Config::VERSION = '0.07';
3 }
4
5 # ABSTRACT: Config role for Dancer2 core objects
3 $Dancer2::Core::Role::Config::VERSION = '0.09';
4 }
65
76 use Moo::Role;
87
98 use Dancer2::Core::Factory;
9 use Dancer2::Core;
1010 use File::Spec;
1111 use Config::Any;
1212 use Dancer2::Core::Types;
1515 use Carp 'croak', 'carp';
1616
1717 has location => (
18 is => 'rw',
19 required => 1,
20 lazy => 1,
21 default => sub { File::Spec->rel2abs('.') },
22 coerce => sub {
23 my ($value) = @_;
24 return File::Spec->rel2abs($value)
25 if !File::Spec->file_name_is_absolute($value);
26 return $value;
27 },
18 is => 'ro',
19 lazy => 1,
20 builder => '_build_location',
2821 );
2922
3023 has config_location => (
7164 builder => '_build_environment',
7265 );
7366
67 has _engines_triggers => (
68 is => 'ro',
69 isa => HashRef,
70 lazy => 1,
71 builder => '_build_engines_triggers',
72 );
73
74 has _config_triggers => (
75 is => 'ro',
76 isa => HashRef,
77 lazy => 1,
78 builder => '_build_config_triggers',
79 );
80
81 has supported_engines => (
82 is => 'ro',
83 isa => ArrayRef,
84 lazy => 1,
85 default => sub { [qw/logger serializer session template/] },
86 );
87
88 has config_files => (
89 is => 'rw',
90 lazy => 1,
91 isa => ArrayRef,
92 builder => '_build_config_files',
93 );
94
95 sub _build_location { File::Spec->rel2abs('.') }
96
7497 sub _build_environment {
7598 $ENV{DANCER_ENVIRONMENT} || $ENV{PLACK_ENV} || 'development';
7699 }
77
78 has _engines_triggers => (
79 is => 'ro',
80 isa => HashRef,
81 lazy => 1,
82 builder => '_build_engines_triggers',
83 );
84
85 has _config_triggers => (
86 is => 'ro',
87 isa => HashRef,
88 lazy => 1,
89 builder => '_build_config_triggers',
90 );
91
92 has supported_engines => (
93 is => 'ro',
94 isa => ArrayRef,
95 lazy => 1,
96 default => sub { [qw/logger serializer session template/] },
97 );
98
99 sub settings { shift->config }
100
101 sub setting {
102 my $self = shift;
103 my @args = @_;
104
105 return ( scalar @args == 1 )
106 ? $self->settings->{ $args[0] }
107 : $self->_set_config_entries(@args);
108 }
109
110 sub has_setting {
111 my ( $self, $name ) = @_;
112 return exists $self->config->{$name};
113 }
114
115 has config_files => (
116 is => 'rw',
117 lazy => 1,
118 isa => ArrayRef,
119 builder => '_build_config_files',
120 );
121100
122101 sub _build_config_files {
123102 my ($self) = @_;
146125 return [ sort @files ];
147126 }
148127
128 sub _build_config {
129 my ($self) = @_;
130 my $location = $self->config_location;
131
132 my $default = {};
133 $default = $self->default_config
134 if $self->can('default_config');
135
136 my $config = Hash::Merge::Simple->merge(
137 $default,
138 map { $self->load_config_file($_) } @{ $self->config_files }
139 );
140
141 $config = $self->_normalize_config($config);
142 return $config;
143 }
144
145 sub _set_config_entries {
146 my ( $self, @args ) = @_;
147 my $no = scalar @args;
148 while (@args) {
149 $self->_set_config_entry( shift(@args), shift(@args) );
150 }
151 return $no;
152 }
153
154 sub _set_config_entry {
155 my ( $self, $name, $value ) = @_;
156
157 $value = $self->_normalize_config_entry( $name, $value );
158 $value = $self->_compile_config_entry( $name, $value, $self->config );
159 $self->config->{$name} = $value;
160 }
161
162 sub _normalize_config {
163 my ( $self, $config ) = @_;
164
165 foreach my $key ( keys %{$config} ) {
166 my $value = $config->{$key};
167 $config->{$key} = $self->_normalize_config_entry( $key, $value );
168 }
169 return $config;
170 }
171
172 sub _compile_config {
173 my ( $self, $config ) = @_;
174
175 foreach my $key ( keys %{$config} ) {
176 my $value = $config->{$key};
177 $config->{$key} =
178 $self->_compile_config_entry( $key, $value, $config );
179 }
180 return $config;
181 }
182
183 sub settings { shift->config }
184
185 sub setting {
186 my $self = shift;
187 my @args = @_;
188
189 return ( scalar @args == 1 )
190 ? $self->settings->{ $args[0] }
191 : $self->_set_config_entries(@args);
192 }
193
194 sub has_setting {
195 my ( $self, $name ) = @_;
196 return exists $self->config->{$name};
197 }
198
149199 sub load_config_file {
150200 my ( $self, $file ) = @_;
151201 my $config;
167217 sub get_postponed_hooks {
168218 my ($self) = @_;
169219 return $self->postponed_hooks;
170
171 # XXX FIXME
172 # return ( ref($self) eq 'Dancer2::Core::App' )
173 # ? (
174 # ( defined $self->server )
175 # ? $self->server->runner->postponed_hooks
176 # : {}
177 # )
178 # : $self->can('postponed_hooks') ? $self->postponed_hooks
179 # : {};
180220 }
181221
182222 # private
183
184 sub _build_config {
185 my ($self) = @_;
186 my $location = $self->config_location;
187
188 my $default = {};
189 $default = $self->default_config
190 if $self->can('default_config');
191
192 my $config = Hash::Merge::Simple->merge(
193 $default,
194 map { $self->load_config_file($_) } @{ $self->config_files }
195 );
196
197 $config = $self->_normalize_config($config);
198 return $config;
199 }
200
201 sub _set_config_entries {
202 my ( $self, @args ) = @_;
203 my $no = scalar @args;
204 while (@args) {
205 $self->_set_config_entry( shift(@args), shift(@args) );
206 }
207 return $no;
208 }
209
210 sub _set_config_entry {
211 my ( $self, $name, $value ) = @_;
212
213 $value = $self->_normalize_config_entry( $name, $value );
214 $value = $self->_compile_config_entry( $name, $value, $self->config );
215 $self->config->{$name} = $value;
216 }
217
218 sub _normalize_config {
219 my ( $self, $config ) = @_;
220
221 foreach my $key ( keys %{$config} ) {
222 my $value = $config->{$key};
223 $config->{$key} = $self->_normalize_config_entry( $key, $value );
224 }
225 return $config;
226 }
227
228 sub _compile_config {
229 my ( $self, $config ) = @_;
230
231 foreach my $key ( keys %{$config} ) {
232 my $value = $config->{$key};
233 $config->{$key} =
234 $self->_compile_config_entry( $key, $value, $config );
235 }
236 return $config;
237 }
238223
239224 my $_normalizers = {
240225 charset => sub {
342327
343328 # XXX we need to move the camilize function out from Core::Factory
344329 # - Franck, 2013/08/03
345 for my $config_key ( $name, Dancer2::Core::Factory::_camelize($name) ) {
330 for my $config_key ( $name, Dancer2::Core::camelize($name) ) {
346331 $engine_config = $config->{engines}{$engine}{$config_key}
347332 if defined $config->{engines}->{$engine}{$config_key};
348333 }
374359 my $engine_options =
375360 $self->_get_config_for_engine( logger => $value, $config );
376361
377 return Dancer2::Core::Factory->create(
362 my $logger = Dancer2::Core::Factory->create(
378363 logger => $value,
379364 %{$engine_options},
380365 app_name => $self->name,
381366 postponed_hooks => $self->get_postponed_hooks
382367 );
368
369 $logger->log_level( $config->{log} ) if exists $config->{log};
370
371 return $logger;
383372 }
384373
385374 sub _build_engine_session {
456445
457446 =head1 VERSION
458447
459 version 0.07
448 version 0.09
460449
461450 =head1 DESCRIPTION
462451
11
22 package Dancer2::Core::Role::DSL;
33 {
4 $Dancer2::Core::Role::DSL::VERSION = '0.07';
4 $Dancer2::Core::Role::DSL::VERSION = '0.09';
55 }
66 use Moo::Role;
77 use Dancer2::Core::Types;
1313
1414 has keywords => (
1515 is => 'rw',
16 isa => ArrayRef,
16 isa => HashRef,
1717 lazy => 1,
1818 builder => '_build_dsl_keywords',
1919 );
2424 my ($self) = @_;
2525 $self->can('dsl_keywords')
2626 ? $self->dsl_keywords
27 : [];
27 : {};
2828 }
2929
3030 sub register {
3131 my ( $self, $keyword, $is_global ) = @_;
32 my $keywords = $self->keywords;
33 my $pkg = ref($self);
34 $pkg =~ s/__WITH__.+$//;
3235
33 grep {/^$keyword$/} @{ $self->keywords }
34 and croak "Keyword '$keyword' is not available.";
36 if ( exists $keywords->{$keyword} ) {
37 my $reg_pkg = $keywords->{$keyword}{'pkg'};
38 $reg_pkg =~ s/__WITH__.+$//;
39 $reg_pkg eq $pkg and return;
3540
36 push @{ $self->keywords }, [ $keyword, $is_global ];
41 croak "[$pkg] Keyword $keyword already registered by $reg_pkg";
42 }
43
44 $keywords->{$keyword} = { is_global => $is_global, pkg => $pkg };
3745 }
3846
3947 sub dsl { $_[0] }
40
41 sub dsl_keywords_as_list {
42 map { $_->[0] } @{ shift->dsl_keywords() };
43 }
4448
4549 # exports new symbol to caller
4650 sub export_symbols_to {
6569 my ( $self, $keyword, $is_global ) = @_;
6670
6771 my $compiled_code = sub {
68 Dancer2::core_debug( "["
72 Dancer2::Core::debug( "["
6973 . $self->app->name
7074 . "] -> $keyword("
7175 . join( ', ', map { defined() ? $_ : '<undef>' } @_ )
8791
8892 sub _construct_export_map {
8993 my ( $self, $args ) = @_;
94 my $keywords = $self->keywords;
9095 my %map;
91 foreach my $keyword ( @{ $self->keywords } ) {
92 my ( $keyword, $is_global ) = @{$keyword};
96 foreach my $keyword ( keys %$keywords ) {
9397
9498 # check if the keyword were excluded from importation
95 $args->{ '!' . $keyword }
96 and next;
97 $map{$keyword} = $self->_compile_keyword( $keyword, $is_global );
99 $args->{ '!' . $keyword } and next;
100 $map{$keyword} =
101 $self->_compile_keyword( $keyword,
102 $keywords->{$keyword}{is_global} );
98103 }
99104 return \%map;
100105 }
111116
112117 =head1 VERSION
113118
114 version 0.07
119 version 0.09
115120
116121 =head1 AUTHOR
117122
11
22 package Dancer2::Core::Role::Engine;
33 {
4 $Dancer2::Core::Role::Engine::VERSION = '0.07';
4 $Dancer2::Core::Role::Engine::VERSION = '0.09';
55 }
66 use Moo::Role;
77 use Dancer2::Core::Types;
4141
4242 =head1 VERSION
4343
44 version 0.07
44 version 0.09
4545
4646 =head1 DESCRIPTION
4747
11
22 package Dancer2::Core::Role::Handler;
33 {
4 $Dancer2::Core::Role::Handler::VERSION = '0.07';
4 $Dancer2::Core::Role::Handler::VERSION = '0.09';
55 }
66 use Moo::Role;
77 use Dancer2::Core::Types;
2727
2828 =head1 VERSION
2929
30 version 0.07
30 version 0.09
3131
3232 =head1 ATTRIBUTES
3333
11
22 package Dancer2::Core::Role::Headers;
33 {
4 $Dancer2::Core::Role::Headers::VERSION = '0.07';
4 $Dancer2::Core::Role::Headers::VERSION = '0.09';
55 }
66
77
8282
8383 =head1 VERSION
8484
85 version 0.07
85 version 0.09
8686
8787 =head1 DESCRIPTION
8888
11
22 package Dancer2::Core::Role::Hookable;
33 {
4 $Dancer2::Core::Role::Hookable::VERSION = '0.07';
4 $Dancer2::Core::Role::Hookable::VERSION = '0.09';
55 }
66 use Moo::Role;
7 use Dancer2::Core;
78 use Dancer2::Core::Types;
89 use Carp 'croak';
910
6869 $h_type = 'engine';
6970 }
7071
71 # Dancer2::core_debug("looking for hooks for $h_type/$h_name");
72 # Dancer2::Core::debug("looking for hooks for $h_type/$h_name");
7273 # keep only the hooks we want
7374 $postponed_hooks = $postponed_hooks->{$h_type}{$h_name};
7475 return unless defined $postponed_hooks;
8182 or croak "$h_name $h_type does not support the hook `$name'. ("
8283 . join( ", ", @{$caller} ) . ")";
8384
84 # Dancer2::core_debug("Adding hook '$name' to $self");
85 # Dancer2::Core::debug("Adding hook '$name' to $self");
8586 $self->add_hook($hook);
8687 }
8788 }
157158
158159 =head1 VERSION
159160
160 version 0.07
161 version 0.09
161162
162163 =head1 AUTHOR
163164
11
22 package Dancer2::Core::Role::Logger;
33 {
4 $Dancer2::Core::Role::Logger::VERSION = '0.07';
4 $Dancer2::Core::Role::Logger::VERSION = '0.09';
55 }
66 use Dancer2::Core::Types;
77
3434 is => 'ro',
3535 isa => Str,
3636 );
37
3837
3938 has log_format => (
4039 is => 'rw',
7776 $message = Encode::encode( $self->auto_encoding_charset, $message )
7877 if $self->auto_encoding_charset;
7978
80 my @stack = caller(2);
79 my @stack = caller(6);
8180
8281 my $block_handler = sub {
8382 my ( $block, $type ) = @_;
177176
178177 =head1 VERSION
179178
180 version 0.07
179 version 0.09
180
181 =head1 DESCRIPTION
182
183 Any class that consumes this role will be able to implement to write log messages.
184
185 In order to implement this role, the consumer B<must> implement the C<log>
186 method. This method will receives as argument the C<level> and the C<message>.
187
188 =head1 ATTRIBUTES
189
190 =head2 auto_encoding_charset
191
192 Charset to use when writing a message.
193
194 =head2 app_name
195
196 Name of the application. Can be used in the message.
197
198 =head2 log_format
199
200 This is a format string (or a preset name) to specify the log format.
201
202 The possible values are:
203
204 =over 4
205
206 =item %h
207
208 host emitting the request
209
210 =item %t
211
212 date (local timezone, formatted like %d/%b/%Y %H:%M:%S)
213
214 =item %T
215
216 date (local timezone, formatted like %Y-%m-%d %H:%M:%S)
217
218 =item %u
219
220 date (UTC timezone, formatted like %d/%b/%Y %H:%M:%S)
221
222 =item %U
223
224 date (UTC timezone, formatted like %Y-%m-%d %H:%M:%S)
225
226 =item %P
227
228 PID
229
230 =item %L
231
232 log level
233
234 =item %D
235
236 timer
237
238 =item %m
239
240 message
241
242 =item %f
243
244 file name that emit the message
245
246 =item %l
247
248 line from the file
249
250 =item %i
251
252 request ID
253
254 =item %{$fmt}t
255
256 timer formatted with a valid time format
257
258 =item %{header}h
259
260 header value
261
262 =back
263
264 =head2 log_level
265
266 Level to use by default.
267
268 =head1 METHODS
269
270 =head2 core
271
272 Log messages as B<core>.
273
274 =head2 debug
275
276 Log messages as B<debug>.
277
278 =head2 info
279
280 Log messages as B<info>.
281
282 =head2 warning
283
284 Log messages as B<warning>.
285
286 =head2 error
287
288 Log messages as B<error>.
289
290 =head2 format_message
291
292 Provides a common message formating.
293
294 =head1 CONFIGURATION
295
296 The B<logger> configuration variable tells Dancer2 which engine to use.
297
298 You can change it either in your config.yml file:
299
300 # logging to console
301 logger: "console"
302
303 The log format can also be configured,
304 please see L<Dancer2::Core::Role::Logger/"logger_format"> for details.
305
306 =head1 METHODS
181307
182308 =head1 AUTHOR
183309
11
22 package Dancer2::Core::Role::Serializer;
33 {
4 $Dancer2::Core::Role::Serializer::VERSION = '0.07';
4 $Dancer2::Core::Role::Serializer::VERSION = '0.09';
55 }
66 use Dancer2::Core::Types;
77
1717
1818 sub _build_type {'Serializer'}
1919
20
2120 requires 'serialize';
2221 requires 'deserialize';
2322 requires 'loaded';
2928 );
3029
3130 around serialize => sub {
32 my ( $orig, $self, @data ) = @_;
33 $self->execute_hook( 'engine.serializer.before', @data );
34 my $serialized = eval { $self->$orig(@data); };
31 my ( $orig, $self, $content, $options ) = @_;
32
33 $self->execute_hook( 'engine.serializer.before', $content );
34 my $serialized = eval { $self->$orig( $content, $options ); };
3535
3636 if ($@) {
3737 $self->error($@);
4343 };
4444
4545 around deserialize => sub {
46 my ( $orig, $self, @data ) = @_;
47 my $data = eval { $self->$orig(@data); };
46 my ( $orig, $self, $content, $options ) = @_;
47 my $data = eval { $self->$orig( $content, $options ); };
4848 $self->error($@) if $@;
4949 return $data;
5050 };
5151
5252 # attribute vs method?
53 sub content_type {'text/plain'}
53 has content_type => (
54 is => 'ro',
55 isa => Str,
56 required => 1,
57 );
5458
5559 # most serializer don't have to overload this one
5660 sub support_content_type {
7478
7579 =head1 VERSION
7680
77 version 0.07
81 version 0.09
7882
79 =head1 REQUIREMENTS
83 =head1 DESCRIPTION
8084
81 Classes that consume that role must implement the following methods
82 C<serialize>, C<deserialize> and C<loaded>.
85 Any class that consumes this role will be able to be used as a
86 serializer under Dancer2.
87
88 In order to implement this role, the consumer B<must> implement the
89 methods C<serialize>, <deserialize> and C<loaded>, and should define
90 the C<content_type> attribute value.
91
92 =head1 ATTRIBUTES
93
94 =head2 error
95
96 The error string in case the serializer is in error state.
97
98 =head2 content_type
99
100 The I<content type> of the object after being serialized. For example,
101 a JSON serializer would have a I<application/json> content type
102 defined.
103
104 =head1 METHODS
105
106 =head2 has_error
107
108 A predicate to check whether the serializer is in error state.
109
110 =head2 serialize($content, [\%options])
111
112 The serialize method need to be implemented by the consumer. It
113 receives the serializer class object and a reference to the object to
114 be serialized. Should return the object after being serialized, in the
115 content type defined by the C<content_type> attribute.
116
117 A third optional argument is a hash reference of options to the
118 serializer.
119
120 =head2 deserialize($content, [\%options])
121
122 The inverse method of C<serialize>. Receives the serializer class
123 object and a string that should be deserialized. The method should
124 return a reference to the deserialized Perl data structure.
125
126 A third optional argument is a hash reference of options to the
127 serializer.
128
129 =head2 loaded
130
131 This method should return a boolean true value if the serializer is
132 able to work. This method might verify the existence of some Perl
133 module or some other detail. If everything needed for the serializer
134 to work is present the method returns a true value. If not, returns a
135 false value.
136
137 =head1 METHODS
83138
84139 =head1 AUTHOR
85140
00 # ABSTRACT: Role for Server classes
1
21 package Dancer2::Core::Role::Server;
32 {
4 $Dancer2::Core::Role::Server::VERSION = '0.07';
3 $Dancer2::Core::Role::Server::VERSION = '0.09';
54 }
65 use Moo::Role;
76
1514 use Dancer2::Core::Request;
1615 use Dancer2::Core::Context;
1716
17 requires '_build_name';
1818
1919 has name => (
2020 is => 'ro',
2222 builder => 1,
2323 );
2424
25
2625 has host => (
2726 is => 'rw',
2827 isa => Str,
2928 required => 1,
3029 );
31
3230
3331 has port => (
3432 is => 'rw',
3634 required => 1,
3735 );
3836
39
4037 has is_daemon => (
4138 is => 'rw',
4239 isa => Bool,
4340 );
44
4541
4642 has apps => (
4743 is => 'ro',
4844 isa => ArrayRef,
4945 default => sub { [] },
5046 );
51
52 has runner => (
53 is => 'ro',
54 required => 1,
55 isa => InstanceOf ['Dancer2::Core::Runner'],
56 weak_ref => 1,
57 );
58
5947
6048 has dispatcher => (
6149 is => 'rw',
6452 builder => '_build_dispatcher',
6553 );
6654
67 requires '_build_name';
55 has postponed_hooks => (
56 is => 'rw',
57 isa => HashRef,
58 default => sub { {} },
59 );
6860
6961 sub _build_dispatcher {
7062 my ($self) = @_;
7264 $d->apps( $self->apps );
7365 return $d;
7466 }
75
7667
7768 # our PSGI application
7869 sub psgi_app {
9485 };
9586 }
9687
97
9888 sub register_application {
9989 my ( $self, $app ) = @_;
10090 push @{ $self->apps }, $app;
10191 $app->server($self);
102 $app->server->runner->postponed_hooks(
103 { %{ $app->server->runner->postponed_hooks },
104 %{ $app->postponed_hooks }
92
93 # add postponed hooks to our app-global copy
94 $self->add_postponed_hooks( $app->postponed_hooks );
95 }
96
97 sub add_postponed_hooks {
98 my $self = shift;
99 my $hooks = shift;
100
101 $self->postponed_hooks(
102 { %{ $self->postponed_hooks },
103 %{$hooks},
105104 }
106105 );
107106 }
118117
119118 =head1 VERSION
120119
121 version 0.07
120 version 0.09
122121
123122 =head1 DESCRIPTION
124123
157156
158157 It has a lazy builder that creates a new dispatcher with the server's apps.
159158
159 =head2 postponed_hooks
160
161 Postponed hooks will be applied at the end, when the hookable objects are
162 instantiated, not before.
163
160164 =head1 METHODS
161165
162166 =head2 psgi_app
00 package Dancer2::Core::Role::SessionFactory::File;
11 {
2 $Dancer2::Core::Role::SessionFactory::File::VERSION = '0.07';
2 $Dancer2::Core::Role::SessionFactory::File::VERSION = '0.09';
33 }
44
55 #ABSTRACT: Role for file-based session factories
114114
115115 =head1 VERSION
116116
117 version 0.07
117 version 0.09
118118
119119 =head1 DESCRIPTION
120120
00 package Dancer2::Core::Role::SessionFactory;
11 {
2 $Dancer2::Core::Role::SessionFactory::VERSION = '0.07';
2 $Dancer2::Core::Role::SessionFactory::VERSION = '0.09';
33 }
44
55 #ABSTRACT: Role for session factories
88 use strict;
99 use warnings;
1010 use Carp 'croak';
11 use Class::Load 'try_load_class';
1112 use Dancer2::Core::Session;
1213 use Dancer2::Core::Types;
13 use Dancer2::ModuleLoader;
1414 use Digest::SHA 'sha1';
1515 use List::Util 'shuffle';
1616 use MIME::Base64 'encode_base64url';
112112
113113 {
114114 my $COUNTER = 0;
115 my $CPRNG_AVAIL = Dancer2::ModuleLoader->require("Math::Random::ISAAC::XS")
116 && Dancer2::ModuleLoader->require("Crypt::URandom");
115 my $CPRNG_AVAIL = try_load_class('Math::Random::ISAAC::XS')
116 && try_load_class('Crypt::URandom');
117117
118118 # don't initialize until generate_id is called so the ISAAC algorithm
119119 # is seeded after any pre-forking
271271
272272 =head1 VERSION
273273
274 version 0.07
274 version 0.09
275275
276276 =head1 DESCRIPTION
277277
11
22 package Dancer2::Core::Role::StandardResponses;
33 {
4 $Dancer2::Core::Role::StandardResponses::VERSION = '0.07';
4 $Dancer2::Core::Role::StandardResponses::VERSION = '0.09';
55 }
66 use Moo::Role;
77
4343
4444 =head1 VERSION
4545
46 version 0.07
46 version 0.09
4747
4848 =head1 METHODS
4949
11
22 package Dancer2::Core::Role::Template;
33 {
4 $Dancer2::Core::Role::Template::VERSION = '0.07';
4 $Dancer2::Core::Role::Template::VERSION = '0.09';
55 }
66
77 use Dancer2::Core::Types;
1111 use Data::Dumper;
1212 use Moo::Role;
1313 with 'Dancer2::Core::Role::Engine';
14
1514
1615 sub supported_hooks {
1716 qw/
2625
2726 requires 'render';
2827
29
3028 has name => (
3129 is => 'ro',
3230 lazy => 1,
3836 $name;
3937 }
4038
41
4239 has charset => (
4340 is => 'ro',
4441 isa => Str,
4542 default => sub {'UTF-8'},
4643 );
4744
48
4945 has default_tmpl_ext => (
5046 is => 'rw',
5147 isa => Str,
5248 default => sub { shift->config->{extension} || 'tt' },
5349 );
5450
55
5651 has views => (
5752 is => 'rw',
5853 isa => Maybe [Str],
5954 );
6055
61
6256 has layout => (
6357 is => 'rw',
6458 isa => Maybe [Str],
6559 );
66
6760
6861 has engine => (
6962 is => 'ro',
7972 return $view;
8073 }
8174
82
8375 sub view_pathname {
8476 my ( $self, $view ) = @_;
8577
8678 $view = $self->_template_name($view);
8779 return path( $self->views, $view );
8880 }
89
9081
9182 sub layout_pathname {
9283 my ( $self, $layout ) = @_;
9485 return path( $self->views, 'layouts', $layout );
9586 }
9687
97
9888 sub render_layout {
9989 my ( $self, $layout, $tokens, $content ) = @_;
10090
10393 # FIXME: not sure if I can "just call render"
10494 $self->render( $layout, { %$tokens, content => $content } );
10595 }
106
10796
10897 sub apply_renderer {
10998 my ( $self, $view, $tokens ) = @_;
119108 defined $content and return $content;
120109 return;
121110 }
122
123111
124112 sub apply_layout {
125113 my ( $self, $content, $tokens, $options ) = @_;
178166 return $tokens;
179167 }
180168
181
182169 sub process {
183170 my ( $self, $view, $tokens, $options ) = @_;
184171 my ( $content, $full_content );
216203
217204 =head1 VERSION
218205
219 version 0.07
206 version 0.09
220207
221208 =head1 DESCRIPTION
222209
223 This role provides methods and attributes needed for working with template.
224
225 All Dancer's templates engine should consume this role, and they B<need> to
226 implement a C<render> method. This method will receive three arguments:
210 Any class that consumes this role will be able to be used as a template engine
211 under Dancer2.
212
213 In order to implement this role, the consumer B<must> implement the method C<render>. This method will receive three arguments:
227214
228215 =over 4
229216
235222
236223 =back
237224
225 =head1 ATTRIBUTES
226
227 =head2 name
228
229 The name of the template engine (e.g.: Simple).
230
231 =head2 charset
232
233 The charset. The default value is B<UTF-8>.
234
235 =head2 default_tmpl_ext
236
237 The default file extension. If not provided, B<tt> is used.
238
239 =head2 views
240
241 Path to the directory containing the views.
242
243 =head2 layout
244
245 Path to the directory containing the layouts.
246
247 =head2 engine
248
249 Contains the engine.
250
238251 =head1 METHODS
239252
240 =head2 name
241
242 The name of the template engine (e.g.: Simple).
243
244 =head2 charset
245
246 The charset. The default value is B<UTF-8>.
247
248 =head2 default_tmpl_ext
249
250 The default file extension. If not provided, B<tt> is used.
251
252 =head2 views
253
254 Path to the directory containing the views.
255
256 =head2 layout
257
258 Path to the directory containing the layouts.
259
260 =head2 engine
261
262 Contains the engine.
263
264253 =head2 view_pathname($view)
265254
266255 Returns the full path to the requested view.
269258
270259 Returns the full path to the requested layout.
271260
272 =head2 render_layout($layout, $tokens, \$content)
261 =head2 render_layout($layout, \%tokens, \$content)
273262
274263 Render the layout with the applied tokens
275264
276 =head2 apply_renderer($view, $tokens)
277
278 =head2 apply_layout
279
280 =head2 process($view, $tokens, $options)
265 =head2 apply_renderer($view, \%tokens)
266
267 =head2 apply_layout($content, \%tokens, \%options)
268
269 =head2 process($view, \%tokens, \%options)
270
271 =head1 METHODS
281272
282273 =head1 AUTHOR
283274
11
22 package Dancer2::Core::Route;
33 {
4 $Dancer2::Core::Route::VERSION = '0.07';
4 $Dancer2::Core::Route::VERSION = '0.09';
55 }
66
77 use strict;
7171 );
7272
7373 has _match_data => (
74 is => 'rw',
75 isa => HashRef,
76 trigger => sub {
77 my ( $self, $value ) = @_;
78 },
74 is => 'rw',
75 isa => HashRef,
7976 );
8077
8178 has _params => (
9390 }
9491
9592 my %params;
96 my @values = $request->path =~ $self->regexp;
93 my @values = $request->dispatch_path =~ $self->regexp;
9794
9895 # the regex comments are how we know if we captured
9996 # a splat or a megasplat
164161 # init prefix
165162 if ($prefix) {
166163 $args{regexp} =
167 ref($regexp) eq 'Regexp' ? qr{\Q${prefix}\E${regexp}}
164 ref($regexp) eq 'Regexp' ? qr{^\Q${prefix}\E${regexp}$}
168165 : $regexp eq '/' ? qr{^\Q${prefix}\E/?$}
169166 : $prefix . $regexp;
170167 }
238235
239236 =head1 VERSION
240237
241 version 0.07
238 version 0.09
242239
243240 =head1 ATTRIBUTES
244241
00 # ABSTRACT: Top-layer class to start a dancer app
11 package Dancer2::Core::Runner;
22 {
3 $Dancer2::Core::Runner::VERSION = '0.07';
3 $Dancer2::Core::Runner::VERSION = '0.09';
44 }
55
66 use Moo;
7 use Carp 'croak';
8 use Class::Load 'try_load_class';
79 use Dancer2::Core::Types;
810 use Dancer2::Core::MIME;
9 use Carp 'croak';
10
11
12 use File::Spec;
13 use File::Basename;
1114 use Dancer2::FileUtils;
12 use Dancer2::ModuleLoader;
13 use File::Basename;
14 use File::Spec;
1515
1616 with 'Dancer2::Core::Role::Config';
17
18
19 has postponed_hooks => (
20 is => 'rw',
21 isa => HashRef,
22 default => sub { {} },
23 );
24
2517
2618 # the path to the caller script that is starting the app
2719 # mandatory, because we use that to determine where the appdir is.
2921 is => 'ro',
3022 isa => Str,
3123 required => 1,
32 trigger => sub {
33 my ( $self, $script ) = @_;
34 $self->_build_location($script);
35 },
3624 );
3725
38
3926 has server => (
40 is => 'rw',
27 is => 'ro',
4128 isa => ConsumerOf ['Dancer2::Core::Role::Server'],
4229 lazy => 1,
4330 builder => '_build_server',
31 );
32
33 has mime_type => (
34 is => 'ro',
35 isa => InstanceOf ['Dancer2::Core::MIME'],
36 default => sub { Dancer2::Core::MIME->new(); },
4437 );
4538
4639 # when the runner is created, it has to init the server instance
5043 my $server_name = $self->config->{apphandler};
5144 my $server_class = "Dancer2::Core::Server::${server_name}";
5245
53 my ( $res, $error ) = Dancer2::ModuleLoader->load($server_class);
54 $res or croak "Unable to load $server_class : $error";
46 my ( $ok, $error ) = try_load_class($server_class);
47 $ok or croak "Unable to load $server_class: $error\n";
5548
5649 return $server_class->new(
5750 host => $self->config->{host},
5851 port => $self->config->{port},
5952 is_daemon => $self->config->{is_daemon},
60 runner => $self,
6153 );
6254 }
63
64
65 has mime_type => (
66 is => 'rw',
67 isa => InstanceOf ["Dancer2::Core::MIME"],
68 default => sub { Dancer2::Core::MIME->new(); },
69 );
70
7155
7256 # our Config role needs a default_config hash
7357 sub default_config {
8064 content_type => ( $ENV{DANCER_CONTENT_TYPE} || 'text/html' ),
8165 charset => ( $ENV{DANCER_CHARSET} || '' ),
8266 warnings => ( $ENV{DANCER_WARNINGS} || 0 ),
83 startup_info => ( $ENV{DANCER_STARTUP_INFO} || 1 ),
67 startup_info => ( $ENV{DANCER_STARTUP_INFO} || 0 ),
8468 traces => ( $ENV{DANCER_TRACES} || 0 ),
8569 logger => ( $ENV{DANCER_LOGGER} || 'console' ),
8670 host => ( $ENV{DANCER_SERVER} || '0.0.0.0' ),
9377 };
9478 }
9579
96
9780 sub _build_location {
98 my ( $self, $script ) = @_;
81 my $self = shift;
82 my $script = $self->caller;
9983
10084 # default to the dir that contains the script...
10185 my $location = Dancer2::FileUtils::dirname($script);
11498 #try to find .dancer_app file to determine the root of dancer app
11599 my $dancerdir = Dancer2::FileUtils::path( $subdir, '.dancer' );
116100
117 # if one of them is found, keep that
118 if ( ( -d $libdir && -d $bindir ) || ( -f $dancerdir ) ) {
101 # if one of them is found, keep that; but skip ./blib since both lib and bin exist
102 # under it, but views and public do not.
103 if ( ( $subdir !~ m!/blib/?$! && -d $libdir && -d $bindir )
104 || ( -f $dancerdir ) )
105 {
119106 $subdir_found = 1;
120107 last;
121108 }
122 $subdir = Dancer2::FileUtils::path( $subdir, '..' );
109
110 $subdir = Dancer2::FileUtils::path( $subdir, '..' ) || '.';
123111 last if File::Spec->rel2abs($subdir) eq File::Spec->rootdir;
124112
125113 }
126114
127 $self->location( $subdir_found ? $subdir : $location );
128 }
129
115 my $path = $subdir_found ? $subdir : $location;
116
117 # return if absolute
118 File::Spec->file_name_is_absolute($path)
119 and return $path;
120
121 # convert relative to absolute
122 return File::Spec->rel2abs($path);
123 }
124
125 sub BUILD {
126 my $self = shift;
127
128 # this assures any failure in building the location
129 # will be encountered as soon as possible
130 # while making sure that 'caller' is already available
131 $self->location;
132
133 # set the global runner object if one doesn't exist yet
134 # this can happen if you create one without going through Dancer2
135 # which doesn't trigger the import that creates it
136 defined $Dancer2::runner
137 or $Dancer2::runner = $self;
138 }
130139
131140 sub start {
132141 my ($self) = @_;
133142 my $server = $self->server;
134143
135 $_->finish for @{ $server->apps };
144 foreach my $app ( @{ $server->apps } ) {
145 $app->finish;
146 }
136147
137148 # update the server config if needed
138149 my $port = $self->setting('server_port');
149160 sub name {"runner"}
150161
151162 1;
152
153163
154164 #still exists?
155165 #=method BUILD
171181
172182 =head1 VERSION
173183
174 version 0.07
184 version 0.09
175185
176186 =head1 DESCRIPTION
177187
194204 =back
195205
196206 =head1 ATTRIBUTES
197
198 =head2 postponed_hooks
199
200 Postponed hooks will be applied at the end, when the hookable objects are
201 instantiated, not before.
202207
203208 =head2 caller
204209
11
22 package Dancer2::Core::Server::PSGI;
33 {
4 $Dancer2::Core::Server::PSGI::VERSION = '0.07';
4 $Dancer2::Core::Server::PSGI::VERSION = '0.09';
55 }
66 use Moo;
77 use Carp;
2929
3030 =head1 VERSION
3131
32 version 0.07
32 version 0.09
3333
3434 =head1 DESCRIPTION
3535
11
22 package Dancer2::Core::Server::Standalone;
33 {
4 $Dancer2::Core::Server::Standalone::VERSION = '0.07';
4 $Dancer2::Core::Server::Standalone::VERSION = '0.09';
55 }
66
77 use Moo;
4141 my $pid = $$; #Todo:how to get background pid?
4242
4343 # we only print the info if we need to
44 $self->runner->config->{'startup_info'} or return;
44 Dancer2->runner->config->{'startup_info'} or return;
4545
4646 # bare minimum
4747 print STDERR ">> Dancer2 v$Dancer2::VERSION server $pid listening "
7272
7373 =head1 VERSION
7474
75 version 0.07
75 version 0.09
7676
7777 =head1 DESCRIPTION
7878
00 package Dancer2::Core::Session;
11 {
2 $Dancer2::Core::Session::VERSION = '0.07';
2 $Dancer2::Core::Session::VERSION = '0.09';
33 }
44
55 #ABSTRACT: class to represent any session object
1313
1414
1515 has id => (
16 is => 'rw',
16 is => 'ro',
1717 isa => Str,
1818 required => 1,
1919 );
7575
7676 =head1 VERSION
7777
78 version 0.07
78 version 0.09
7979
8080 =head1 DESCRIPTION
8181
00 package Dancer2::Core::Time;
11 {
2 $Dancer2::Core::Time::VERSION = '0.07';
2 $Dancer2::Core::Time::VERSION = '0.09';
33 }
44
55 #ABSTRACT: class to handle common helpers for time manipulations
145145
146146 =head1 VERSION
147147
148 version 0.07
148 version 0.09
149149
150150 =head1 SYNOPSIS
151151
00 package Dancer2::Core::Types;
11 {
2 $Dancer2::Core::Types::VERSION = '0.07';
2 $Dancer2::Core::Types::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Moo types for Dancer2 core.
147147
148148 =head1 VERSION
149149
150 version 0.07
150 version 0.09
151151
152152 =head1 DESCRIPTION
153153
0 # ABSTRACT: Core libraries for Dancer2 2.0
1
02 package Dancer2::Core;
13 {
2 $Dancer2::Core::VERSION = '0.07';
4 $Dancer2::Core::VERSION = '0.09';
35 }
46
5 # ABSTRACT: Core libraries for Dancer2 2.0
7 use strict;
8 use warnings;
9
10
11 sub debug {
12 return unless $ENV{DANCER_DEBUG_CORE};
13
14 my $msg = shift;
15 my (@stuff) = @_;
16
17 my $vars = @stuff ? Dumper( \@stuff ) : '';
18
19 my ( $package, $filename, $line ) = caller;
20
21 chomp $msg;
22 print STDERR "core: $msg\n$vars";
23 }
24
25
26 sub camelize {
27 my ($value) = @_;
28
29 my $camelized = '';
30 for my $word ( split /_/, $value ) {
31 $camelized .= ucfirst($word);
32 }
33 return $camelized;
34 }
35
636
737 1;
838
1646
1747 =head1 VERSION
1848
19 version 0.07
49 version 0.09
50
51 =head1 FUNCTIONS
52
53 =head2 debug
54
55 Output a message to STDERR and take further arguments as some data structures using
56 L<Data::Dumper>
57
58 =head2 camelize
59
60 Camelize a underscore-separated-string.
2061
2162 =head1 AUTHOR
2263
11
22 package Dancer2::FileUtils;
33 {
4 $Dancer2::FileUtils::VERSION = '0.07';
4 $Dancer2::FileUtils::VERSION = '0.09';
55 }
66
77 use strict;
8585 # by Mitch Frazier
8686 my $path = shift or return;
8787 my $seqregex = qr{
88 [^/]* # anything without a slash
89 /\.\./ # that is accompanied by two dots as such
88 [^/]* # anything without a slash
89 /\.\.(/|\z) # that is accompanied by two dots as such
9090 }x;
9191
9292 $path =~ s{/\./}{/}g;
110110
111111 =head1 VERSION
112112
113 version 0.07
113 version 0.09
114114
115115 =head1 SYNOPSIS
116116
11
22 package Dancer2::Handler::AutoPage;
33 {
4 $Dancer2::Handler::AutoPage::VERSION = '0.07';
4 $Dancer2::Handler::AutoPage::VERSION = '0.09';
55 }
66 use Moo;
77 use Carp 'croak';
2626 sub {
2727 my $ctx = shift;
2828
29 my $template = $ctx->app->config->{template};
29 my $page = $ctx->request->path_info;
30
31 my $template = $ctx->app->engine('template');
3032 if ( !defined $template ) {
3133 $ctx->response->has_passed(1);
3234 return;
3335 }
3436
35 my $page = $ctx->request->params->{'page'};
36 my $view_path = $template->view($page);
37 my $view_path = $template->view_pathname($page);
38
3739 if ( !-f $view_path ) {
3840 $ctx->response->has_passed(1);
3941 return;
4547 };
4648 }
4749
48 sub regexp {'/:page'}
50 sub regexp {'/**'}
4951
5052 sub methods {qw(head get)}
5153
6163
6264 =head1 VERSION
6365
64 version 0.07
66 version 0.09
6567
6668 =head1 DESCRIPTION
6769
68 The AutoPage feature is a Handler (turned on by default) that is responsible
69 for serving pages that match an existing template. If a view exists with a name
70 that matches the requested path, Dancer2 processes the request using the
71 Autopage handler.
70 The AutoPage feature is a Handler (turned off by default) that is
71 responsible for serving pages that match an existing template. If a
72 view exists with a name that matches the requested path, Dancer2
73 processes the request using the Autopage handler.
7274
73 This allows you to easily serve simple pages without having to write a route
74 definition for them.
75 To turn it add to your config file:
76
77 auto_page: 1
78
79 This allows you to easily serve simple pages without having to write a
80 route definition for them.
7581
7682 If there's no view with the name request, the route passes, allowing
7783 other matching routes to be dispatched.
11
22 package Dancer2::Handler::File;
33 {
4 $Dancer2::Handler::File::VERSION = '0.07';
4 $Dancer2::Handler::File::VERSION = '0.09';
55 }
66 use Carp 'croak';
77 use Moo;
4242
4343 sub BUILD {
4444 my ($self) = @_;
45
4645 if ( !defined $self->public_dir ) {
4746 my $public =
4847 $self->app->config->{public}
9291 }
9392
9493 my $file_path = path( $self->public_dir, @tokens );
95 $self->execute_hook( 'handler.file.before_render', $file_path );
9694
9795 if ( !-f $file_path ) {
9896 $ctx->response->has_passed(1);
102100 if ( !-r $file_path ) {
103101 return $self->response_403($ctx);
104102 }
103
104 # Now we are sure we can render the file...
105 $self->execute_hook( 'handler.file.before_render', $file_path );
105106
106107 # Read file content as bytes
107108 my $fh = open_file( "<", $file_path );
147148
148149 =head1 VERSION
149150
150 version 0.07
151 version 0.09
151152
152153 =head1 AUTHOR
153154
11
22 package Dancer2::Logger::Capture::Trap;
33 {
4 $Dancer2::Logger::Capture::Trap::VERSION = '0.07';
4 $Dancer2::Logger::Capture::Trap::VERSION = '0.09';
55 }
66 use Moo;
77 use Dancer2::Core::Types;
3737
3838 =head1 VERSION
3939
40 version 0.07
40 version 0.09
4141
4242 =head1 SYNOPSIS
4343
11
22 package Dancer2::Logger::Capture;
33 {
4 $Dancer2::Logger::Capture::VERSION = '0.07';
4 $Dancer2::Logger::Capture::VERSION = '0.09';
55 }
66 use Moo;
77 use Dancer2::Logger::Capture::Trap;
3636
3737 =head1 VERSION
3838
39 version 0.07
39 version 0.09
4040
4141 =head1 SYNOPSIS
4242
43 The basics:
44
4345 set logger => "capture";
4446
45 my $trap = Dancer2::Logger::Capture->trap;
47 my $trap = dancer_app->engine('logger')->trapper;
4648 my $logs = $trap->read;
4749
48 #a real-world example
49 use Test::More import => ['!pass'], tests => 2;
50 use Dancer2;
50 A worked-out real-world example:
51
52 use Test::More tests => 2;
53 use Dancer2 ':tests';
5154
5255 set logger => 'capture';
5356
5457 warning "Danger! Warning!";
5558 debug "I like pie.";
5659
57 my $trap = Dancer2::Logger::Capture->trap;
60 my $trap = dancer_app->engine('logger')->trapper;
61
5862 is_deeply $trap->read, [
5963 { level => "warning", message => "Danger! Warning!" },
6064 { level => "debug", message => "I like pie.", }
7882
7983 =head1 SEE ALSO
8084
81 L<Dancer2::Logger>, L<Dancer2::Logger::Capture::Trap>
85 L<Dancer2::Core::Role::Logger>, L<Dancer2::Logger::Capture::Trap>
8286
8387 =head1 AUTHOR
8488
11
22 package Dancer2::Logger::Console;
33 {
4 $Dancer2::Logger::Console::VERSION = '0.07';
4 $Dancer2::Logger::Console::VERSION = '0.09';
55 }
66 use Moo;
77 with 'Dancer2::Core::Role::Logger';
2323
2424 =head1 VERSION
2525
26 version 0.07
26 version 0.09
27
28 =head1 DESCRIPTION
29
30 This is a logging engine that allows you to print debug messages on the
31 standard error output.
32
33 =head1 METHODS
34
35 =head2 log
36
37 Writes the log message to the console.
38
39 =head1 CONFIGURATION
40
41 The setting C<logger> should be set to C<console> in order to use this logging
42 engine in a Dancer2 application.
43
44 There is no additional setting available with this engine.
45
46 =head1 METHODS
47
48 =head1 SEE ALSO
49
50 L<Dancer2::Core::Role::Logger>
2751
2852 =head1 AUTHOR
2953
11
22 package Dancer2::Logger::Diag;
33 {
4 $Dancer2::Logger::Diag::VERSION = '0.07';
4 $Dancer2::Logger::Diag::VERSION = '0.09';
55 }
66 use Moo;
77 use Test::More;
2626
2727 =head1 VERSION
2828
29 version 0.07
30
31 =head1 SYNOPSIS
29 version 0.09
3230
3331 =head1 DESCRIPTION
3432
11
22 package Dancer2::Logger::File;
33 {
4 $Dancer2::Logger::File::VERSION = '0.07';
4 $Dancer2::Logger::File::VERSION = '0.09';
55 }
66 use Carp 'carp';
77 use Moo;
8484
8585 =head1 VERSION
8686
87 version 0.07
87 version 0.09
8888
8989 =head1 DESCRIPTION
9090
11
22 package Dancer2::Logger::Note;
33 {
4 $Dancer2::Logger::Note::VERSION = '0.07';
4 $Dancer2::Logger::Note::VERSION = '0.09';
55 }
66 use Moo;
77 use Test::More;
2626
2727 =head1 VERSION
2828
29 version 0.07
29 version 0.09
3030
3131 =head1 DESCRIPTION
3232
11
22 package Dancer2::Logger::Null;
33 {
4 $Dancer2::Logger::Null::VERSION = '0.07';
4 $Dancer2::Logger::Null::VERSION = '0.09';
55 }
66 use Moo;
77 with 'Dancer2::Core::Role::Logger';
2121
2222 =head1 VERSION
2323
24 version 0.07
24 version 0.09
2525
2626 =head1 DESCRIPTION
2727
1010
1111 =head1 VERSION
1212
13 version 0.07
13 version 0.09
1414
1515 =head1 DESCRIPTION
1616
1919 It's a complete rewrite of L<Dancer>, based on L<Moo> and using a more robust
2020 and extensible fully-OO design.
2121
22 It's designed to be powerful and flexible, but very easy to use - getting up and
22 It's designed to be powerful and flexible, but also easy to use - getting up and
2323 running with your web app is trivial, and an ecosystem of adaptors for common
24 template engines, session storage and logging methods and plugins to make
24 template engines, session storage, logging methods and plugins to make
2525 common tasks easy mean you can do what you want to do, your way, easily.
2626
2727 =encoding utf8
143143 =head2 Route Handlers
144144
145145 The route action is the code reference declared. It can access parameters
146 through the `params' keyword, which returns a hashref.
146 through the C<params> keyword, which returns a hashref.
147147 This hashref is a merge of the route pattern matches and the request params.
148148
149149 You can have more details about how params are built and how to access them in
150 the L<Dancer2::Request> documentation.
150 the L<Dancer2::Core::Request> documentation.
151151
152152 =head3 Named Matching
153153
162162 Tokens can be optional, for example:
163163
164164 get '/hello/:name?' => sub {
165 "Hello there " . param('name') || "whoever you are!";
165 defined param('name') ? "Hello there ".param('name') : "whoever you are!";
166166 };
167167
168168 =head3 Wildcards Matching
169169
170 A route can contain a wildcard (represented by a '*'). Each wildcard match will
171 be returned in an arrayref, accessible via the `splat' keyword.
170 A route can contain a wildcard (represented by a C<*>). Each wildcard match will
171 be returned in an arrayref, accessible via the C<splat> keyword.
172172
173173 get '/download/*.*' => sub {
174174 my ($file, $ext) = splat;
240240 "I say a number: ".params->{number};
241241 };
242242
243 =head2 Load rules from external files
244
245 You can use the load method to include additional routes into your application:
246
247 get '/go/:value', sub {
248 # foo
249 };
250
251 load 'more_routes.pl';
252
253 # then, in the file more_routes.pl:
254 get '/yes', sub {
255 'orly?';
256 };
257
258 B<load> is just a wrapper for B<require>, but you can also specify a list of
259 routes files:
260
261 load 'login_routes.pl', 'session_routes.pl', 'misc_routes.pl';
262
263 =head2 Importing just the syntax
264
265 If you want to use more complex file hierarchies, you can import just the
266 syntax of Dancer2.
267
268 package App;
269
270 use Dancer2; # App may contain generic routes
271 use App::User::Routes; # user-related routes
272
273 Then in App/User/Routes.pm:
274
275 use Dancer2 ':syntax';
276
277 get '/user/view/:id' => sub {
278 ...
279 };
280
281243 =head2 Default Error Pages
282244
283245 When an error is rendered (the action responded with a status code different
320282
321283 hook before => sub {
322284 var note => 'Hi there';
323 request->path_info('/foo/oversee')
324285 };
325286
326287 get '/foo/*' => sub {
332293 give non-logged-in users a login page:
333294
334295 hook before => sub {
335 if (!session('user') && request->path_info !~ m{^/login}) {
296 if (!session('user') && request->dispatch_path !~ m{^/login}) {
336297 # Pass the original path requested along to the handler:
337 var requested_path => request->path_info;
338 request->path_info('/login');
298 forward '/login', { requested_path => request->dispatch_path };
339299 }
340300 };
341301
475435
476436 =head2 Serializers
477437
478 C<before_serializer> is called before serializing the content, and eceives as
438 C<before_serializer> is called before serializing the content, and receives as
479439 argument the content to serialize.
480440
481441 hook before_serializer => sub {
482 my @data = @_;
483 $response->content->{start_time} = time();
442 my $content = shift;
443 ...
484444 };
485445
486446 C<after_serializer> is called after the payload was serialized, and receives
487447 the serialized content as an argument.
488448
489 hook before_deserializer => sub {
449 hook after_serializer => sub {
490450 my $content = shift;
451 ...
491452 };
492453
493454 =head1 CONFIGURATION AND ENVIRONMENTS
554515
555516 More usefully, settings can be defined in a configuration file.
556517 Environment-specific settings can also be defined in environment-specific files
557 (for instance, you don't want auto_reload in production, and might want extra
518 (for instance, you do not want to show error stacktraces in production, and might want extra
558519 logging in development). See the cookbook for examples.
559520
560521 =head2 Serializers
797758
798759 =head2 cookies
799760
800 Accesses cookies values, it returns a HashRef of L<Dancer2::Cookie> objects:
761 Accesses cookies values, it returns a HashRef of L<Dancer2::Core::Cookie>
762 objects:
801763
802764 get '/some_action' => sub {
803765 my $cookie = cookies->{name};
930892 reached. If it was a B<GET>, it will remain a B<GET> (but if you do need to
931893 change the method, you can do so; read on below for details.)
932894
933 B<WARNING> : Issuing a forward immediately exits the current route,
934 and perform the forward. Thus, any code after a forward is ignored, until the
935 end of the route. e.g.
895 B<WARNING> : Issuing a forward will B<NOT> exits the current
896 route. Unlike with Dancer 1, in Dancer 2 you need to explicitly return
897 from the route. This keeps up with the behavior of C<redirect>.
936898
937899 get '/foo/:article_id' => sub {
938900 if ($condition) {
939901 forward "/articles/" . params->{article_id};
940 # The following code is never executed
902 # The following code WILL BE executed
941903 do_stuff();
942904 }
943905
944906 more_stuff();
945907 };
946
947 So it's not necessary anymore to use C<return> with forward.
948908
949909 Note that forward doesn't parse GET arguments. So, you can't use
950910 something like:
966926
967927 Deserializes a Data::Dumper structure.
968928
969 =head2 from_json ($structure, %options)
929 =head2 from_json ($structure, \%options)
970930
971931 Deserializes a JSON structure. Can receive optional arguments. Those arguments
972932 are valid L<JSON> arguments to change the behaviour of the default
975935 =head2 from_yaml ($structure)
976936
977937 Deserializes a YAML structure.
978
979 =head2 from_xml ($structure, %options)
980
981 Deserializes a XML structure. Can receive optional arguments. These arguments
982 are valid L<XML::Simple> arguments to change the behaviour of the default
983 C<XML::Simple::XMLin> function.
984938
985939 =head2 get
986940
10491003 Adds a hook at some position. For example :
10501004
10511005 hook before_serializer => sub {
1052 my $response = shift;
1053 $response->content->{generated_at} = localtime();
1006 my $content = shift;
1007 ...
10541008 };
10551009
10561010 There can be multiple hooks assigned to a given position, and each will be
10571011 executed in order.
10581012
1059 All the hooks are documented in L<Dancer2::Manual::Hooks>.
1060
10611013 =head2 info
10621014
10631015 Logs a message of info level:
10731025 sugar around Perl's C<require>:
10741026
10751027 load 'UserActions.pl', 'AdminActions.pl';
1076
1077 =head2 load_app
1078
1079 Loads a Dancer package. This method sets the libdir to the current C<./lib>
1080 directory:
1081
1082 # if we have lib/Webapp.pm, we can load it like:
1083 load_app 'Webapp';
1084 # or with options
1085 load_app 'Forum', prefix => '/forum', settings => {foo => 'bar'};
1086
1087 Note that the package loaded using load_app B<must> import Dancer with the
1088 C<:syntax> option.
1089
1090 To load multiple apps repeat load_app:
1091
1092 load_app 'one';
1093 load_app 'two';
10941028
10951029 =head2 mime
10961030
14931427 # destroy session
14941428 get '/logout' => sub {
14951429 ...
1496 session->destroy;
1430 context->destroy_session;
14971431 ...
14981432 };
14991433
16361570
16371571 Serializes a structure with Data::Dumper.
16381572
1639 =head2 to_json ($structure, %options)
1573 Calling this function will B<not> trigger the serialization's hooks.
1574
1575 =head2 to_json ($structure, \%options)
16401576
16411577 Serializes a structure to JSON. Can receive optional arguments. Thoses arguments
16421578 are valid L<JSON> arguments to change the behaviour of the default
16431579 C<JSON::to_json> function.
16441580
1581 Calling this function will B<not> trigger the serialization's hooks.
1582
16451583 =head2 to_yaml ($structure)
16461584
16471585 Serializes a structure to YAML.
16481586
1649 =head2 to_xml ($structure, %options)
1650
1651 Serializes a structure to XML. Can receive optional arguments. Thoses arguments
1652 are valid L<XML::Simple> arguments to change the behaviour of the default
1653 C<XML::Simple::XMLout> function.
1587 Calling this function will B<not> trigger the serialization's hooks.
16541588
16551589 =head2 true
16561590
+0
-192
lib/Dancer2/ModuleLoader.pm less more
0 package Dancer2::ModuleLoader;
1 {
2 $Dancer2::ModuleLoader::VERSION = '0.07';
3 }
4
5 # ABSTRACT: Dynamic module loading helpers for Dancer2 core components
6
7 use strict;
8 use warnings;
9
10 use Module::Runtime qw/ use_module /;
11
12
13 sub load {
14 my ( $class, $module, $version ) = @_;
15
16 $class->require( $module, $version );
17
18 # normal 'use', can be done via require + import
19 my ( $res, $error ) = $class->load_with_params($module);
20 return wantarray ? ( $res, $error ) : $res;
21 }
22
23
24 sub require {
25 my ( $class, $module, $version ) = @_;
26
27 eval {
28 defined $version
29 ? use_module( $module, $version )
30 : use_module($module);
31 }
32 or return wantarray ? ( 0, $@ ) : 0;
33
34 return 1;
35 }
36
37
38 sub load_with_params {
39 my ( $class, $module, @args ) = @_;
40 my ( $res, $error ) = $class->require($module);
41 $res or return wantarray ? ( 0, $error ) : 0;
42
43 # From perlfunc : If no "import" method can be found then the call is
44 # skipped, even if there is an AUTOLOAD method.
45 if ( $module->can('import') ) {
46
47 # bump Exporter Level to import symbols in the caller
48 local $Exporter::ExportLevel = ( $Exporter::ExportLevel || 0 ) + 1;
49 local $@;
50 eval { $module->import(@args) };
51 my $error = $@;
52 $error and return wantarray ? ( 0, $error ) : 0;
53 }
54 return 1;
55 }
56
57
58 sub use_lib {
59 my ( $class, @args ) = @_;
60 use lib;
61 local $@;
62 lib->import(@args);
63 my $error = $@;
64 $error and return wantarray ? ( 0, $error ) : 0;
65 return 1;
66 }
67
68 1;
69
70 __END__
71
72 =pod
73
74 =head1 NAME
75
76 Dancer2::ModuleLoader - Dynamic module loading helpers for Dancer2 core components
77
78 =head1 VERSION
79
80 version 0.07
81
82 =head1 DESCRIPTION
83
84 Sometimes in Dancer2 core we need to use modules, but we don't want to declare
85 them all in advance in compile-time. These could be because the specific modules
86 provide extra features which depend on code that isn't (and shouldn't) be in
87 core, or perhaps because we only want these components loaded in lazy style,
88 saving loading time a bit.
89
90 To do such things takes a bit of code for localizing C<$@> and C<eval>ing. That
91 code has been refactored into this module to help Dancer2 core developers.
92
93 B<Please only use this for Dancer2 core modules>. If you're writing an external
94 Dancer2 module (L<Dancer2::Template::Tiny>, L<Dancer2::Session::Cookie>, etc.),
95 please simply "C<use ModuleYouNeed>" in your code and don't use this module.
96
97 WARNING, all the following methods are CLASS methods.
98
99 =head1 METHODS
100
101 =head2 load
102
103 Runs a "C<use ModuleYouNeed>".
104
105 use Dancer2::ModuleLoader;
106 ...
107 Dancer2::ModuleLoader->load('Something')
108 or die "Couldn't load Something\n";
109
110 # load version 5.0 or more
111 Dancer2::ModuleLoader->load('Something', '5.0')
112 or die "Couldn't load Something\n";
113
114 # load version 5.0 or more
115 my ($res, $error) = Dancer2::ModuleLoader->load('Something', '5.0');
116 $res or die "Couldn't load Something : '$error'\n";
117
118 Takes in arguments the module name, and optionally the minimum version number required.
119
120 In scalar context, returns 1 if successful, 0 if not.
121 In list context, returns 1 if successful, C<(0, "error message")> if not.
122
123 If you need to give arguments to the loading module, please use the method C<load_with_params>
124
125 =head2 require
126
127 Runs a "C<require ModuleYouNeed>".
128
129 use Dancer2::ModuleLoader;
130 ...
131 Dancer2::ModuleLoader->require('Something')
132 or die "Couldn't require Something\n";
133 my ($res, $error) = Dancer2::ModuleLoader->require('Something');
134 $res or die "Couldn't require Something : '$error'\n";
135
136 If you are unsure what you need (C<require> or C<load>), learn the differences
137 between C<require> and C<use>.
138
139 Takes in arguments the module name, and an optional version.
140
141 In scalar context, returns 1 if successful, 0 if not.
142 In list context, returns 1 if successful, C<(0, "error message")> if not.
143
144 =head2 load_with_params
145
146 Runs a "C<use ModuleYouNeed qw(param1 param2 ...)>".
147
148 use Dancer2::ModuleLoader;
149 ...
150 Dancer2::ModuleLoader->load('Something', qw(param1 param2) )
151 or die "Couldn't load Something\n";
152
153 my ($res, $error) = Dancer2::ModuleLoader->load('Something', @params);
154 $res or die "Couldn't load Something : '$error'\n";
155
156 Takes in arguments the module name, and optionally parameters to pass to the import internal method.
157
158 In scalar context, returns 1 if successful, 0 if not.
159 In list context, returns 1 if successful, C<(0, "error message")> if not.
160
161 =head2 use_lib
162
163 Runs a "C<use lib qw(path1 path2)>" at run time instead of compile time.
164
165 use Dancer2::ModuleLoader;
166 ...
167 Dancer2::ModuleLoader->use_lib('path1', @other_paths)
168 or die "Couldn't perform use lib\n";
169
170 my ($res, $error) = Dancer2::ModuleLoader->use_lib('path1', @other_paths);
171 $res or die "Couldn't perform use lib : '$error'\n";
172
173 Takes in arguments a list of path to be prepended to C<@INC>, in a similar way
174 than C<use lib>. However, this is performed at run time, so the list of paths
175 can be generated and dynamic.
176
177 In scalar context, returns 1 if successful, 0 if not.
178 In list context, returns 1 if successful, C<(0, "error message")> if not.
179
180 =head1 AUTHOR
181
182 Dancer Core Developers
183
184 =head1 COPYRIGHT AND LICENSE
185
186 This software is copyright (c) 2013 by Alexis Sukrieh.
187
188 This is free software; you can redistribute it and/or modify it under
189 the same terms as the Perl 5 programming language system itself.
190
191 =cut
11
22 package Dancer2::Plugin::Ajax;
33 {
4 $Dancer2::Plugin::Ajax::VERSION = '0.07';
4 $Dancer2::Plugin::Ajax::VERSION = '0.09';
55 }
66
77 use strict;
6262
6363 =head1 VERSION
6464
65 version 0.07
65 version 0.09
6666
6767 =head1 SYNOPSIS
6868
00 package Dancer2::Plugin;
11 {
2 $Dancer2::Plugin::VERSION = '0.07';
2 $Dancer2::Plugin::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Extending Dancer2's DSL with plugins
2929 . " (it should match ^[a-zA-Z_]+[a-zA-Z0-9_]*$ )";
3030
3131 if (grep { $_ eq $keyword }
32 map { s/^(?:\$|%|&|@|\*)//; $_ }
33 ( map { $_->[0] } @{ Dancer2::Core::DSL->dsl_keywords } )
32 keys %{ Dancer2::Core::DSL->dsl_keywords }
3433 )
3534 {
3635 croak "You can't use '$keyword', this is a reserved keyword";
209208 # their first argument).
210209 # These modified versions of the DSL are then exported in the namespace of the
211210 # plugin.
212 for my $symbol ( $dsl->dsl_keywords_as_list ) {
211 for my $symbol ( keys %{ $dsl->keywords } ) {
213212
214213 # get the original symbol from the real DSL
215214 no strict 'refs';
216 no warnings 'redefine';
215 no warnings qw( redefine once );
217216 my $code = *{"Dancer2::Core::DSL::$symbol"}{CODE};
218217
219218 # compile it with $caller->dsl
260259
261260 =head1 VERSION
262261
263 version 0.07
262 version 0.09
264263
265264 =head1 DESCRIPTION
266265
289288
290289 The coderef receives as its first argument the Dancer2::Core::DSL object.
291290
292 Instead of importing C<use Dancer ':syntax'>, plugins B<must> use the DSL
293 object to access application components and work with them directly.
291 Plugins B<must> use the DSL object to access application components and work
292 with them directly.
294293
295294 sub {
296295 my $dsl = shift;
1010
1111 =head1 VERSION
1212
13 version 0.07
13 version 0.09
1414
1515 =head1 DESCRIPTION
1616
11
22 package Dancer2::Serializer::Dumper;
33 {
4 $Dancer2::Serializer::Dumper::VERSION = '0.07';
4 $Dancer2::Serializer::Dumper::VERSION = '0.09';
55 }
66
77 use Moo;
1010
1111 with 'Dancer2::Core::Role::Serializer';
1212
13 has '+content_type' => ( default => 'text/x-data-dumper' );
1314
1415 # helpers
1516 sub from_dumper {
2526 # class definition
2627 sub loaded {1}
2728
28
2929 sub serialize {
3030 my ( $self, $entity ) = @_;
3131
3535 }
3636 }
3737
38
3938 sub deserialize {
4039 my ( $self, $content ) = @_;
4140
4342 croak "unable to deserialize : $@" if $@;
4443 return $res;
4544 }
46
47
48 sub content_type {'text/x-data-dumper'}
4945
5046 1;
5147
5955
6056 =head1 VERSION
6157
62 version 0.07
63
64 =head1 SYNOPSIS
58 version 0.09
6559
6660 =head1 DESCRIPTION
6761
68 Turn Perl data structures into L<Data::Dumper> output and vice-versa.
62 This is a serializer engine that allows you to turn Perl data structures into
63 L<Data::Dumper> output and vice-versa.
64
65 =head1 ATTRIBUTES
66
67 =head2 content_type
68
69 Returns 'text/x-data-dumper'
6970
7071 =head1 METHODS
7172
72 =head2 serialize
73 =head2 serialize($content)
7374
74 Serialize a Perl data structure into a Dumper string.
75 Serializes a Perl data structure into a Dumper string.
7576
76 =head2 deserialize
77 =head2 deserialize($content)
7778
78 Deserialize a Dumper string into a Perl data structure
79 Deserialize a Dumper string into a Perl data structure.
7980
80 =head2 content_type
81 =head1 FUNCTIONS
8182
82 Return 'text/x-data-dumper'
83 =head2 from_dumper($content)
84
85 This is an helper available to transform a L<Data::Dumper> output to a Perl
86 data structures.
87
88 =head2 to_dumper($content)
89
90 This is an helper available to transform a Perl data structures to a
91 L<Data::Dumper> output.
92
93 Calling this function will B<not> trigger the serialization's hooks.
94
95 =head1 METHODS
8396
8497 =head1 AUTHOR
8598
11
22 package Dancer2::Serializer::JSON;
33 {
4 $Dancer2::Serializer::JSON::VERSION = '0.07';
4 $Dancer2::Serializer::JSON::VERSION = '0.09';
55 }
66 use Moo;
77 use JSON ();
88
99 with 'Dancer2::Core::Role::Serializer';
1010
11 has '+content_type' => ( default => 'application/json' );
1112
1213 # helpers
1314 sub from_json {
2223
2324 # class definition
2425 sub loaded {1}
25
2626
2727 sub serialize {
2828 my ( $self, $entity, $options ) = @_;
4141 JSON::to_json( $entity, $options );
4242 }
4343
44
4544 sub deserialize {
4645 my ( $self, $entity, $options ) = @_;
4746
4847 $options->{utf8} = 1 if !defined $options->{utf8};
4948 JSON::from_json( $entity, $options );
5049 }
51
52
53 sub content_type {'application/json'}
5450
5551 1;
5652
6460
6561 =head1 VERSION
6662
67 version 0.07
68
69 =head1 SYNOPSIS
63 version 0.09
7064
7165 =head1 DESCRIPTION
7266
73 Turn Perl data structures into JSON output and vice-versa.
67 This is a serializer engine that allows you to turn Perl data structures into
68 JSON output and vice-versa.
69
70 =head1 ATTRIBUTES
71
72 =head2 content_type
73
74 Returns 'application/json'
7475
7576 =head1 METHODS
7677
77 =head2 serialize
78 =head2 serialize($content)
7879
79 Serialize a Perl data structure into a JSON string.
80 Serializes a Perl data structure into a JSON string.
8081
81 =head2 deserialize
82 =head2 deserialize($content)
8283
83 Deserialize a JSON string into a Perl data structure
84 Deserializes a JSON string into a Perl data structure.
8485
85 =head2 content_type
86 =head1 FUNCTIONS
8687
87 return 'application/json'
88 =head2 from_json($content, \%options)
89
90 This is an helper available to transform a JSON data structure to a Perl data structures.
91
92 =head2 to_json($content, \%options)
93
94 This is an helper available to transform a Perl data structure to JSON.
95
96 Calling this function will B<not> trigger the serialization's hooks.
97
98 =head1 METHODS
8899
89100 =head1 AUTHOR
90101
11
22 package Dancer2::Serializer::YAML;
33 {
4 $Dancer2::Serializer::YAML::VERSION = '0.07';
4 $Dancer2::Serializer::YAML::VERSION = '0.09';
55 }
66 use Moo;
77 use Carp 'croak';
88 with 'Dancer2::Core::Role::Serializer';
9
10 has '+content_type' => ( default => 'text/x-yaml' );
911
1012 # helpers
1113
3638 YAML::Any::Load($content);
3739 }
3840
39 sub content_type {'text/x-yaml'}
40
4141 1;
4242
4343 __END__
5050
5151 =head1 VERSION
5252
53 version 0.07
54
55 =head1 SYNOPSIS
53 version 0.09
5654
5755 =head1 DESCRIPTION
5856
59 =head1 METHODS
57 This is a serializer engine that allows you to turn Perl data structures into
58 YAML output and vice-versa.
6059
61 =head2 serialize
62
63 Serialize a data structure to a YAML structure.
64
65 =head2 deserialize
66
67 Deserialize a YAML structure to a data structure
60 =head1 ATTRIBUTES
6861
6962 =head2 content_type
7063
71 Return 'text/x-yaml'
64 Returns 'text/x-yaml'
65
66 =head1 METHODS
67
68 =head2 serialize($content)
69
70 Serializes a data structure to a YAML structure.
71
72 =head2 deserialize($content)
73
74 Deserializes a YAML structure to a data structure.
75
76 =head1 FUNCTIONS
77
78 =head2 fom_yaml($content)
79
80 This is an helper available to transform a YAML data structure to a Perl data structures.
81
82 =head2 to_yaml($content)
83
84 This is an helper available to transform a Perl data structure to YAML.
85
86 Calling this function will B<not> trigger the serialization's hooks.
87
88 =head1 METHODS
7289
7390 =head1 AUTHOR
7491
11
22 package Dancer2::Session::Simple;
33 {
4 $Dancer2::Session::Simple::VERSION = '0.07';
4 $Dancer2::Session::Simple::VERSION = '0.09';
55 }
66 use Moo;
77 use Dancer2::Core::Types;
5050
5151 =head1 VERSION
5252
53 version 0.07
53 version 0.09
5454
5555 =head1 DESCRIPTION
5656
00 package Dancer2::Session::YAML;
11 {
2 $Dancer2::Session::YAML::VERSION = '0.07';
2 $Dancer2::Session::YAML::VERSION = '0.09';
33 }
44
55 # ABSTRACT: YAML-file-based session backend for Dancer2
3939
4040 =head1 VERSION
4141
42 version 0.07
42 version 0.09
4343
4444 =head1 DESCRIPTION
4545
00 package Dancer2::Template::Implementation::ForkedTiny;
11 {
2 $Dancer2::Template::Implementation::ForkedTiny::VERSION = '0.07';
2 $Dancer2::Template::Implementation::ForkedTiny::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Dancer2 own implementation of Template::Tiny
229229
230230 =head1 VERSION
231231
232 version 0.07
232 version 0.09
233233
234234 =head1 SYNOPSIS
235235
11
22 package Dancer2::Template::Simple;
33 {
4 $Dancer2::Template::Simple::VERSION = '0.07';
4 $Dancer2::Template::Simple::VERSION = '0.09';
55 }
66 use strict;
77 use warnings;
8 use Carp;
9
108 use Moo;
119 use Dancer2::FileUtils 'read_file_content';
1210
1311 with 'Dancer2::Core::Role::Template';
14
1512
1613 has start_tag => (
1714 is => 'rw',
159156
160157 =head1 VERSION
161158
162 version 0.07
159 version 0.09
160
161 =head1 SYNOPSIS
162
163 To use this engine, you may configure L<Dancer2> via C<config.yaml>:
164
165 template: simple
163166
164167 =head1 DESCRIPTION
165168
171174 template processing.
172175
173176 If you want to power an application with Dancer2 in production environment, it's
174 strongly advised to switch to Dancer2::Template::TemplateToolkit.
177 strongly advised to switch to L<Dancer2::Template::TemplateToolkit>.
178
179 =head1 METHODS
180
181 =head2 render($template, \%tokens)
182
183 Renders the template. The first arg is a filename for the template file
184 or a reference to a string that contains the template. The second arg
185 is a hashref for the tokens that you wish to pass to
186 L<Template::Toolkit> for rendering.
175187
176188 =head1 SYNTAX
177189
178 A template written for Dancer2::Template::Simple should be working just fine with
179 Dancer2::Template::TemplateToolkit. The opposite is not true though.
190 A template written for C<Dancer2::Template::Simple> should be working just fine
191 with L<Dancer2::Template::TemplateToolkit>. The opposite is not true though.
180192
181193 =over 4
182194
186198
187199 <% var1 %>
188200
189 If 'var1' exists in the tokens hash given, its value will be written there.
201 If B<var1> exists in the tokens hash given, its value will be written there.
190202
191203 =back
192204
193205 =head1 SEE ALSO
194206
195 L<Dancer2>, L<Dancer2::Template>
207 L<Dancer2>, L<Dancer2::Core::Role::Template>,
208 L<Dancer2::Template::TemplateToolkit>.
196209
197210 =head1 AUTHOR
198211
11
22 package Dancer2::Template::TemplateToolkit;
33 {
4 $Dancer2::Template::TemplateToolkit::VERSION = '0.07';
4 $Dancer2::Template::TemplateToolkit::VERSION = '0.09';
55 }
66
77 use strict;
88 use warnings;
9 use Carp;
9 use Carp qw/croak/;
1010 use Moo;
1111 use Dancer2::Core::Types;
1212 use Template;
1313
1414 with 'Dancer2::Core::Role::Template';
15
1615
1716 has '+engine' => ( isa => InstanceOf ['Template'], );
1817
3837 return Template->new(%tt_config);
3938 }
4039
41
4240 sub render {
4341 my ( $self, $template, $tokens ) = @_;
4442
45 if ( !ref $template ) {
46 -f $template
47 or croak "'$template' doesn't exist or not a regular file";
48 }
43 ( ref $template || -f $template )
44 or croak "$template is not a regular file or reference";
4945
5046 my $content = "";
5147 my $charset = $self->charset;
6763
6864 =head1 VERSION
6965
70 version 0.07
66 version 0.09
7167
7268 =head1 SYNOPSIS
7369
8177 # code code code
8278 set template => 'template_toolkit';
8379
80 =head1 DESCRIPTION
81
82 This template engine allows you to use L<Template::Toolkit> in L<Dancer2>.
83
8484 =head1 METHODS
8585
86 =head2 render TEMPLATE, TOKENS
86 =head2 render($template, \%tokens)
8787
8888 Renders the template. The first arg is a filename for the template file
8989 or a reference to a string that contains the template. The second arg
9090 is a hashref for the tokens that you wish to pass to
9191 L<Template::Toolkit> for rendering.
92
93 =head1 SEE ALSO
94
95 L<Dancer2>, L<Dancer2::Core::Role::Template>, L<Template::Toolkit>.
9296
9397 =head1 AUTHOR
9498
00 package Dancer2::Template::Tiny;
11 {
2 $Dancer2::Template::Tiny::VERSION = '0.07';
2 $Dancer2::Template::Tiny::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Template::Tiny engine for Dancer2
66
77 use strict;
88 use warnings;
9 use Carp;
9 use Carp qw/croak/;
1010 use Moo;
1111 use Dancer2::Core::Types;
1212 use Dancer2::Template::Implementation::ForkedTiny;
1313 use Dancer2::FileUtils 'read_file_content';
1414
1515 with 'Dancer2::Core::Role::Template';
16
1716
1817 has '+engine' =>
1918 ( isa => InstanceOf ['Dancer2::Template::Implementation::ForkedTiny'], );
2221 Dancer2::Template::Implementation::ForkedTiny->new( %{ $_[0]->config } );
2322 }
2423
25
2624 sub render {
2725 my ( $self, $template, $tokens ) = @_;
2826
2927 ( ref $template || -f $template )
30 or die "$template is not a regular file or reference";
28 or croak "$template is not a regular file or reference";
3129
3230 my $template_data =
3331 ref $template
5452
5553 =head1 VERSION
5654
57 version 0.07
55 version 0.09
5856
5957 =head1 SYNOPSIS
6058
9391
9492 Since L<Dancer2> has internal support for a wrapper-like option with the
9593 C<layout> configuration option, you can have a L<Template::Toolkit>-like WRAPPER
96 even though L<Template::Tiny> doesn't really support it. :)
94 even though L<Template::Tiny> doesn't really support it.
9795
9896 =head1 METHODS
9997
100 =head2 render
98 =head2 render($template, \%tokens)
10199
102 Renders the template. Accepts a string to a file or a reference to a string of
103 the template.
100 Renders the template. The first arg is a filename for the template file
101 or a reference to a string that contains the template. The second arg
102 is a hashref for the tokens that you wish to pass to
103 L<Template::Toolkit> for rendering.
104
105 =head1 SEE ALSO
106
107 L<Dancer2>, L<Dancer2::Core::Role::Template>, L<Template::Tiny>,
108 L<Dancer2::Template::Implementation::ForkedTiny>.
104109
105110 =head1 AUTHOR
106111
11
22 package Dancer2::Test;
33 {
4 $Dancer2::Test::VERSION = '0.07';
4 $Dancer2::Test::VERSION = '0.09';
55 }
66 use strict;
77 use warnings;
462462 }
463463
464464 # register the apps to the test dispatcher
465 $_dispatcher->apps( [ map { $_->dancer_app } @applications ] );
465 $_dispatcher->apps(
466 [ map {
467 $_->dancer_app->finish();
468 $_->dancer_app;
469 } @applications
470 ]
471 );
466472
467473 $class->export_to_level( 1, $class, @EXPORT );
468474 }
565571
566572 =head1 VERSION
567573
568 version 0.07
574 version 0.09
569575
570576 =head1 DESCRIPTION
571577
601607 state of the application and cause Schrodinger's cat to die.
602608
603609 my $response = dancer_response POST => '/widgets';
604 is $response->{status}, 202, "response for POST /widgets is 202";
605 is $response->{content}, "Widget #1 has been scheduled for creation",
610 is $response->status, 202, "response for POST /widgets is 202";
611 is $response->content, "Widget #1 has been scheduled for creation",
606612 "response content looks good for first POST /widgets";
607613
608614 $response = dancer_response POST => '/widgets';
609 is $response->{status}, 202, "response for POST /widgets is 202";
610 is $response->{content}, "Widget #2 has been scheduled for creation",
615 is $response->status, 202, "response for POST /widgets is 202";
616 is $response->content, "Widget #2 has been scheduled for creation",
611617 "response content looks good for second POST /widgets";
612618
613619 It's possible to test file uploads:
1010
1111 =head1 VERSION
1212
13 version 0.07
13 version 0.09
1414
1515 =head1 What is Dancer2?
1616
4444
4545 So "Dancr" was born.
4646
47 Dancr is a simple "micro" blog which uses the L<SQLite|http://www.sqlite.org> database engine for simplicity's sake.
47 Dancr is a simple "micro" blog which uses the L<SQLite|http://www.sqlite.org>
48 database engine for simplicity's sake. (You'll need to install sqlite for Dancr
49 if you don't have it installed already.)
4850
4951 =head1 Required perl modules
5052
5759
5860 We're not going to spend a lot of time on the database, as it's not really the point of this particular
5961 tutorial.
62 Open your favorite L<text editor|http://www.vim.org> and create a schema
63 definition called 'schema.sql' as follows:
6064
6165 create table if not exists entries (
6266 id integer primary key autoincrement,
6771 Here we have a single table with three columns: id, title, and text. The 'id' field is the primary key and will
6872 automatically get an ID assigned by the database engine when a row is inserted.
6973
70 We want our application to initialize the database automatically for us when we start it, so open your favorite
71 L<text editor|http://www.vim.org> and create a file called 'dancr.pl'. We're going to put the following subroutines
72 in that file:
74 We want our application to initialize the database automatically for us when we
75 start it, so next, create a file called 'dancr.pl'. (The entire file is
76 listed below, so don't worry about copying each of these fragments into
77 'dancr.pl' as you read through this document.) We're going to put the following
78 subroutines in that file:
7379
7480 sub connect_db {
7581 my $dbh = DBI->connect("dbi:SQLite:dbname=".setting('database')) or
107113 };
108114 };
109115
110 As you can see, the handler is created by specifying the HTTP verb 'get' and
111 the URL to match, '/' and finally a subroutine to do something once those
116 As you can see, the handler is created by specifying the HTTP verb 'get',
117 the '/' URL to match, and finally, a subroutine to do something once those
112118 conditions have been satisfied. Something you might not notice right away is
113119 the semicolon at the end of the route handler. Since the subroutine actually
114120 is a coderef, it requires a semicolon.
245251 configuration directives toward the top of our dancr.pl file. But there are
246252 more options than just the session engine we want to set.
247253
254 set 'database' => File::Spec->catfile(File::Spec->tmpdir(), 'dancr.db');
248255 set 'session' => 'Simple';
249256 set 'template' => 'template_toolkit';
250257 set 'logger' => 'console';
342349 logout procedure.
343350
344351 get '/logout' => sub {
345 session->destroy;
352 context->destroy_session;
346353 set_flash('You are logged out.');
347354 redirect '/';
348355 };
349356
350 C<session-E<gt>destroy;> is Dancer2's way to remove a stored session. We notify
351 the user she is logged out and route her back to the root URL once again.
357 C<context-E<gt>destroy_session;> is Dancer2's way to remove a stored session.
358 We notify the user she is logged out and route her back to the root URL
359 once again.
352360
353361 =head1 Layout and static files
354362
431439 time since you only need to update the values in this one place instead of
432440 everywhere you render a template.
433441
434 before_template sub {
442 hook before_template => sub {
435443 my $tokens = shift;
436444
437445 $tokens->{'css_url'} = request->base . 'css/style.css';
494502 $db->do($schema) or die $db->errstr;
495503 }
496504
497 before_template sub {
505 hook before_template => sub {
498506 my $tokens = shift;
499507
500508 $tokens->{'css_url'} = request->base . 'css/style.css';
554562 };
555563
556564 get '/logout' => sub {
557 session->destroy;
565 context->destroy_session;
558566 set_flash('You are logged out.');
559567 redirect '/';
560568 };
606614
607615 The CSS stylesheet is copied verbatim from the Flaskr example application and is subject to their license:
608616
609 Copyright (c) 2010 by Armin Ronacher and contributors.
617 Copyright (c) 2010, 2013 by Armin Ronacher and contributors.
610618
611619 Some rights reserved.
612620
00 package Dancer2;
11 {
2 $Dancer2::VERSION = '0.07';
2 $Dancer2::VERSION = '0.09';
33 }
44
55 # ABSTRACT: Lightweight yet powerful web application framework
66
77 use strict;
88 use warnings;
9 use Data::Dumper;
9 use Class::Load 'load_class';
10 use Dancer2::Core;
1011 use Dancer2::Core::Runner;
1112 use Dancer2::Core::App;
1213 use Dancer2::FileUtils;
13 use Dancer2::ModuleLoader;
1414
1515 our $AUTHORITY = 'SUKRIA';
1616
1717 # set version in dist.ini now
1818 # but we still need a basic version for
1919 # the tests
20 $Dancer2::VERSION ||= '0.06'; # 2.0.6
21
22
23 my $runner;
20 $Dancer2::VERSION ||= '0.09'; # 2.0.9
21
22
23 our $runner;
2424
2525 sub runner {$runner}
2626
3333 utf8->import;
3434
3535 my @final_args;
36 my $syntax_only = 0;
37 my $as_script = 0;
36 my $as_script = 0;
3837 foreach (@args) {
3938 if ( $_ eq ':tests' ) {
4039 push @final_args, '!pass' => 1;
41 }
42 elsif ( $_ eq ':syntax' ) {
43 $syntax_only = 1;
4440 }
4541 elsif ( $_ eq ':script' ) {
4642 $as_script = 1;
6864 $runner = Dancer2::Core::Runner->new( caller => $script, );
6965 }
7066
71 my $local_libdir = Dancer2::FileUtils::path( $runner->location, 'lib' );
72 Dancer2::ModuleLoader->use_lib($local_libdir) if -d $local_libdir;
67 my $server = $runner->server;
7368
7469 # the app object
70 # populating with the server's postponed hooks in advance
7571 my $app = Dancer2::Core::App->new(
7672 name => $caller,
7773 environment => $runner->environment,
7874 location => $runner->location,
7975 runner_config => $runner->config,
80 postponed_hooks => $runner->postponed_hooks,
76 postponed_hooks => $server->postponed_hooks,
8177 );
8278
83 core_debug("binding import method to $caller");
79 Dancer2::Core::debug("binding import method to $caller");
8480 _set_import_method_to_caller($caller);
8581
8682 # register the app within the runner instance
87 core_debug("binding app to $caller");
88 $runner->server->register_application($app);
89
90 core_debug("exporting DSL symbols for $caller");
83 Dancer2::Core::debug("binding app to $caller");
84 $server->register_application($app);
85
86 Dancer2::Core::debug("exporting DSL symbols for $caller");
9187
9288 # load the DSL, defaulting to Dancer2::Core::DSL
93 Dancer2::ModuleLoader->require( $final_args{dsl} )
94 or die "Couldn't require '" . $final_args{dsl} . "'\n";
89 load_class( $final_args{dsl} );
9590 my $dsl = $final_args{dsl}->new( app => $app );
9691 $dsl->export_symbols_to( $caller, \%final_args );
9792
98 #
99 # # if :syntax option exists, don't change settings
100 # return if $syntax_only;
10193 #
10294 # $as_script = 1 if $ENV{PLACK_ENV};
10395 #
123115 }
124116 }
125117
126
127 sub core_debug {
128 return unless $ENV{DANCER_DEBUG_CORE};
129
130 my $msg = shift;
131 my (@stuff) = @_;
132
133 my $vars = @stuff ? Dumper( \@stuff ) : '';
134
135 my ( $package, $filename, $line ) = caller;
136
137 chomp $msg;
138 print STDERR "core: $msg\n$vars";
139 }
140
141118 1;
142119
143120 __END__
150127
151128 =head1 VERSION
152129
153 version 0.07
130 version 0.09
154131
155132 =head1 DESCRIPTION
156133
224201 you control over the keywords that will be imported into your webapp and other
225202 things:
226203
227 use Dancer2 ':syntax';
204 use Dancer2 ':script';
228205
229206 =head3 Import Options
230207
235212 No importing of C<pass> function. This is to prevent conflict with
236213 L<Test::More> et al.
237214
238 =item C<:syntax>
239
240 Imports syntax only instead of treating your code as a script with command line
241 parameter parsing and built-in web server.
242
243215 =item C<:script>
244216
245217 Do not process arguments.
251223 =head2 my $runner=runner();
252224
253225 Returns the current runner. It is of type L<Dancer2::Core::Runner>.
254
255 =head2 core_debug
256
257 Output a message to STDERR and take further arguments as some data structures using
258 L<Data::Dumper>
259226
260227 =head1 AUTHOR
261228
4545 my $DANCER_APP_DIR = get_application_path($path, $name);
4646 my $DANCER_SCRIPT = get_script_path($name);
4747 my ($LIB_FILE, $LIB_PATH) = get_lib_path($name);
48
49 my $AUTO_RELOAD = eval "require Module::Refresh and require Clone" ? 1 : 0;
5048
5149 require Dancer2;
5250 my $DANCER_VERSION = $Dancer2::VERSION;
344342 clean => { FILES => '$cleanfiles-*' },
345343 );
346344 ",
347 'index.tt' =>
348 '
349 <!--
350 Credit goes to the Ruby on Rails team for this page
351 has been heavily based on the default Rails page that is
345 'index.tt' =>
346 '
347 <!--
348 Credit goes to the Ruby on Rails team for this page
349 has been heavily based on the default Rails page that is
352350 built with a scaffolded application.
353351
354352 Thanks a lot to them for their work.
369367 <li><a href="https://github.com/PerlDancer/Dancer2/">GitHub Community</a></li>
370368 </ul>
371369 </li>
372
370
373371 <li>
374372 <h3>Browse the documentation</h3>
375373
408406 <div id="getting-started">
409407 <h1>Getting started</h1>
410408 <h2>Here&rsquo;s how to get dancing:</h2>
411
409
412410 <h3><a href="#" id="about_env_link">About your application\'s environment</a></h3>
413411
414412 <div id="about-content" style="display: none;">
456454 </script>
457455
458456
459 <ol>
457 <ol>
460458 <li>
461459 <h2>Tune your application</h2>
462460
474472 <p>
475473 The default route that displays this page can be removed,
476474 it\'s just here to help you get started. The template used to
477 generate this content is located in
475 generate this content is located in
478476 <code>views/index.tt</code>.
479 You can add some routes to <tt>lib/'.$LIB_PATH.$LIB_FILE.'</tt>.
477 You can add some routes to <tt>lib/'.$LIB_PATH.$LIB_FILE.'</tt>.
480478 </p>
481479 </li>
482480
520518
521519 "dispatch.cgi" =>
522520 "$PERL_INTERPRETER
523 use Dancer2 ':syntax';
521 use Dancer2;
524522 use FindBin '\$RealBin';
525523 use Plack::Runner;
526524
527525 # For some reason Apache SetEnv directives dont propagate
528 # correctly to the dispatchers, so forcing PSGI and env here
526 # correctly to the dispatchers, so forcing PSGI and env here
529527 # is safer.
530528 set apphandler => 'PSGI';
531529 set environment => 'production';
539537
540538 "dispatch.fcgi" =>
541539 qq{$PERL_INTERPRETER
542 use Dancer2 ':syntax';
540 use Dancer2;
543541 use FindBin '\$RealBin';
544542 use Plack::Handler::FCGI;
545543
546544 # For some reason Apache SetEnv directives dont propagate
547 # correctly to the dispatchers, so forcing PSGI and env here
545 # correctly to the dispatchers, so forcing PSGI and env here
548546 # is safer.
549547 set apphandler => 'PSGI';
550548 set environment => 'production';
568566 "$LIB_FILE" =>
569567
570568 "package $appname;
571 use Dancer2 ':syntax';
569 use Dancer2;
572570
573571 our \$VERSION = '0.1';
574572
945943 # should Dancer2 show a stacktrace when an error is caught?
946944 show_errors: 1
947945
948 # auto_reload is a development and experimental feature
949 # you should enable it by yourself if you want it
950 # Module::Refresh is needed
951 #
952 # Be aware it's unstable and may cause a memory leak.
953 # DO NOT EVER USE THIS FEATURE IN PRODUCTION
954 # OR TINY KITTENS SHALL DIE WITH LOTS OF SUFFERING
955 auto_reload: 0
956
957946 # print the banner
958947 startup_info: 1
959948 ",
970959 # don\'t consider warnings critical
971960 warnings: 0
972961
973 # hide errors
962 # hide errors
974963 show_errors: 0
975964
976965 # cache route resolution for maximum performance
12061195
12071196 =head1 VERSION
12081197
1209 version 0.07
1198 version 0.09
12101199
12111200 =head1 SYNOPSIS
12121201
t/.multiserver.t.swp less more
Binary diff not shown
00 use strict;
11 use warnings;
22
3 # This test was generated via Dist::Zilla::Plugin::Test::Compile 2.017
3 # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.023
44
5 use Test::More 0.88;
5 use Test::More tests => 58 + ( $ENV{AUTHOR_TESTING} ? 1 : 0 );
66
77
8 use Capture::Tiny qw{ capture };
9
10 my @module_files = qw(
11 Dancer2.pm
12 Dancer2/Core.pm
13 Dancer2/Core/App.pm
14 Dancer2/Core/Context.pm
15 Dancer2/Core/Cookie.pm
16 Dancer2/Core/DSL.pm
17 Dancer2/Core/Dispatcher.pm
18 Dancer2/Core/Error.pm
19 Dancer2/Core/Factory.pm
20 Dancer2/Core/HTTP.pm
21 Dancer2/Core/Hook.pm
22 Dancer2/Core/MIME.pm
23 Dancer2/Core/Request.pm
24 Dancer2/Core/Request/Upload.pm
25 Dancer2/Core/Response.pm
26 Dancer2/Core/Role/Config.pm
27 Dancer2/Core/Role/DSL.pm
28 Dancer2/Core/Role/Engine.pm
29 Dancer2/Core/Role/Handler.pm
30 Dancer2/Core/Role/Headers.pm
31 Dancer2/Core/Role/Hookable.pm
32 Dancer2/Core/Role/Logger.pm
33 Dancer2/Core/Role/Serializer.pm
34 Dancer2/Core/Role/Server.pm
35 Dancer2/Core/Role/SessionFactory.pm
36 Dancer2/Core/Role/SessionFactory/File.pm
37 Dancer2/Core/Role/StandardResponses.pm
38 Dancer2/Core/Role/Template.pm
39 Dancer2/Core/Route.pm
40 Dancer2/Core/Runner.pm
41 Dancer2/Core/Server/PSGI.pm
42 Dancer2/Core/Server/Standalone.pm
43 Dancer2/Core/Session.pm
44 Dancer2/Core/Time.pm
45 Dancer2/Core/Types.pm
46 Dancer2/FileUtils.pm
47 Dancer2/Handler/AutoPage.pm
48 Dancer2/Handler/File.pm
49 Dancer2/Logger/Capture.pm
50 Dancer2/Logger/Capture/Trap.pm
51 Dancer2/Logger/Console.pm
52 Dancer2/Logger/Diag.pm
53 Dancer2/Logger/File.pm
54 Dancer2/Logger/Note.pm
55 Dancer2/Logger/Null.pm
56 Dancer2/ModuleLoader.pm
57 Dancer2/Plugin.pm
58 Dancer2/Plugin/Ajax.pm
59 Dancer2/Serializer/Dumper.pm
60 Dancer2/Serializer/JSON.pm
61 Dancer2/Serializer/YAML.pm
62 Dancer2/Session/Simple.pm
63 Dancer2/Session/YAML.pm
64 Dancer2/Template/Implementation/ForkedTiny.pm
65 Dancer2/Template/Simple.pm
66 Dancer2/Template/TemplateToolkit.pm
67 Dancer2/Template/Tiny.pm
68 Dancer2/Test.pm
8 my @module_files = (
9 'Dancer2.pm',
10 'Dancer2/Core.pm',
11 'Dancer2/Core/App.pm',
12 'Dancer2/Core/Context.pm',
13 'Dancer2/Core/Cookie.pm',
14 'Dancer2/Core/DSL.pm',
15 'Dancer2/Core/Dispatcher.pm',
16 'Dancer2/Core/Error.pm',
17 'Dancer2/Core/Factory.pm',
18 'Dancer2/Core/HTTP.pm',
19 'Dancer2/Core/Hook.pm',
20 'Dancer2/Core/MIME.pm',
21 'Dancer2/Core/Request.pm',
22 'Dancer2/Core/Request/Upload.pm',
23 'Dancer2/Core/Response.pm',
24 'Dancer2/Core/Role/Config.pm',
25 'Dancer2/Core/Role/DSL.pm',
26 'Dancer2/Core/Role/Engine.pm',
27 'Dancer2/Core/Role/Handler.pm',
28 'Dancer2/Core/Role/Headers.pm',
29 'Dancer2/Core/Role/Hookable.pm',
30 'Dancer2/Core/Role/Logger.pm',
31 'Dancer2/Core/Role/Serializer.pm',
32 'Dancer2/Core/Role/Server.pm',
33 'Dancer2/Core/Role/SessionFactory.pm',
34 'Dancer2/Core/Role/SessionFactory/File.pm',
35 'Dancer2/Core/Role/StandardResponses.pm',
36 'Dancer2/Core/Role/Template.pm',
37 'Dancer2/Core/Route.pm',
38 'Dancer2/Core/Runner.pm',
39 'Dancer2/Core/Server/PSGI.pm',
40 'Dancer2/Core/Server/Standalone.pm',
41 'Dancer2/Core/Session.pm',
42 'Dancer2/Core/Time.pm',
43 'Dancer2/Core/Types.pm',
44 'Dancer2/FileUtils.pm',
45 'Dancer2/Handler/AutoPage.pm',
46 'Dancer2/Handler/File.pm',
47 'Dancer2/Logger/Capture.pm',
48 'Dancer2/Logger/Capture/Trap.pm',
49 'Dancer2/Logger/Console.pm',
50 'Dancer2/Logger/Diag.pm',
51 'Dancer2/Logger/File.pm',
52 'Dancer2/Logger/Note.pm',
53 'Dancer2/Logger/Null.pm',
54 'Dancer2/Plugin.pm',
55 'Dancer2/Plugin/Ajax.pm',
56 'Dancer2/Serializer/Dumper.pm',
57 'Dancer2/Serializer/JSON.pm',
58 'Dancer2/Serializer/YAML.pm',
59 'Dancer2/Session/Simple.pm',
60 'Dancer2/Session/YAML.pm',
61 'Dancer2/Template/Implementation/ForkedTiny.pm',
62 'Dancer2/Template/Simple.pm',
63 'Dancer2/Template/TemplateToolkit.pm',
64 'Dancer2/Template/Tiny.pm',
65 'Dancer2/Test.pm'
6966 );
7067
71 my @scripts = qw(
72 script/dancer2
73 );
68 my @scripts = ( 'script/dancer2' );
7469
7570 # no fake home requested
7671
72 use IPC::Open3;
73 use IO::Handle;
74
7775 my @warnings;
7876 for my $lib (@module_files) {
79 my ( $stdout, $stderr, $exit ) = capture {
80 system( $^X, '-Mblib', '-e', qq{require q[$lib]} );
81 };
82 is( $?, 0, "$lib loaded ok" );
83 warn $stderr if $stderr;
84 push @warnings, $stderr if $stderr;
77
78 # see L<perlfaq8/How can I capture STDERR from an external command?>
79 my $stdin = ''; # converted to a gensym by open3
80 my $stderr = IO::Handle->new;
81
82 my $pid =
83 open3( $stdin, '>&STDERR', $stderr, qq{$^X -Mblib -e"require q[$lib]"} );
84 waitpid( $pid, 0 );
85 is( $? >> 8, 0, "$lib loaded ok" );
86
87 if ( my @_warnings = <$stderr> ) {
88 warn @_warnings;
89 push @warnings, @_warnings;
90 }
8591 }
92
93 foreach my $file (@scripts) {
94 SKIP: {
95 open my $fh, '<', $file or warn("Unable to open $file: $!"), next;
96 my $line = <$fh>;
97 close $fh and skip( "$file isn't perl", 1 )
98 unless $line =~ /^#!.*?\bperl\b\s*(.*)$/;
99
100 my $flags = $1;
101
102 my $stdin = ''; # converted to a gensym by open3
103 my $stderr = IO::Handle->new;
104
105 my $pid =
106 open3( $stdin, '>&STDERR', $stderr, qq{$^X -Mblib $flags -c $file} );
107 waitpid( $pid, 0 );
108 is( $? >> 8, 0, "$file compiled ok" );
109
110 if ( my @_warnings = grep { !/syntax OK$/ } <$stderr> ) {
111 warn @_warnings;
112 push @warnings, @_warnings;
113 }
114 }
115 }
116
86117
87118 is( scalar(@warnings), 0, 'no warnings found' ) if $ENV{AUTHOR_TESTING};
88119
89 use Test::Script 1.05;
90 foreach my $file (@scripts) {
91 script_compiles( $file, "$file compiles" );
92 }
93120
94
95 done_testing;
1212 CGI::Deurl::XS
1313 Capture::Tiny
1414 Carp
15 Class::Load
16 Class::Load::XS
1517 Config::Any
1618 Crypt::URandom
1719 Cwd
3638 HTTP::Server::Simple::PSGI
3739 Hash::Merge::Simple
3840 IO::File
41 IO::Handle
42 IPC::Open3
3943 JSON
4044 JSON::XS
4145 LWP::UserAgent
4448 MIME::Types
4549 Math::Random::ISAAC::XS
4650 Module::Build
47 Module::Runtime
4851 Moo
4952 Moo::Role
5053 MooX::Types::MooseLike
5457 Pod::Simple::Search
5558 Pod::Simple::SimpleTree
5659 Pod::Usage
60 Role::Tiny
5761 Scalar::Util
5862 Template
5963 Template::Tiny
6872 URL::Encode::XS
6973 YAML
7074 YAML::Any
71 blib
7275 constant
7376 lib
7477 overload
180180 };
181181 $app->add_route(%$regexp_route);
182182
183 # try to get an invalid engine
184 eval { $app->engine('foo') };
185 ok $!, "Engine 'foo' does not exists";
186
187 my $tmpl_engine = $app->engine('template');
188 ok $tmpl_engine, "Template engine is defined";
189
190 my $serializer_engine = $app->engine('serializer');
191 ok !defined $serializer_engine, "Serializer engine is not defined";
192
183193 done_testing;
0 use strict;
1 use warnings;
2
3 use Test::More;
4
5 {
6
7 package AutoPageTest;
8 use Dancer2;
9
10 set auto_page => 1;
11 ## HACK HACK HACK
12 Dancer2::Handler::AutoPage->register(app);
13 engine('template')->views('t/views');
14 engine('template')->layout('main');
15
16 }
17
18 use Dancer2::Test apps => ['AutoPageTest'];
19
20 my $r = dancer_response GET => '/auto_page';
21
22 is $r->status, 200, 'Autopage found the page';
23 like $r->content, qr/---\nHey! This is Auto Page working/,
24 '...with proper content';
25
26 $r = dancer_response GET => '/folder/page';
27
28 is $r->status, 200, 'Autopage found the page under a folder';
29 like $r->content, qr/---\nPage under folder/, '...with proper content';
30
31 $r = dancer_response GET => '/non_existent_page';
32 is $r->status, 404, 'Autopage doesnt try to render nonexistent pages';
33
34 $r = dancer_response GET => '/file.txt';
35 is $r->status, 200, 'Found file on public with Autopage';
36
37 like $r->headers->{'content-type'}, qr!text/plain!,
38 "Public served file as correct mime";
39
40 done_testing;
0 log: "info"
1
02 plugins:
13 "t::lib::FooPlugin":
24 plugin: 42
22 use Test::More import => ['!pass'];
33
44 use FindBin qw($Bin);
5 use lib "$Bin/t/lib";
5 use lib "$Bin/lib";
66 use Dancer2 dsl => 'MyDancerDSL';
77 use Dancer2::Test;
88
44
55 use Test::More tests => 49;
66
7 use Dancer2 ':syntax';
7 use Dancer2;
88 use Dancer2::Test;
99 use Dancer2::Core::Request;
1010 use File::Temp;
00 use strict;
11 use warnings;
22
3 use Test::More tests => 6;
3 use Test::More tests => 9;
44
55 {
66
1919 my $p = request->data;
2020 return join " : ", map { $_ => $p->{$_} } sort keys %$p;
2121 };
22
23 post '/from/:town' => sub {
24 my $p = params;
25 return $p;
26 };
2227 }
2328
2429 use utf8;
2530 use JSON;
2631 use Encode;
2732 use Dancer2::Test apps => ['MyApp'];
33 use Class::Load 'load_class';
2834
2935 is dancer_response(
3036 Dancer2::Core::Request->new(
3743 )->content => 'bar : 2 : foo : 1', "using $_"
3844 for qw/ params data /;
3945
40 my $utf8 = '∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i)';
41 my $r = dancer_response(
42 Dancer2::Core::Request->new(
46 note "Verify Serializers decode into characters";
47 {
48 my $utf8 = '∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i)';
49
50 for my $type (qw/Dumper JSON YAML/) {
51 my $class = "Dancer2::Serializer::$type";
52 load_class($class);
53
54 my $serializer = $class->new();
55 my $body = $serializer->serialize( { utf8 => $utf8 } );
56
57 my $r = dancer_response(
58 Dancer2::Core::Request->new(
59 method => 'PUT',
60 path => '/from_params',
61 content_type => $serializer->content_type,
62 body => $body,
63 serializer => $serializer,
64 )
65 );
66
67 my $content = Encode::decode( 'UTF-8', $r->content );
68 is( $content, "utf8 : $utf8",
69 "utf-8 string returns the same using the $type serializer" );
70 }
71 }
72
73 note "Decoding of mixed route and deserialized body params";
74 {
75 # Check integers from request body remain integers
76 # but route params get decoded.
77 my $r = dancer_response(
78 Dancer2::Core::Request->new(
79 method => 'POST',
80 path => "/from/D\x{c3}\x{bc}sseldorf", # /from/d%C3%BCsseldorf
81 content_type => 'application/json',
82 body => JSON::to_json( { population => 592393 } ),
83 serializer => Dancer2::Serializer::JSON->new(),
84 )
85 );
86
87 my $content = Encode::decode( 'UTF-8', $r->content );
88
89 # Watch out for hash order randomization..
90 like( $content, qr/[{,]"population":592393/,
91 "Integer from JSON body remains integer" );
92 like( $content, qr/[{,]"town":"Düsseldorf"/, "Route params are decoded" );
93 }
94
95 note 'Check serialization errors';
96 {
97 my $serializer = Dancer2::Serializer::JSON->new();
98 my $req = Dancer2::Core::Request->new(
4399 method => 'PUT',
44100 path => '/from_params',
45101 content_type => 'application/json',
46 body => JSON::to_json( { utf8 => $utf8 }, { utf8 => 1 } ),
47 serializer => Dancer2::Serializer::JSON->new(),
48 )
49 );
102 body => "---",
103 serializer => $serializer,
104 );
50105
51 my $content = Encode::decode( 'UTF-8', $r->content );
52 is( $content, "utf8 : $utf8", 'utf-8 string returns the same' );
53
54 my $req = Dancer2::Core::Request->new(
55 method => 'PUT',
56 path => '/from_params',
57 content_type => 'application/json',
58 body => "---",
59 serializer => Dancer2::Serializer::JSON->new(),
60 );
61
62 ok !$req->serializer->has_error;
63 $req->deserialize();
64 ok $req->serializer->has_error;
65 like $req->serializer->error, qr/malformed number/;
106 ok $req->serializer->has_error, "Invalid JSON threw error in serializer";
107 like $req->serializer->error, qr/malformed number/,
108 ".. of a 'malformed number'";
109 }
22 use Test::More;
33 use Test::Fatal;
44
5 use Dancer2::Core;
56 use Dancer2::Core::Factory;
67
7 is Dancer2::Core::Factory::_camelize('foo_bar_baz'), 'FooBarBaz';
8 is Dancer2::Core::Factory::_camelize('FooBarBaz'), 'FooBarBaz';
8 is Dancer2::Core::camelize('foo_bar_baz'), 'FooBarBaz';
9 is Dancer2::Core::camelize('FooBarBaz'), 'FooBarBaz';
910
1011 like(
1112 exception { my $l = Dancer2::Core::Factory->create( unknown => 'stuff' ) },
00 use strict;
11 use warnings;
2 use Test::More tests => 11;
2 use Test::More tests => 14;
33 use Test::Fatal;
44 use File::Spec;
55 BEGIN { @File::Spec::ISA = ("File::Spec::Unix") }
3131 my $content = Dancer2::FileUtils::read_file_content();
3232 is $content, undef;
3333
34 is Dancer2::FileUtils::normalize_path(), undef;
34 my $paths = [
35 [ undef => 'undef' ],
36 [ '/foo/./bar/' => '/foo/bar/' ],
37 [ '/foo/../bar' => '/bar' ],
38 [ '/foo/bar/..' => '/foo/' ],
39 ];
40
41 for my $case (@$paths) {
42 is Dancer2::FileUtils::normalize_path( $case->[0] ), $case->[1];
43 }
3544
3645 my $p = Dancer2::FileUtils::dirname('/somewhere');
3746 is $p, '/';
0 use Test::More import => ['!pass'], tests => 3;
1 use Dancer2;
2 use Dancer2::Test;
3 use strict;
4 use warnings;
5
6
7 get '/' => sub {
8 return 'Forbidden';
9 };
10
11 get '/default' => sub {
12 return 'Default';
13 };
14
15 get '/redirect' => sub {
16 return 'Secret stuff never seen';
17 };
18
19 hook before => sub {
20 my $context = shift;
21 return if $context->request->dispatch_path eq '/default';
22
23 # Add some content to the response
24 $context->response->content("SillyStringIsSilly");
25
26 # redirect - response should include the above content
27 return redirect '/default'
28 if $context->request->dispatch_path eq '/redirect';
29
30 # The response object will get replaced by the result of the forward.
31 forward '/default';
32 };
33
34 response_content_like(
35 [ GET => '/' ], qr{Default},
36 'forward in before hook'
37 );
38
39 # redirect in before hook
40 my $r = dancer_response GET => '/redirect';
41 is $r->status, 302, "redirect in before hook";
42 is $r->content, "SillyStringIsSilly", ".. and the response content is correct";
43
44 done_testing();
33 use File::Spec;
44 use Carp;
55
6 use Class::Load 'try_load_class';
67 use Capture::Tiny 0.12 'capture_stderr';
78
8 Dancer2::ModuleLoader->require('Template')
9 try_load_class('Template')
910 or plan skip_all => 'Template::Toolkit not present';
1011
1112 my @hooks = qw(
2728 my $tests_flags = {};
2829 {
2930 use Dancer2;
30
3131
3232 for my $hook (@hooks) {
3333 hook $hook => sub {
9797 like $response->content, qr/Internal Server Error/;
9898 };
9999
100 # make sure we compile all the apps without starting a webserver
101 main->dancer_app->finish;
102100 }
103101
104102 use Dancer2::Test;
0 use strict;
1 use warnings;
2 use Test::More tests => 11;
3
4 use Dancer2::Core::HTTP;
5
6 note "HTTP status";
7 {
8 my @tests = (
9 { status => undef, expected => undef },
10 { status => 200, expected => 200 },
11 { status => 'Not Found', expected => 404 },
12 { status => 'bad_request', expected => 400 },
13 { status => 'i_m_a_teapot', expected => 418 },
14 { status => 'error', expected => 500 },
15 { status => 911, expected => 911 },
16 );
17
18 for my $test (@tests) {
19 my $status_text = defined $test->{status} ? $test->{status} : 'undef';
20 is( Dancer2::Core::HTTP->status( $test->{status} ),
21 $test->{expected},
22 "HTTP status looks good for $status_text"
23 );
24 }
25 }
26
27
28 note "HTTP status_message";
29 {
30 my @tests = (
31 { status => undef, expected => undef },
32 { status => 200, expected => 'OK' },
33 { status => 'error', expected => 'Internal Server Error' },
34 { status => 911, expected => undef },
35 );
36
37 for my $test (@tests) {
38 my $status_text = defined $test->{status} ? $test->{status} : 'undef';
39 is( Dancer2::Core::HTTP->status_message( $test->{status} ),
40 $test->{expected},
41 "HTTP status message looks good for $status_text"
42 );
43 }
44 }
45
1111 my $orig = shift;
1212 my $keywords = $orig->(@_);
1313
14 push @$keywords, [ gateau => 0 ], # cookie
15 [ moteur => 1 ], # engine
16 [ stop => 0 ], # halt
17 [ prend => 1 ], # post
18 [ envoie => 1 ], # post
19 [ entete => 0 ]; #header
14 $keywords->{gateau} = { is_global => 0 }; # cookie
15 $keywords->{moteur} = { is_global => 1 }; # engine
16 $keywords->{stop} = { is_global => 0 }; # halt
17 $keywords->{prend} = { is_global => 1 }; # post
18 $keywords->{envoie} = { is_global => 1 }; # post
19 $keywords->{entete} = { is_global => 0 }; #header
2020
2121 return $keywords;
2222 };
66 # Freeze time at Tue, 15-Jun-2010 00:00:00 GMT
77 *CORE::GLOBAL::time = sub { return 1276560000 }
88 }
9
910
1011 my $_logs = [];
1112
2526
2627 is $logger->log_level, 'debug';
2728 $logger->debug("foo");
28 like $_logs->[0], qr{debug \@2010-06-1\d \d\d:00:00> foo in t/logger.t};
2929
30 subtest 'logger capture' => sub {
30 # Hard to make caller(6) work when we deal with the logger directly,
31 # so do not check for a specific filename.
32 like $_logs->[0], qr{debug \@2010-06-1\d \d\d:00:00> foo in };
33
34 subtest 'log level and capture' => sub {
3135 use Dancer2::Logger::Capture;
3236 use Dancer2;
3337
38 # NOTE: this will read the config.yml under t/ that defines log level as info
3439 set logger => 'capture';
3540
3641 warning "Danger! Warning!";
3742 info "Tango, Foxtrot";
3843 debug "I like pie.";
3944
40 my $app = dancer_app;
41 my $trap = $app->setting('logger')->trapper;
45 my $trap = dancer_app->engine('logger')->trapper;
4246 is_deeply $trap->read,
4347 [ { level => "warning", message => "Danger! Warning!" },
4448 { level => "info", message => "Tango, Foxtrot" },
45 { level => "debug", message => "I like pie.", }
4649 ];
4750
4851 # each call to read cleans the trap
99
1010 for my $level (qw{core debug warning error}) {
1111 my $stderr = capture_stderr { $l->$level("$level") };
12 like $stderr, qr{$level in t/logger_console.t}, "$level message sent";
12
13 # Again, we are dealing directly with the logger, not through the
14 # DSL, so the caller(6) stack has a different size
15 like $stderr, qr{$level in -}, "$level message sent";
1316 }
1417 done_testing;
+0
-29
t/path_info.t less more
0 use Test::More import => ['!pass'], tests => 1;
1 use Dancer2;
2 use Dancer2::Test;
3 use strict;
4 use warnings;
5
6
7 get '/' => sub {
8 return 'Forbidden';
9 };
10
11 get '/default' => sub {
12 return 'Default';
13 };
14
15 hook before => sub {
16 my $context = shift;
17 return if $context->request->path eq '/default';
18
19 $context->response( forward('/default') );
20 $context->response->halt;
21 };
22
23 response_content_like(
24 [ GET => '/' ], qr{Default},
25 'Changing request->path_info worked'
26 );
27
28 done_testing();
33
44 use Dancer2::Core::Request;
55
6 diag "If you want extract speed, install URL::Encode::XS"
6 diag "If you want extra speed, install URL::Encode::XS"
77 if !$Dancer2::Core::Request::XS_URL_DECODE;
8 diag "If you want extract speed, install CGI::Deurl::XS"
8 diag "If you want extra speed, install CGI::Deurl::XS"
99 if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING;
1010
1111 sub run_test {
8989 is $req->base, 'http://oddhostname:5000/foo';
9090 }
9191
92 {
93 note "testing begind proxy";
94 $req = Dancer2::Core::Request->new(
92 note "testing behind proxy";
93 {
94 my $req = Dancer2::Core::Request->new(
9595 env => $env,
9696 is_behind_proxy => 1
9797 );
100100 is $req->scheme, 'https';
101101 }
102102
103 note "testing uri_base";
104 $env = {
105 'psgi.url_scheme' => 'http',
106 REQUEST_METHOD => 'GET',
107 SCRIPT_NAME => '/',
108 PATH_INFO => '/bar/baz',
109 REQUEST_URI => '/foo/bar/baz',
110 QUERY_STRING => '',
111 SERVER_NAME => 'localhost',
112 SERVER_PORT => 5000,
113 SERVER_PROTOCOL => 'HTTP/1.1',
114 };
115
116 $req = Dancer2::Core::Request->new( env => $env );
117 is( $req->uri_base, 'http://localhost:5000',
118 'remove trailing slash if only one',
119 );
120
121 $env->{'SCRIPT_NAME'} = '/foo/';
122 $req = Dancer2::Core::Request->new( env => $env );
123 is( $req->uri_base, 'http://localhost:5000/foo/',
124 'keeping trailing slash if not only',
125 );
126
127 $env->{'PATH_INFO'} = '/';
128 $env->{'SCRIPT_NAME'} = '';
129 $req = Dancer2::Core::Request->new( env => $env );
130 is( $req->uri_base, 'http://localhost:5000', );
131
103 note "testing path, dispatch_path and uri_base";
104 {
105 # Base env used for path, dispatch_path and uri_base tests
106 my $base = {
107 'psgi.url_scheme' => 'http',
108 REQUEST_METHOD => 'GET',
109 QUERY_STRING => '',
110 SERVER_NAME => 'localhost',
111 SERVER_PORT => 5000,
112 SERVER_PROTOCOL => 'HTTP/1.1',
113 };
114
115 # PATH_INFO not set
116 my $env = {
117 %$base,
118 SCRIPT_NAME => '/foo',
119 PATH_INFO => '',
120 REQUEST_URI => '/foo',
121 };
122 my $req = Dancer2::Core::Request->new( env => $env );
123 is( $req->path, '/foo', 'path corrent when empty PATH_INFO' );
124 is( $req->uri_base, 'http://localhost:5000/foo',
125 'uri_base correct when empty PATH_INFO'
126 );
127 is( $req->dispatch_path, '/',
128 'dispatch_path correct when empty PATH_INFO'
129 );
130
131 # SCRIPT_NAME not set
132 $env = {
133 %$base,
134 SCRIPT_NAME => '',
135 PATH_INFO => '/foo',
136 REQUEST_URI => '/foo',
137 };
138 $req = Dancer2::Core::Request->new( env => $env );
139 is( $req->path, '/foo', 'path corrent when empty SCRIPT_NAME' );
140 is( $req->uri_base, 'http://localhost:5000',
141 'uri_base handles empty SCRIPT_NAME'
142 );
143 is( $req->dispatch_path, '/foo',
144 'dispatch_path handles empty SCRIPT_NAME'
145 );
146
147 # Both SCRIPT_NAME and PATH_INFO set
148 # PSGI spec does not allow SCRIPT_NAME='/', PATH_INFO='/some/path'
149 $env = {
150 %$base,
151 SCRIPT_NAME => '/foo',
152 PATH_INFO => '/bar/baz/',
153 REQUEST_URI => '/foo/bar/baz/',
154 };
155 $req = Dancer2::Core::Request->new( env => $env );
156 is( $req->path, '/foo/bar/baz/',
157 'path corrent when both PATH_INFO and SCRIPT_NAME set'
158 );
159 is( $req->uri_base, 'http://localhost:5000/foo',
160 'uri_base correct when both PATH_INFO and SCRIPT_NAME set',
161 );
162 is( $req->dispatch_path, '/bar/baz/',
163 'dispatch_path correct when both PATH_INFO and SCRIPT_NAME set'
164 );
165
166 # Neither SCRIPT_NAME or PATH_INFO set
167 $env = {
168 %$base,
169 SCRIPT_NAME => '',
170 PATH_INFO => '',
171 REQUEST_URI => '/foo/',
172 };
173 $req = Dancer2::Core::Request->new( env => $env );
174 is( $req->path, '/foo/',
175 'path corrent when calculated from REQUEST_URI'
176 );
177 is( $req->uri_base, 'http://localhost:5000',
178 'uri_base correct when calculated from REQUEST_URI',
179 );
180 is( $req->dispatch_path, '/foo/',
181 'dispatch_path correct when calculated from REQUEST_URI'
182 );
183 }
132184
133185 note "testing forward";
134186 $env = {
22 use warnings FATAL => 'all';
33 use Dancer2::Core::Request;
44
5 diag "If you want extract speed, install URL::Encode::XS"
5 diag "If you want extra speed, install URL::Encode::XS"
66 if !$Dancer2::Core::Request::XS_URL_DECODE;
7 diag "If you want extract speed, install CGI::Deurl::XS"
7 diag "If you want extra speed, install CGI::Deurl::XS"
88 if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING;
99
1010 sub run_test {
1010 use File::Spec;
1111 use Encode qw(encode_utf8);
1212
13 diag "If you want extract speed, install URL::Encode::XS"
13 diag "If you want extra speed, install URL::Encode::XS"
1414 if !$Dancer2::Core::Request::XS_URL_DECODE;
15 diag "If you want extract speed, install CGI::Deurl::XS"
15 diag "If you want extra speed, install CGI::Deurl::XS"
1616 if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING;
1717
1818 sub test_path {
00 use strict;
11 use warnings;
22
3 use Test::More tests => 3;
3 use Test::More tests => 7;
4 use Dancer2::Serializer::Dumper;
45
56 {
67
1011
1112 set serializer => 'JSON';
1213
13 get '/foo' => sub { return { bar => 'baz' } };
14 get '/json' => sub { return { bar => 'baz' } };
15 get '/to_json' => sub { to_json( { bar => 'baz' }, { pretty => 1 } ) };
1416 }
1517
1618 use Dancer2::Test apps => ['MyApp'];
1719
18 my $resp = dancer_response('/foo');
19
20 response_status_is $resp => 200;
21
20 # Response with implicit call to the serializer
21 my $resp = dancer_response('/json');
22 response_status_is $resp => 200;
2223 response_content_is $resp => '{"bar":"baz"}';
23
2424 response_headers_include $resp, [ 'Content-Type' => 'application/json' ];
2525
26 # Response with explicit call to the serializer
27 $resp = dancer_response('/to_json');
28 response_status_is $resp => 200;
29 response_content_is $resp => "{\n \"bar\" : \"baz\"\n}\n";
30
31 # When calling `to_json', the content_type is not set,
32 # because we can't assume we're calling it for a response
33 response_headers_include $resp,
34 [ 'Content-Type' => 'text/html; charset=UTF-8' ];
35
36 my $serializer = Dancer2::Serializer::Dumper->new();
37 is $serializer->content_type, 'text/x-data-dumper',
38 'content-type is set correctly';
55
66 use Dancer2::Core::Session;
77 use Dancer2::Session::Simple;
8 use Class::Load 'try_load_class';
89
910 my $ENGINE = Dancer2::Session::Simple->new;
1011
11 my $CPRNG_AVAIL = Dancer2::ModuleLoader->require("Math::Random::ISAAC::XS")
12 && Dancer2::ModuleLoader->require("Crypt::URandom");
12 my $CPRNG_AVAIL = try_load_class('Math::Random::ISAAC::XS')
13 && try_load_class('Crypt::URandom');
1314
1415 diag $CPRNG_AVAIL
1516 ? "Crypto strength tokens"
22 use strict;
33 use warnings;
44 use Test::More;
5 use Dancer2::ModuleLoader;
5 use Class::Load 'try_load_class';
66
77 my $mocked_epoch = 1355676244; # "Sun, 16-Dec-2012 16:44:04 GMT"
88
99 # The order is important!
10 Dancer2::ModuleLoader->require('Test::MockTime')
10 try_load_class('Test::MockTime')
1111 or plan skip_all => 'Test::MockTime not present';
1212
1313 Test::MockTime::set_fixed_time($mocked_epoch);
0 Hey! This is Auto Page working.
0 Page under folder.