Imported Upstream version 0.09+dfsg
Nuno Carvalho
10 years ago
17 | 17 | "Dancer Core Developers" |
18 | 18 | ], |
19 | 19 | "dist_name" => "Dancer2", |
20 | "dist_version" => "0.07", | |
20 | "dist_version" => "0.09", | |
21 | 21 | "license" => "perl", |
22 | 22 | "module_name" => "Dancer2", |
23 | 23 | "recommends" => { |
35 | 35 | "recursive_test_files" => 1, |
36 | 36 | "requires" => { |
37 | 37 | "Carp" => 0, |
38 | "Class::Load" => 0, | |
38 | 39 | "Config::Any" => 0, |
39 | 40 | "Cwd" => 0, |
40 | 41 | "Data::Dumper" => 0, |
60 | 61 | "List::Util" => 0, |
61 | 62 | "MIME::Base64" => "3.13", |
62 | 63 | "MIME::Types" => 0, |
63 | "Module::Runtime" => 0, | |
64 | 64 | "Moo" => "1.003000", |
65 | 65 | "Moo::Role" => 0, |
66 | 66 | "MooX::Types::MooseLike" => "0.16", |
70 | 70 | "Pod::Simple::Search" => 0, |
71 | 71 | "Pod::Simple::SimpleTree" => 0, |
72 | 72 | "Pod::Usage" => 0, |
73 | "Role::Tiny" => "1.003000", | |
73 | 74 | "Scalar::Util" => 0, |
74 | 75 | "Template" => 0, |
75 | 76 | "Template::Tiny" => 0, |
79 | 80 | "URI::Escape" => 0, |
80 | 81 | "YAML::Any" => 0, |
81 | 82 | "constant" => 0, |
82 | "lib" => 0, | |
83 | 83 | "overload" => 0, |
84 | 84 | "parent" => 0, |
85 | 85 | "perl" => "5.00503", |
97 | 97 | "HTTP::Body" => 0, |
98 | 98 | "HTTP::Request::Common" => 0, |
99 | 99 | "HTTP::Server::Simple::PSGI" => 0, |
100 | "IO::Handle" => 0, | |
101 | "IPC::Open3" => 0, | |
100 | 102 | "Test::Fatal" => 0, |
101 | 103 | "Test::MockTime" => 0, |
102 | 104 | "Test::More" => "0.92", |
103 | "Test::Script" => "1.05", | |
105 | "Test::Script" => 0, | |
104 | 106 | "Test::TCP" => "1.13", |
105 | 107 | "YAML" => 0, |
106 | 108 | "YAML::Any" => 0, |
107 | "blib" => 0, | |
109 | "lib" => 0, | |
108 | 110 | "utf8" => 0, |
109 | 111 | "vars" => 0 |
110 | 112 | } |
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 | ||
0 | 62 | 0.07 2013-08-04 01:14:59 Asia/Jerusalem |
1 | 63 | |
2 | 64 | [ 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! |
0 | 0 | AUTHORS |
1 | 1 | Build.PL |
2 | 2 | Changes |
3 | GitGuide.md | |
3 | 4 | LICENSE |
4 | 5 | MANIFEST |
5 | 6 | META.json |
54 | 55 | lib/Dancer2/Logger/Note.pm |
55 | 56 | lib/Dancer2/Logger/Null.pm |
56 | 57 | lib/Dancer2/Manual.pod |
57 | lib/Dancer2/ModuleLoader.pm | |
58 | 58 | lib/Dancer2/Plugin.pm |
59 | 59 | lib/Dancer2/Plugin/Ajax.pm |
60 | 60 | lib/Dancer2/Plugins.pod |
70 | 70 | lib/Dancer2/Test.pm |
71 | 71 | lib/Dancer2/Tutorial.pod |
72 | 72 | script/dancer2 |
73 | t/.multiserver.t.swp | |
74 | 73 | t/00-compile.t |
75 | 74 | t/00-report-prereqs.t |
76 | 75 | t/ajax_plugin.t |
83 | 82 | t/app/t2/.dancer |
84 | 83 | t/app/t2/config.yml |
85 | 84 | t/app/t2/lib/App3.pm |
85 | t/auto_page.t | |
86 | 86 | t/charset_server.t |
87 | 87 | t/config.t |
88 | 88 | t/config.yml |
114 | 114 | t/factory.t |
115 | 115 | t/file_utils.t |
116 | 116 | t/forward.t |
117 | t/forward_before_hook.t | |
117 | 118 | t/forward_test_tcp.t |
118 | 119 | t/handler_file.t |
119 | 120 | t/hooks.t |
120 | 121 | t/http_methods.t |
122 | t/http_status.t | |
121 | 123 | t/lib/App1.pm |
122 | 124 | t/lib/App2.pm |
123 | 125 | t/lib/DancerPlugin.pm |
133 | 135 | t/logger.t |
134 | 136 | t/logger_console.t |
135 | 137 | t/mime.t |
136 | t/path_info.t | |
137 | 138 | t/plugin_import.t |
138 | 139 | t/plugin_multiple_apps.t |
139 | 140 | t/plugin_register.t |
161 | 162 | t/session_forward.t |
162 | 163 | t/session_lifecycle.t |
163 | 164 | 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 | |
164 | 171 | t/sessions/Uf1awwAAXkpPhxmXEwjwLY6Oi5Dv7A8v.yml |
165 | 172 | 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 | |
166 | 178 | t/sessions/UfRHAQAAJsw_2oX_cvBqBtXlgre8vFiy.yml |
167 | 179 | t/sessions/UfRXcwAAO8pp0bx4QwYmc73cP7UKMyY7.yml |
168 | 180 | t/sessions/UfUFzgAAOBHtc6NRAKAH4xNgo-GIz8VA.yml |
169 | 181 | 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 | |
170 | 193 | t/shared_engines.t |
171 | 194 | t/special_import.t |
172 | 195 | t/splat.t |
210 | 233 | t/types.t |
211 | 234 | t/uri_for.t |
212 | 235 | t/vars.t |
236 | t/views/auto_page.tt | |
237 | t/views/folder/page.tt | |
213 | 238 | t/views/index.tt |
214 | 239 | t/views/layouts/main.tt |
215 | 240 | t/views/tokens.tt |
45 | 45 | }, |
46 | 46 | "requires" : { |
47 | 47 | "Carp" : "0", |
48 | "Class::Load" : "0", | |
48 | 49 | "Config::Any" : "0", |
49 | 50 | "Cwd" : "0", |
50 | 51 | "Data::Dumper" : "0", |
70 | 71 | "List::Util" : "0", |
71 | 72 | "MIME::Base64" : "3.13", |
72 | 73 | "MIME::Types" : "0", |
73 | "Module::Runtime" : "0", | |
74 | 74 | "Moo" : "1.003000", |
75 | 75 | "Moo::Role" : "0", |
76 | 76 | "MooX::Types::MooseLike" : "0.16", |
80 | 80 | "Pod::Simple::Search" : "0", |
81 | 81 | "Pod::Simple::SimpleTree" : "0", |
82 | 82 | "Pod::Usage" : "0", |
83 | "Role::Tiny" : "1.003000", | |
83 | 84 | "Scalar::Util" : "0", |
84 | 85 | "Template" : "0", |
85 | 86 | "Template::Tiny" : "0", |
89 | 90 | "URI::Escape" : "0", |
90 | 91 | "YAML::Any" : "0", |
91 | 92 | "constant" : "0", |
92 | "lib" : "0", | |
93 | 93 | "overload" : "0", |
94 | 94 | "parent" : "0", |
95 | 95 | "perl" : "5.00503", |
97 | 97 | "warnings" : "0" |
98 | 98 | }, |
99 | 99 | "suggests" : { |
100 | "Class::Load::XS" : "0", | |
100 | 101 | "Fcntl" : "0", |
101 | 102 | "YAML::Any" : "0" |
102 | 103 | } |
110 | 111 | "HTTP::Body" : "0", |
111 | 112 | "HTTP::Request::Common" : "0", |
112 | 113 | "HTTP::Server::Simple::PSGI" : "0", |
114 | "IO::Handle" : "0", | |
115 | "IPC::Open3" : "0", | |
113 | 116 | "Test::Fatal" : "0", |
114 | 117 | "Test::MockTime" : "0", |
115 | 118 | "Test::More" : "0.92", |
116 | "Test::Script" : "1.05", | |
119 | "Test::Script" : "0", | |
117 | 120 | "Test::TCP" : "1.13", |
118 | 121 | "YAML" : "0", |
119 | 122 | "YAML::Any" : "0", |
120 | "blib" : "0", | |
123 | "lib" : "0", | |
121 | 124 | "utf8" : "0", |
122 | 125 | "vars" : "0" |
123 | 126 | } |
126 | 129 | "provides" : { |
127 | 130 | "Dancer2" : { |
128 | 131 | "file" : "lib/Dancer2.pm", |
129 | "version" : "0.07" | |
132 | "version" : "0.09" | |
130 | 133 | }, |
131 | 134 | "Dancer2::Config" : { |
132 | 135 | "file" : "lib/Dancer2/Config.pod", |
133 | "version" : "0.07" | |
136 | "version" : "0.09" | |
134 | 137 | }, |
135 | 138 | "Dancer2::Cookbook" : { |
136 | 139 | "file" : "lib/Dancer2/Cookbook.pod", |
137 | "version" : "0.07" | |
140 | "version" : "0.09" | |
138 | 141 | }, |
139 | 142 | "Dancer2::Core" : { |
140 | 143 | "file" : "lib/Dancer2/Core.pm", |
141 | "version" : "0.07" | |
144 | "version" : "0.09" | |
142 | 145 | }, |
143 | 146 | "Dancer2::Core::App" : { |
144 | 147 | "file" : "lib/Dancer2/Core/App.pm", |
145 | "version" : "0.07" | |
148 | "version" : "0.09" | |
146 | 149 | }, |
147 | 150 | "Dancer2::Core::Context" : { |
148 | 151 | "file" : "lib/Dancer2/Core/Context.pm", |
149 | "version" : "0.07" | |
152 | "version" : "0.09" | |
150 | 153 | }, |
151 | 154 | "Dancer2::Core::Cookie" : { |
152 | 155 | "file" : "lib/Dancer2/Core/Cookie.pm", |
153 | "version" : "0.07" | |
156 | "version" : "0.09" | |
154 | 157 | }, |
155 | 158 | "Dancer2::Core::DSL" : { |
156 | 159 | "file" : "lib/Dancer2/Core/DSL.pm", |
157 | "version" : "0.07" | |
160 | "version" : "0.09" | |
158 | 161 | }, |
159 | 162 | "Dancer2::Core::Dispatcher" : { |
160 | 163 | "file" : "lib/Dancer2/Core/Dispatcher.pm", |
161 | "version" : "0.07" | |
164 | "version" : "0.09" | |
162 | 165 | }, |
163 | 166 | "Dancer2::Core::Error" : { |
164 | 167 | "file" : "lib/Dancer2/Core/Error.pm", |
165 | "version" : "0.07" | |
168 | "version" : "0.09" | |
166 | 169 | }, |
167 | 170 | "Dancer2::Core::Factory" : { |
168 | 171 | "file" : "lib/Dancer2/Core/Factory.pm", |
169 | "version" : "0.07" | |
172 | "version" : "0.09" | |
170 | 173 | }, |
171 | 174 | "Dancer2::Core::HTTP" : { |
172 | 175 | "file" : "lib/Dancer2/Core/HTTP.pm", |
173 | "version" : "0.07" | |
176 | "version" : "0.09" | |
174 | 177 | }, |
175 | 178 | "Dancer2::Core::Hook" : { |
176 | 179 | "file" : "lib/Dancer2/Core/Hook.pm", |
177 | "version" : "0.07" | |
180 | "version" : "0.09" | |
178 | 181 | }, |
179 | 182 | "Dancer2::Core::MIME" : { |
180 | 183 | "file" : "lib/Dancer2/Core/MIME.pm", |
181 | "version" : "0.07" | |
184 | "version" : "0.09" | |
182 | 185 | }, |
183 | 186 | "Dancer2::Core::Request" : { |
184 | 187 | "file" : "lib/Dancer2/Core/Request.pm", |
185 | "version" : "0.07" | |
188 | "version" : "0.09" | |
186 | 189 | }, |
187 | 190 | "Dancer2::Core::Request::Upload" : { |
188 | 191 | "file" : "lib/Dancer2/Core/Request/Upload.pm", |
189 | "version" : "0.07" | |
192 | "version" : "0.09" | |
190 | 193 | }, |
191 | 194 | "Dancer2::Core::Response" : { |
192 | 195 | "file" : "lib/Dancer2/Core/Response.pm", |
193 | "version" : "0.07" | |
196 | "version" : "0.09" | |
194 | 197 | }, |
195 | 198 | "Dancer2::Core::Role::Config" : { |
196 | 199 | "file" : "lib/Dancer2/Core/Role/Config.pm", |
197 | "version" : "0.07" | |
200 | "version" : "0.09" | |
198 | 201 | }, |
199 | 202 | "Dancer2::Core::Role::DSL" : { |
200 | 203 | "file" : "lib/Dancer2/Core/Role/DSL.pm", |
201 | "version" : "0.07" | |
204 | "version" : "0.09" | |
202 | 205 | }, |
203 | 206 | "Dancer2::Core::Role::Engine" : { |
204 | 207 | "file" : "lib/Dancer2/Core/Role/Engine.pm", |
205 | "version" : "0.07" | |
208 | "version" : "0.09" | |
206 | 209 | }, |
207 | 210 | "Dancer2::Core::Role::Handler" : { |
208 | 211 | "file" : "lib/Dancer2/Core/Role/Handler.pm", |
209 | "version" : "0.07" | |
212 | "version" : "0.09" | |
210 | 213 | }, |
211 | 214 | "Dancer2::Core::Role::Headers" : { |
212 | 215 | "file" : "lib/Dancer2/Core/Role/Headers.pm", |
213 | "version" : "0.07" | |
216 | "version" : "0.09" | |
214 | 217 | }, |
215 | 218 | "Dancer2::Core::Role::Hookable" : { |
216 | 219 | "file" : "lib/Dancer2/Core/Role/Hookable.pm", |
217 | "version" : "0.07" | |
220 | "version" : "0.09" | |
218 | 221 | }, |
219 | 222 | "Dancer2::Core::Role::Logger" : { |
220 | 223 | "file" : "lib/Dancer2/Core/Role/Logger.pm", |
221 | "version" : "0.07" | |
224 | "version" : "0.09" | |
222 | 225 | }, |
223 | 226 | "Dancer2::Core::Role::Serializer" : { |
224 | 227 | "file" : "lib/Dancer2/Core/Role/Serializer.pm", |
225 | "version" : "0.07" | |
228 | "version" : "0.09" | |
226 | 229 | }, |
227 | 230 | "Dancer2::Core::Role::Server" : { |
228 | 231 | "file" : "lib/Dancer2/Core/Role/Server.pm", |
229 | "version" : "0.07" | |
232 | "version" : "0.09" | |
230 | 233 | }, |
231 | 234 | "Dancer2::Core::Role::SessionFactory" : { |
232 | 235 | "file" : "lib/Dancer2/Core/Role/SessionFactory.pm", |
233 | "version" : "0.07" | |
236 | "version" : "0.09" | |
234 | 237 | }, |
235 | 238 | "Dancer2::Core::Role::SessionFactory::File" : { |
236 | 239 | "file" : "lib/Dancer2/Core/Role/SessionFactory/File.pm", |
237 | "version" : "0.07" | |
240 | "version" : "0.09" | |
238 | 241 | }, |
239 | 242 | "Dancer2::Core::Role::StandardResponses" : { |
240 | 243 | "file" : "lib/Dancer2/Core/Role/StandardResponses.pm", |
241 | "version" : "0.07" | |
244 | "version" : "0.09" | |
242 | 245 | }, |
243 | 246 | "Dancer2::Core::Role::Template" : { |
244 | 247 | "file" : "lib/Dancer2/Core/Role/Template.pm", |
245 | "version" : "0.07" | |
248 | "version" : "0.09" | |
246 | 249 | }, |
247 | 250 | "Dancer2::Core::Route" : { |
248 | 251 | "file" : "lib/Dancer2/Core/Route.pm", |
249 | "version" : "0.07" | |
252 | "version" : "0.09" | |
250 | 253 | }, |
251 | 254 | "Dancer2::Core::Runner" : { |
252 | 255 | "file" : "lib/Dancer2/Core/Runner.pm", |
253 | "version" : "0.07" | |
256 | "version" : "0.09" | |
254 | 257 | }, |
255 | 258 | "Dancer2::Core::Server::PSGI" : { |
256 | 259 | "file" : "lib/Dancer2/Core/Server/PSGI.pm", |
257 | "version" : "0.07" | |
260 | "version" : "0.09" | |
258 | 261 | }, |
259 | 262 | "Dancer2::Core::Server::Standalone" : { |
260 | 263 | "file" : "lib/Dancer2/Core/Server/Standalone.pm", |
261 | "version" : "0.07" | |
264 | "version" : "0.09" | |
262 | 265 | }, |
263 | 266 | "Dancer2::Core::Session" : { |
264 | 267 | "file" : "lib/Dancer2/Core/Session.pm", |
265 | "version" : "0.07" | |
268 | "version" : "0.09" | |
266 | 269 | }, |
267 | 270 | "Dancer2::Core::Time" : { |
268 | 271 | "file" : "lib/Dancer2/Core/Time.pm", |
269 | "version" : "0.07" | |
272 | "version" : "0.09" | |
270 | 273 | }, |
271 | 274 | "Dancer2::Core::Types" : { |
272 | 275 | "file" : "lib/Dancer2/Core/Types.pm", |
273 | "version" : "0.07" | |
276 | "version" : "0.09" | |
274 | 277 | }, |
275 | 278 | "Dancer2::FileUtils" : { |
276 | 279 | "file" : "lib/Dancer2/FileUtils.pm", |
277 | "version" : "0.07" | |
280 | "version" : "0.09" | |
278 | 281 | }, |
279 | 282 | "Dancer2::Handler::AutoPage" : { |
280 | 283 | "file" : "lib/Dancer2/Handler/AutoPage.pm", |
281 | "version" : "0.07" | |
284 | "version" : "0.09" | |
282 | 285 | }, |
283 | 286 | "Dancer2::Handler::File" : { |
284 | 287 | "file" : "lib/Dancer2/Handler/File.pm", |
285 | "version" : "0.07" | |
288 | "version" : "0.09" | |
286 | 289 | }, |
287 | 290 | "Dancer2::Logger::Capture" : { |
288 | 291 | "file" : "lib/Dancer2/Logger/Capture.pm", |
289 | "version" : "0.07" | |
292 | "version" : "0.09" | |
290 | 293 | }, |
291 | 294 | "Dancer2::Logger::Capture::Trap" : { |
292 | 295 | "file" : "lib/Dancer2/Logger/Capture/Trap.pm", |
293 | "version" : "0.07" | |
296 | "version" : "0.09" | |
294 | 297 | }, |
295 | 298 | "Dancer2::Logger::Console" : { |
296 | 299 | "file" : "lib/Dancer2/Logger/Console.pm", |
297 | "version" : "0.07" | |
300 | "version" : "0.09" | |
298 | 301 | }, |
299 | 302 | "Dancer2::Logger::Diag" : { |
300 | 303 | "file" : "lib/Dancer2/Logger/Diag.pm", |
301 | "version" : "0.07" | |
304 | "version" : "0.09" | |
302 | 305 | }, |
303 | 306 | "Dancer2::Logger::File" : { |
304 | 307 | "file" : "lib/Dancer2/Logger/File.pm", |
305 | "version" : "0.07" | |
308 | "version" : "0.09" | |
306 | 309 | }, |
307 | 310 | "Dancer2::Logger::Note" : { |
308 | 311 | "file" : "lib/Dancer2/Logger/Note.pm", |
309 | "version" : "0.07" | |
312 | "version" : "0.09" | |
310 | 313 | }, |
311 | 314 | "Dancer2::Logger::Null" : { |
312 | 315 | "file" : "lib/Dancer2/Logger/Null.pm", |
313 | "version" : "0.07" | |
316 | "version" : "0.09" | |
314 | 317 | }, |
315 | 318 | "Dancer2::Manual" : { |
316 | 319 | "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" | |
322 | 321 | }, |
323 | 322 | "Dancer2::Plugin" : { |
324 | 323 | "file" : "lib/Dancer2/Plugin.pm", |
325 | "version" : "0.07" | |
324 | "version" : "0.09" | |
326 | 325 | }, |
327 | 326 | "Dancer2::Plugin::Ajax" : { |
328 | 327 | "file" : "lib/Dancer2/Plugin/Ajax.pm", |
329 | "version" : "0.07" | |
328 | "version" : "0.09" | |
330 | 329 | }, |
331 | 330 | "Dancer2::Plugins" : { |
332 | 331 | "file" : "lib/Dancer2/Plugins.pod", |
333 | "version" : "0.07" | |
332 | "version" : "0.09" | |
334 | 333 | }, |
335 | 334 | "Dancer2::Serializer::Dumper" : { |
336 | 335 | "file" : "lib/Dancer2/Serializer/Dumper.pm", |
337 | "version" : "0.07" | |
336 | "version" : "0.09" | |
338 | 337 | }, |
339 | 338 | "Dancer2::Serializer::JSON" : { |
340 | 339 | "file" : "lib/Dancer2/Serializer/JSON.pm", |
341 | "version" : "0.07" | |
340 | "version" : "0.09" | |
342 | 341 | }, |
343 | 342 | "Dancer2::Serializer::YAML" : { |
344 | 343 | "file" : "lib/Dancer2/Serializer/YAML.pm", |
345 | "version" : "0.07" | |
344 | "version" : "0.09" | |
346 | 345 | }, |
347 | 346 | "Dancer2::Session::Simple" : { |
348 | 347 | "file" : "lib/Dancer2/Session/Simple.pm", |
349 | "version" : "0.07" | |
348 | "version" : "0.09" | |
350 | 349 | }, |
351 | 350 | "Dancer2::Session::YAML" : { |
352 | 351 | "file" : "lib/Dancer2/Session/YAML.pm", |
353 | "version" : "0.07" | |
352 | "version" : "0.09" | |
354 | 353 | }, |
355 | 354 | "Dancer2::Template::Implementation::ForkedTiny" : { |
356 | 355 | "file" : "lib/Dancer2/Template/Implementation/ForkedTiny.pm", |
357 | "version" : "0.07" | |
356 | "version" : "0.09" | |
358 | 357 | }, |
359 | 358 | "Dancer2::Template::Simple" : { |
360 | 359 | "file" : "lib/Dancer2/Template/Simple.pm", |
361 | "version" : "0.07" | |
360 | "version" : "0.09" | |
362 | 361 | }, |
363 | 362 | "Dancer2::Template::TemplateToolkit" : { |
364 | 363 | "file" : "lib/Dancer2/Template/TemplateToolkit.pm", |
365 | "version" : "0.07" | |
364 | "version" : "0.09" | |
366 | 365 | }, |
367 | 366 | "Dancer2::Template::Tiny" : { |
368 | 367 | "file" : "lib/Dancer2/Template/Tiny.pm", |
369 | "version" : "0.07" | |
368 | "version" : "0.09" | |
370 | 369 | }, |
371 | 370 | "Dancer2::Test" : { |
372 | 371 | "file" : "lib/Dancer2/Test.pm", |
373 | "version" : "0.07" | |
372 | "version" : "0.09" | |
374 | 373 | }, |
375 | 374 | "Dancer2::Tutorial" : { |
376 | 375 | "file" : "lib/Dancer2/Tutorial.pod", |
377 | "version" : "0.07" | |
376 | "version" : "0.09" | |
378 | 377 | } |
379 | 378 | }, |
380 | 379 | "release_status" : "stable", |
387 | 386 | "url" : "https://github.com/PerlDancer/Dancer2" |
388 | 387 | } |
389 | 388 | }, |
390 | "version" : "0.07" | |
389 | "version" : "0.09" | |
391 | 390 | } |
392 | 391 |
9 | 9 | HTTP::Body: 0 |
10 | 10 | HTTP::Request::Common: 0 |
11 | 11 | HTTP::Server::Simple::PSGI: 0 |
12 | IO::Handle: 0 | |
13 | IPC::Open3: 0 | |
12 | 14 | Module::Build: 0.3601 |
13 | 15 | Test::Fatal: 0 |
14 | 16 | Test::MockTime: 0 |
15 | 17 | Test::More: 0.92 |
16 | Test::Script: 1.05 | |
18 | Test::Script: 0 | |
17 | 19 | Test::TCP: 1.13 |
18 | 20 | YAML: 0 |
19 | 21 | YAML::Any: 0 |
20 | blib: 0 | |
22 | lib: 0 | |
21 | 23 | utf8: 0 |
22 | 24 | vars: 0 |
23 | 25 | configure_requires: |
33 | 35 | provides: |
34 | 36 | Dancer2: |
35 | 37 | file: lib/Dancer2.pm |
36 | version: 0.07 | |
38 | version: 0.09 | |
37 | 39 | Dancer2::Config: |
38 | 40 | file: lib/Dancer2/Config.pod |
39 | version: 0.07 | |
41 | version: 0.09 | |
40 | 42 | Dancer2::Cookbook: |
41 | 43 | file: lib/Dancer2/Cookbook.pod |
42 | version: 0.07 | |
44 | version: 0.09 | |
43 | 45 | Dancer2::Core: |
44 | 46 | file: lib/Dancer2/Core.pm |
45 | version: 0.07 | |
47 | version: 0.09 | |
46 | 48 | Dancer2::Core::App: |
47 | 49 | file: lib/Dancer2/Core/App.pm |
48 | version: 0.07 | |
50 | version: 0.09 | |
49 | 51 | Dancer2::Core::Context: |
50 | 52 | file: lib/Dancer2/Core/Context.pm |
51 | version: 0.07 | |
53 | version: 0.09 | |
52 | 54 | Dancer2::Core::Cookie: |
53 | 55 | file: lib/Dancer2/Core/Cookie.pm |
54 | version: 0.07 | |
56 | version: 0.09 | |
55 | 57 | Dancer2::Core::DSL: |
56 | 58 | file: lib/Dancer2/Core/DSL.pm |
57 | version: 0.07 | |
59 | version: 0.09 | |
58 | 60 | Dancer2::Core::Dispatcher: |
59 | 61 | file: lib/Dancer2/Core/Dispatcher.pm |
60 | version: 0.07 | |
62 | version: 0.09 | |
61 | 63 | Dancer2::Core::Error: |
62 | 64 | file: lib/Dancer2/Core/Error.pm |
63 | version: 0.07 | |
65 | version: 0.09 | |
64 | 66 | Dancer2::Core::Factory: |
65 | 67 | file: lib/Dancer2/Core/Factory.pm |
66 | version: 0.07 | |
68 | version: 0.09 | |
67 | 69 | Dancer2::Core::HTTP: |
68 | 70 | file: lib/Dancer2/Core/HTTP.pm |
69 | version: 0.07 | |
71 | version: 0.09 | |
70 | 72 | Dancer2::Core::Hook: |
71 | 73 | file: lib/Dancer2/Core/Hook.pm |
72 | version: 0.07 | |
74 | version: 0.09 | |
73 | 75 | Dancer2::Core::MIME: |
74 | 76 | file: lib/Dancer2/Core/MIME.pm |
75 | version: 0.07 | |
77 | version: 0.09 | |
76 | 78 | Dancer2::Core::Request: |
77 | 79 | file: lib/Dancer2/Core/Request.pm |
78 | version: 0.07 | |
80 | version: 0.09 | |
79 | 81 | Dancer2::Core::Request::Upload: |
80 | 82 | file: lib/Dancer2/Core/Request/Upload.pm |
81 | version: 0.07 | |
83 | version: 0.09 | |
82 | 84 | Dancer2::Core::Response: |
83 | 85 | file: lib/Dancer2/Core/Response.pm |
84 | version: 0.07 | |
86 | version: 0.09 | |
85 | 87 | Dancer2::Core::Role::Config: |
86 | 88 | file: lib/Dancer2/Core/Role/Config.pm |
87 | version: 0.07 | |
89 | version: 0.09 | |
88 | 90 | Dancer2::Core::Role::DSL: |
89 | 91 | file: lib/Dancer2/Core/Role/DSL.pm |
90 | version: 0.07 | |
92 | version: 0.09 | |
91 | 93 | Dancer2::Core::Role::Engine: |
92 | 94 | file: lib/Dancer2/Core/Role/Engine.pm |
93 | version: 0.07 | |
95 | version: 0.09 | |
94 | 96 | Dancer2::Core::Role::Handler: |
95 | 97 | file: lib/Dancer2/Core/Role/Handler.pm |
96 | version: 0.07 | |
98 | version: 0.09 | |
97 | 99 | Dancer2::Core::Role::Headers: |
98 | 100 | file: lib/Dancer2/Core/Role/Headers.pm |
99 | version: 0.07 | |
101 | version: 0.09 | |
100 | 102 | Dancer2::Core::Role::Hookable: |
101 | 103 | file: lib/Dancer2/Core/Role/Hookable.pm |
102 | version: 0.07 | |
104 | version: 0.09 | |
103 | 105 | Dancer2::Core::Role::Logger: |
104 | 106 | file: lib/Dancer2/Core/Role/Logger.pm |
105 | version: 0.07 | |
107 | version: 0.09 | |
106 | 108 | Dancer2::Core::Role::Serializer: |
107 | 109 | file: lib/Dancer2/Core/Role/Serializer.pm |
108 | version: 0.07 | |
110 | version: 0.09 | |
109 | 111 | Dancer2::Core::Role::Server: |
110 | 112 | file: lib/Dancer2/Core/Role/Server.pm |
111 | version: 0.07 | |
113 | version: 0.09 | |
112 | 114 | Dancer2::Core::Role::SessionFactory: |
113 | 115 | file: lib/Dancer2/Core/Role/SessionFactory.pm |
114 | version: 0.07 | |
116 | version: 0.09 | |
115 | 117 | Dancer2::Core::Role::SessionFactory::File: |
116 | 118 | file: lib/Dancer2/Core/Role/SessionFactory/File.pm |
117 | version: 0.07 | |
119 | version: 0.09 | |
118 | 120 | Dancer2::Core::Role::StandardResponses: |
119 | 121 | file: lib/Dancer2/Core/Role/StandardResponses.pm |
120 | version: 0.07 | |
122 | version: 0.09 | |
121 | 123 | Dancer2::Core::Role::Template: |
122 | 124 | file: lib/Dancer2/Core/Role/Template.pm |
123 | version: 0.07 | |
125 | version: 0.09 | |
124 | 126 | Dancer2::Core::Route: |
125 | 127 | file: lib/Dancer2/Core/Route.pm |
126 | version: 0.07 | |
128 | version: 0.09 | |
127 | 129 | Dancer2::Core::Runner: |
128 | 130 | file: lib/Dancer2/Core/Runner.pm |
129 | version: 0.07 | |
131 | version: 0.09 | |
130 | 132 | Dancer2::Core::Server::PSGI: |
131 | 133 | file: lib/Dancer2/Core/Server/PSGI.pm |
132 | version: 0.07 | |
134 | version: 0.09 | |
133 | 135 | Dancer2::Core::Server::Standalone: |
134 | 136 | file: lib/Dancer2/Core/Server/Standalone.pm |
135 | version: 0.07 | |
137 | version: 0.09 | |
136 | 138 | Dancer2::Core::Session: |
137 | 139 | file: lib/Dancer2/Core/Session.pm |
138 | version: 0.07 | |
140 | version: 0.09 | |
139 | 141 | Dancer2::Core::Time: |
140 | 142 | file: lib/Dancer2/Core/Time.pm |
141 | version: 0.07 | |
143 | version: 0.09 | |
142 | 144 | Dancer2::Core::Types: |
143 | 145 | file: lib/Dancer2/Core/Types.pm |
144 | version: 0.07 | |
146 | version: 0.09 | |
145 | 147 | Dancer2::FileUtils: |
146 | 148 | file: lib/Dancer2/FileUtils.pm |
147 | version: 0.07 | |
149 | version: 0.09 | |
148 | 150 | Dancer2::Handler::AutoPage: |
149 | 151 | file: lib/Dancer2/Handler/AutoPage.pm |
150 | version: 0.07 | |
152 | version: 0.09 | |
151 | 153 | Dancer2::Handler::File: |
152 | 154 | file: lib/Dancer2/Handler/File.pm |
153 | version: 0.07 | |
155 | version: 0.09 | |
154 | 156 | Dancer2::Logger::Capture: |
155 | 157 | file: lib/Dancer2/Logger/Capture.pm |
156 | version: 0.07 | |
158 | version: 0.09 | |
157 | 159 | Dancer2::Logger::Capture::Trap: |
158 | 160 | file: lib/Dancer2/Logger/Capture/Trap.pm |
159 | version: 0.07 | |
161 | version: 0.09 | |
160 | 162 | Dancer2::Logger::Console: |
161 | 163 | file: lib/Dancer2/Logger/Console.pm |
162 | version: 0.07 | |
164 | version: 0.09 | |
163 | 165 | Dancer2::Logger::Diag: |
164 | 166 | file: lib/Dancer2/Logger/Diag.pm |
165 | version: 0.07 | |
167 | version: 0.09 | |
166 | 168 | Dancer2::Logger::File: |
167 | 169 | file: lib/Dancer2/Logger/File.pm |
168 | version: 0.07 | |
170 | version: 0.09 | |
169 | 171 | Dancer2::Logger::Note: |
170 | 172 | file: lib/Dancer2/Logger/Note.pm |
171 | version: 0.07 | |
173 | version: 0.09 | |
172 | 174 | Dancer2::Logger::Null: |
173 | 175 | file: lib/Dancer2/Logger/Null.pm |
174 | version: 0.07 | |
176 | version: 0.09 | |
175 | 177 | Dancer2::Manual: |
176 | 178 | 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 | |
181 | 180 | Dancer2::Plugin: |
182 | 181 | file: lib/Dancer2/Plugin.pm |
183 | version: 0.07 | |
182 | version: 0.09 | |
184 | 183 | Dancer2::Plugin::Ajax: |
185 | 184 | file: lib/Dancer2/Plugin/Ajax.pm |
186 | version: 0.07 | |
185 | version: 0.09 | |
187 | 186 | Dancer2::Plugins: |
188 | 187 | file: lib/Dancer2/Plugins.pod |
189 | version: 0.07 | |
188 | version: 0.09 | |
190 | 189 | Dancer2::Serializer::Dumper: |
191 | 190 | file: lib/Dancer2/Serializer/Dumper.pm |
192 | version: 0.07 | |
191 | version: 0.09 | |
193 | 192 | Dancer2::Serializer::JSON: |
194 | 193 | file: lib/Dancer2/Serializer/JSON.pm |
195 | version: 0.07 | |
194 | version: 0.09 | |
196 | 195 | Dancer2::Serializer::YAML: |
197 | 196 | file: lib/Dancer2/Serializer/YAML.pm |
198 | version: 0.07 | |
197 | version: 0.09 | |
199 | 198 | Dancer2::Session::Simple: |
200 | 199 | file: lib/Dancer2/Session/Simple.pm |
201 | version: 0.07 | |
200 | version: 0.09 | |
202 | 201 | Dancer2::Session::YAML: |
203 | 202 | file: lib/Dancer2/Session/YAML.pm |
204 | version: 0.07 | |
203 | version: 0.09 | |
205 | 204 | Dancer2::Template::Implementation::ForkedTiny: |
206 | 205 | file: lib/Dancer2/Template/Implementation/ForkedTiny.pm |
207 | version: 0.07 | |
206 | version: 0.09 | |
208 | 207 | Dancer2::Template::Simple: |
209 | 208 | file: lib/Dancer2/Template/Simple.pm |
210 | version: 0.07 | |
209 | version: 0.09 | |
211 | 210 | Dancer2::Template::TemplateToolkit: |
212 | 211 | file: lib/Dancer2/Template/TemplateToolkit.pm |
213 | version: 0.07 | |
212 | version: 0.09 | |
214 | 213 | Dancer2::Template::Tiny: |
215 | 214 | file: lib/Dancer2/Template/Tiny.pm |
216 | version: 0.07 | |
215 | version: 0.09 | |
217 | 216 | Dancer2::Test: |
218 | 217 | file: lib/Dancer2/Test.pm |
219 | version: 0.07 | |
218 | version: 0.09 | |
220 | 219 | Dancer2::Tutorial: |
221 | 220 | file: lib/Dancer2/Tutorial.pod |
222 | version: 0.07 | |
221 | version: 0.09 | |
223 | 222 | recommends: |
224 | 223 | CGI::Deurl::XS: 0 |
225 | 224 | Crypt::URandom: 0 |
233 | 232 | URL::Encode::XS: 0 |
234 | 233 | requires: |
235 | 234 | Carp: 0 |
235 | Class::Load: 0 | |
236 | 236 | Config::Any: 0 |
237 | 237 | Cwd: 0 |
238 | 238 | Data::Dumper: 0 |
258 | 258 | List::Util: 0 |
259 | 259 | MIME::Base64: 3.13 |
260 | 260 | MIME::Types: 0 |
261 | Module::Runtime: 0 | |
262 | 261 | Moo: 1.003000 |
263 | 262 | Moo::Role: 0 |
264 | 263 | MooX::Types::MooseLike: 0.16 |
268 | 267 | Pod::Simple::Search: 0 |
269 | 268 | Pod::Simple::SimpleTree: 0 |
270 | 269 | Pod::Usage: 0 |
270 | Role::Tiny: 1.003000 | |
271 | 271 | Scalar::Util: 0 |
272 | 272 | Template: 0 |
273 | 273 | Template::Tiny: 0 |
277 | 277 | URI::Escape: 0 |
278 | 278 | YAML::Any: 0 |
279 | 279 | constant: 0 |
280 | lib: 0 | |
281 | 280 | overload: 0 |
282 | 281 | parent: 0 |
283 | 282 | perl: 5.00503 |
287 | 286 | bugtracker: https://github.com/PerlDancer/Dancer2/issues |
288 | 287 | homepage: http://perldancer.org/ |
289 | 288 | repository: https://github.com/PerlDancer/Dancer2 |
290 | version: 0.07 | |
289 | version: 0.09 |
25 | 25 | "NAME" => "Dancer2", |
26 | 26 | "PREREQ_PM" => { |
27 | 27 | "Carp" => 0, |
28 | "Class::Load" => 0, | |
28 | 29 | "Config::Any" => 0, |
29 | 30 | "Cwd" => 0, |
30 | 31 | "Data::Dumper" => 0, |
50 | 51 | "List::Util" => 0, |
51 | 52 | "MIME::Base64" => "3.13", |
52 | 53 | "MIME::Types" => 0, |
53 | "Module::Runtime" => 0, | |
54 | 54 | "Moo" => "1.003000", |
55 | 55 | "Moo::Role" => 0, |
56 | 56 | "MooX::Types::MooseLike" => "0.16", |
60 | 60 | "Pod::Simple::Search" => 0, |
61 | 61 | "Pod::Simple::SimpleTree" => 0, |
62 | 62 | "Pod::Usage" => 0, |
63 | "Role::Tiny" => "1.003000", | |
63 | 64 | "Scalar::Util" => 0, |
64 | 65 | "Template" => 0, |
65 | 66 | "Template::Tiny" => 0, |
69 | 70 | "URI::Escape" => 0, |
70 | 71 | "YAML::Any" => 0, |
71 | 72 | "constant" => 0, |
72 | "lib" => 0, | |
73 | 73 | "overload" => 0, |
74 | 74 | "parent" => 0, |
75 | 75 | "strict" => 0, |
83 | 83 | "HTTP::Body" => 0, |
84 | 84 | "HTTP::Request::Common" => 0, |
85 | 85 | "HTTP::Server::Simple::PSGI" => 0, |
86 | "IO::Handle" => 0, | |
87 | "IPC::Open3" => 0, | |
86 | 88 | "Test::Fatal" => 0, |
87 | 89 | "Test::MockTime" => 0, |
88 | 90 | "Test::More" => "0.92", |
89 | "Test::Script" => "1.05", | |
91 | "Test::Script" => 0, | |
90 | 92 | "Test::TCP" => "1.13", |
91 | 93 | "YAML" => 0, |
92 | 94 | "YAML::Any" => 0, |
93 | "blib" => 0, | |
95 | "lib" => 0, | |
94 | 96 | "utf8" => 0, |
95 | 97 | "vars" => 0 |
96 | 98 | }, |
97 | "VERSION" => "0.07", | |
99 | "VERSION" => "0.09", | |
98 | 100 | "test" => { |
99 | 101 | "TESTS" => "t/*.t t/roles/*.t t/route-pod-coverage/*.t t/template_tiny/*.t" |
100 | 102 | } |
0 | 0 | # Dancer2 |
1 | 1 | |
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) | |
3 | 3 | |
4 | 4 | Dancer2 is the new generation lightweight web-framework for Perl. |
5 | 5 | |
30 | 30 | * [Builds status on Travis](https://travis-ci.org/PerlDancer/Dancer2) |
31 | 31 | * [Our Mailing List](http://list.perldancer.org/cgi-bin/listinfo/dancer-users) |
32 | 32 | * [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) | |
34 | 34 | * [The Advent Calendar](http://advent.perldancer.org/) |
35 | * [Contribution/Git Guide](https://github.com/PerlDancer/Dancer2/blob/master/GitGuide.md) | |
35 | 36 | |
36 | ## Contributing | |
37 | ## Available Plugins | |
37 | 38 | |
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) | | |
154 | 57 | |
155 | 58 | |
156 | ### Install various dependencies (required) | |
59 | ## Templates engines | |
157 | 60 | |
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) | | |
300 | 66 | |
301 | 67 | |
302 | 68 | ## License |
10 | 10 | |
11 | 11 | =head1 VERSION |
12 | 12 | |
13 | version 0.07 | |
13 | version 0.09 | |
14 | 14 | |
15 | 15 | =head1 DESCRIPTION |
16 | 16 | |
344 | 344 | |
345 | 345 | =back |
346 | 346 | |
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 | ||
363 | 347 | =head2 Logger engine |
364 | 348 | |
365 | 349 | The logger must be configured in a separate C<engines> section, like so: |
10 | 10 | |
11 | 11 | =head1 VERSION |
12 | 12 | |
13 | version 0.07 | |
13 | version 0.09 | |
14 | 14 | |
15 | 15 | =head1 DESCRIPTION |
16 | 16 | |
17 | 17 | A quick-start guide with examples to get you up and running with the Dancer2 web |
18 | 18 | framework. |
19 | ||
20 | =encoding utf8 | |
19 | 21 | |
20 | 22 | =head1 BEGINNER'S DANCE |
21 | 23 | |
54 | 56 | =head2 Starting a Dancer2 project |
55 | 57 | |
56 | 58 | 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 | |
58 | 60 | script, which will build the framework of your application with a single |
59 | 61 | command: |
60 | 62 | |
61 | $ dancer -a mywebapp | |
63 | $ dancer2 -a mywebapp | |
62 | 64 | + mywebapp |
65 | + mywebapp/bin | |
66 | + mywebapp/bin/app.pl | |
63 | 67 | + mywebapp/config.yml |
64 | 68 | + mywebapp/environments |
65 | 69 | + mywebapp/environments/development.yml |
68 | 72 | + mywebapp/views/index.tt |
69 | 73 | + mywebapp/views/layouts |
70 | 74 | + mywebapp/views/layouts/main.tt |
71 | + mywebapp/mywebapp.pl | |
75 | + mywebapp/MANIFEST.SKIP | |
72 | 76 | + mywebapp/lib |
77 | mywebapp/lib/ | |
73 | 78 | + mywebapp/lib/mywebapp.pm |
74 | 79 | + mywebapp/public |
75 | 80 | + mywebapp/public/css |
76 | 81 | + mywebapp/public/css/style.css |
77 | 82 | + mywebapp/public/css/error.css |
78 | 83 | + mywebapp/public/images |
84 | + mywebapp/public/500.html | |
79 | 85 | + mywebapp/public/404.html |
80 | 86 | + mywebapp/public/dispatch.fcgi |
81 | 87 | + mywebapp/public/dispatch.cgi |
82 | + mywebapp/public/500.html | |
83 | + mywebapp/Makefile.PL | |
88 | + mywebapp/public/javascripts | |
89 | + mywebapp/public/javascripts/jquery.js | |
84 | 90 | + mywebapp/t |
85 | 91 | + mywebapp/t/002_index_route.t |
86 | 92 | + mywebapp/t/001_base.t |
93 | + mywebapp/Makefile.PL | |
87 | 94 | |
88 | 95 | As you can see, it creates a directory named after the name of the app, along |
89 | 96 | with a configuration file, a views directory (where your templates and layouts |
169 | 176 | a request is passed to the appropriate route. |
170 | 177 | |
171 | 178 | hook before => sub { |
172 | var note => 'Hi there'; | |
173 | request->path('/foo/oversee') | |
179 | forward '/foo/oversee', { note => 'Hi there' }; | |
174 | 180 | }; |
175 | 181 | |
176 | 182 | get '/foo/*' => sub { |
177 | 183 | 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>. | |
185 | 189 | |
186 | 190 | See also the L<hook|Dancer2/hook> hook keyword. |
187 | 191 | |
263 | 267 | admin section, and want to maintain this in a different package: |
264 | 268 | |
265 | 269 | package myapp; |
266 | use Dancer2 ':syntax'; | |
270 | use Dancer2; | |
267 | 271 | use myapp::admin; |
268 | 272 | |
269 | 273 | prefix undef; |
273 | 277 | 1; |
274 | 278 | |
275 | 279 | package myapp::admin; |
276 | use Dancer2 ':syntax'; | |
280 | use Dancer2; | |
277 | 281 | |
278 | 282 | prefix '/admin'; |
279 | 283 | |
364 | 368 | |
365 | 369 | When you're done with your session, you can destroy it: |
366 | 370 | |
367 | session->destroy | |
371 | context->destroy_session | |
368 | 372 | |
369 | 373 | =head2 Sessions and logging in |
370 | 374 | |
374 | 378 | This can easily be handled with a before filter to check their session: |
375 | 379 | |
376 | 380 | 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 }; | |
381 | 383 | } |
382 | 384 | }; |
383 | 385 | |
384 | 386 | get '/login' => sub { |
385 | 387 | # 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'} }; | |
388 | 390 | }; |
389 | 391 | |
390 | 392 | post '/login' => sub { |
657 | 659 | Use Dancer2 instead. Without any ado, magic or too big jumps, you can use the |
658 | 660 | values from config.yml and some additional default values: |
659 | 661 | |
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 | |
664 | 666 | |
665 | 667 | Note that config->{log} should result undef error on a default scaffold since |
666 | 668 | you did not load the environment and in the default scaffold log is defined in |
670 | 672 | One way to do so, is to tell Dancer2 where the webapp lives. From there Dancer2 |
671 | 673 | deducts where the config.yml file is (typically $webapp/config.yml). |
672 | 674 | |
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 | |
687 | 689 | |
688 | 690 | By default Dancer2 loads development environment (typically |
689 | 691 | $webapp/environment/development.yml). In contrast to the example before, you |
693 | 695 | could just as well hand over a simple path for the app if you like that better, |
694 | 696 | e.g.: |
695 | 697 | |
696 | Dancer2::Config::setting('appdir','/path/to/app/dir'); | |
698 | Dancer2::Config::setting('appdir','/path/to/app/dir'); | |
697 | 699 | |
698 | 700 | If you want to load an environment other than the default, try this: |
699 | 701 | |
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 | |
713 | 715 | |
714 | 716 | By the way, you not only get values, you can also set values straightforward |
715 | 717 | like we do above with config->{environment}='production'. Of course, this value |
785 | 787 | |
786 | 788 | get '/hello/:name' => sub { |
787 | 789 | # this structure will be returned to the client as |
788 | # {"name":"$name"} | |
790 | # {"name":"$name"} | |
789 | 791 | return {name => params->{name}}; |
790 | 792 | }; |
791 | 793 | |
809 | 811 | |
810 | 812 | The content of the error will be serialized using the appropriate serializer. |
811 | 813 | |
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 | ||
818 | 814 | =head1 DANCER ON THE STAGE: DEPLOYMENT |
819 | 815 | |
820 | 816 | =head2 Running stand-alone |
832 | 828 | |
833 | 829 | This option can be useful for small personal web apps or internal apps, but if |
834 | 830 | 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. | |
835 | 844 | |
836 | 845 | =head2 CGI and Fast-CGI |
837 | 846 | |
894 | 903 | |
895 | 904 | You can use the same technique to deploy with FastCGI, by just changing the line: |
896 | 905 | |
897 | AddHandler cgi-script .cgi | |
906 | AddHandler cgi-script .cgi | |
898 | 907 | |
899 | 908 | By: |
900 | 909 | |
901 | AddHandler fastcgi-script .fcgi | |
910 | AddHandler fastcgi-script .fcgi | |
902 | 911 | |
903 | 912 | Of course remember to update your rewrite rules, if you have set any: |
904 | 913 | |
987 | 996 | |
988 | 997 | =head2 Plack middlewares |
989 | 998 | |
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. | |
1023 | 1024 | |
1024 | 1025 | =head3 Path-based middlewares |
1025 | 1026 | |
1026 | 1027 | 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 | }; | |
1032 | 1041 | |
1033 | 1042 | =head3 Running on Perl webservers with plackup |
1034 | 1043 | |
1067 | 1076 | Accept-Encoding HTTP request header. |
1068 | 1077 | |
1069 | 1078 | 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 | }; | |
1094 | 1090 | |
1095 | 1091 | To test if content compression works, trace the HTTP request and response |
1096 | 1092 | before and after enabling this middleware. Among other things, you should |
1099 | 1095 | |
1100 | 1096 | =head3 Running multiple apps with Plack::Builder |
1101 | 1097 | |
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 | |
1103 | 1099 | a L<PSGI> webserver like L<Starman>. |
1104 | 1100 | |
1105 | 1101 | Start by creating a simple app.psgi file: |
1106 | 1102 | |
1107 | use Dancer2 ':syntax'; | |
1103 | use OurWiki; # first app | |
1104 | use OurForum; # second app | |
1108 | 1105 | use Plack::Builder; |
1109 | 1106 | |
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 | ||
1134 | 1107 | builder { |
1135 | mount "/app1" => builder {$app1}; | |
1136 | mount "/app2" => builder {$app2}; | |
1108 | mount '/wiki' => OurWiki->dance, | |
1109 | mount '/forum' => OurForum->dance, | |
1137 | 1110 | }; |
1138 | 1111 | |
1139 | 1112 | and now use L<Starman> |
1140 | 1113 | |
1141 | 1114 | 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. | |
1142 | 1119 | |
1143 | 1120 | =head3 Running from Apache with Plack |
1144 | 1121 |
0 | 0 | # ABSTRACT: encapsulation of Dancer2 packages |
1 | ||
2 | 1 | package Dancer2::Core::App; |
3 | 2 | { |
4 | $Dancer2::Core::App::VERSION = '0.07'; | |
5 | } | |
6 | ||
7 | ||
8 | use strict; | |
9 | use warnings; | |
3 | $Dancer2::Core::App::VERSION = '0.09'; | |
4 | } | |
10 | 5 | |
11 | 6 | use Moo; |
12 | 7 | use File::Spec; |
14 | 9 | use Carp 'croak'; |
15 | 10 | |
16 | 11 | use Dancer2::FileUtils 'path', 'read_file_content'; |
12 | use Dancer2::Core; | |
17 | 13 | use Dancer2::Core::Types; |
18 | 14 | use Dancer2::Core::Route; |
19 | 15 | use Dancer2::Core::Hook; |
22 | 18 | with 'Dancer2::Core::Role::Hookable'; |
23 | 19 | with 'Dancer2::Core::Role::Config'; |
24 | 20 | |
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 | ); | |
36 | 26 | |
37 | 27 | has plugins => ( |
38 | 28 | is => 'rw', |
40 | 30 | default => sub { [] }, |
41 | 31 | ); |
42 | 32 | |
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 | ||
61 | 33 | has server => ( |
62 | 34 | is => 'rw', |
63 | 35 | isa => ConsumerOf ['Dancer2::Core::Role::Server'], |
64 | 36 | weak_ref => 1, |
65 | 37 | ); |
66 | 38 | |
67 | ||
68 | 39 | has runner_config => ( |
69 | 40 | is => 'ro', |
70 | 41 | isa => HashRef, |
71 | 42 | default => sub { {} }, |
72 | 43 | ); |
73 | ||
74 | 44 | |
75 | 45 | has default_config => ( |
76 | 46 | is => 'ro', |
79 | 49 | builder => '_build_default_config', |
80 | 50 | ); |
81 | 51 | |
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', | |
186 | 91 | isa => HashRef, |
187 | default => sub { {} }, | |
92 | default => sub { | |
93 | { get => [], | |
94 | head => [], | |
95 | post => [], | |
96 | put => [], | |
97 | del => [], | |
98 | options => [], | |
99 | }; | |
100 | }, | |
188 | 101 | ); |
189 | 102 | |
190 | 103 | # add_hook will add the hook to the first "hook candidate" it finds that support |
252 | 165 | return $self->$orig(@_); |
253 | 166 | }; |
254 | 167 | |
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 | }; | |
330 | 183 | } |
331 | 184 | |
332 | 185 | sub _init_hooks { |
374 | 227 | ); |
375 | 228 | } |
376 | 229 | |
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 | ||
377 | 424 | sub finish { |
378 | 425 | my ($self) = @_; |
379 | 426 | $self->register_route_handlers; |
380 | 427 | $self->compile_hooks; |
381 | 428 | } |
382 | 429 | |
383 | has route_handlers => ( | |
384 | is => 'rw', | |
385 | isa => HashRef, | |
386 | default => sub { {} }, | |
387 | ); | |
388 | ||
389 | 430 | sub init_route_handlers { |
390 | 431 | my ($self) = @_; |
391 | 432 | |
392 | 433 | 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}; | |
395 | 436 | $config = {} if !ref($config); |
396 | 437 | $config->{app} = $self; |
438 | ||
397 | 439 | my $handler = Dancer2::Core::Factory->create( |
398 | 440 | Handler => $handler_name, |
399 | 441 | %$config, |
400 | 442 | postponed_hooks => $self->postponed_hooks, |
401 | 443 | ); |
402 | $self->route_handlers->{$handler_name} = $handler; | |
444 | ||
445 | push @{ $self->route_handlers }, | |
446 | { name => $handler_name, | |
447 | handler => $handler, | |
448 | }; | |
403 | 449 | } |
404 | 450 | } |
405 | 451 | |
406 | 452 | sub register_route_handlers { |
407 | 453 | 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); | |
411 | 457 | } |
412 | 458 | } |
413 | 459 | |
435 | 481 | } |
436 | 482 | } |
437 | 483 | |
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 | ||
480 | 484 | sub lexical_prefix { |
481 | 485 | my ( $self, $prefix, $cb ) = @_; |
482 | 486 | undef $prefix if $prefix eq '/'; |
502 | 506 | if $e; |
503 | 507 | } |
504 | 508 | |
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 | ||
521 | 509 | sub add_route { |
522 | 510 | my ( $self, %route_attrs ) = @_; |
523 | 511 | |
529 | 517 | push @{ $self->routes->{$method} }, $route; |
530 | 518 | } |
531 | 519 | |
532 | ||
533 | 520 | sub route_exists { |
534 | 521 | my ( $self, $route ) = @_; |
535 | 522 | |
540 | 527 | } |
541 | 528 | return 0; |
542 | 529 | } |
543 | ||
544 | 530 | |
545 | 531 | sub routes_regexps_for { |
546 | 532 | my ( $self, $method ) = @_; |
559 | 545 | |
560 | 546 | =head1 VERSION |
561 | 547 | |
562 | version 0.07 | |
548 | version 0.09 | |
563 | 549 | |
564 | 550 | =head1 DESCRIPTION |
565 | 551 |
0 | 0 | package Dancer2::Core::Context; |
1 | 1 | { |
2 | $Dancer2::Core::Context::VERSION = '0.07'; | |
2 | $Dancer2::Core::Context::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: handles everything proper to a request's context. |
15 | 15 | |
16 | 16 | |
17 | 17 | 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, | |
21 | 22 | ); |
22 | 23 | |
23 | 24 | |
40 | 41 | |
41 | 42 | sub _build_request { |
42 | 43 | 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 | ||
47 | 59 | return $req; |
48 | 60 | } |
49 | 61 | |
75 | 87 | default => sub { |
76 | 88 | my $self = shift; |
77 | 89 | 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 | } | |
80 | 94 | return $resp; |
81 | 95 | }, |
82 | 96 | ); |
111 | 125 | $destination = $self->request->uri_for( $destination, {}, 1 ); |
112 | 126 | } |
113 | 127 | |
128 | $self->response->halt; | |
114 | 129 | $self->response->redirect( $destination, $status ); |
115 | 130 | } |
116 | 131 | |
201 | 216 | |
202 | 217 | =head1 VERSION |
203 | 218 | |
204 | version 0.07 | |
219 | version 0.09 | |
205 | 220 | |
206 | 221 | =head1 ATTRIBUTES |
207 | 222 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Cookie; |
3 | 3 | { |
4 | $Dancer2::Core::Cookie::VERSION = '0.07'; | |
4 | $Dancer2::Core::Cookie::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use URI::Escape; |
114 | 114 | |
115 | 115 | =head1 VERSION |
116 | 116 | |
117 | version 0.07 | |
117 | version 0.09 | |
118 | 118 | |
119 | 119 | =head1 SYNOPSIS |
120 | 120 |
1 | 1 | |
2 | 2 | package Dancer2::Core::DSL; |
3 | 3 | { |
4 | $Dancer2::Core::DSL::VERSION = '0.07'; | |
4 | $Dancer2::Core::DSL::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use Moo; |
8 | use Carp; | |
9 | use Class::Load 'load_class'; | |
8 | 10 | use Dancer2::Core::Hook; |
9 | 11 | use Dancer2::Core::Error; |
10 | 12 | use Dancer2::FileUtils; |
11 | use Dancer2::ModuleLoader; | |
12 | use Carp; | |
13 | 13 | |
14 | 14 | with 'Dancer2::Core::Role::DSL'; |
15 | 15 | |
18 | 18 | # the flag means : 1 = is global, 0 = is not global. global means can be |
19 | 19 | # called from anywhere. not global means must be called from within a route |
20 | 20 | # 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 | }; | |
86 | 85 | } |
87 | 86 | |
88 | 87 | sub dancer_app { shift->app } |
129 | 128 | $self->app->add_hook( |
130 | 129 | Dancer2::Core::Hook->new( name => $name, code => $code ) ); |
131 | 130 | } |
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 | ||
147 | 131 | |
148 | 132 | sub prefix { |
149 | 133 | my $app = shift->app; |
392 | 376 | |
393 | 377 | =head1 VERSION |
394 | 378 | |
395 | version 0.07 | |
379 | version 0.09 | |
396 | 380 | |
397 | 381 | =head1 FUNCTIONS |
398 | 382 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Dispatcher; |
3 | 3 | { |
4 | $Dancer2::Core::Dispatcher::VERSION = '0.07'; | |
4 | $Dancer2::Core::Dispatcher::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Encode; |
65 | 65 | or next ROUTE; |
66 | 66 | |
67 | 67 | $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 | } | |
76 | 68 | |
77 | 69 | # if the request has been altered by a before filter, we should not continue |
78 | 70 | # with this route handler, we should continue to walk through the |
125 | 117 | |
126 | 118 | # pass the baton if the response says so... |
127 | 119 | 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 | ||
128 | 127 | $response->has_passed(0); # clear for the next round |
129 | 128 | next ROUTE; |
130 | 129 | } |
177 | 176 | environment => Dancer2->runner->environment, |
178 | 177 | location => Dancer2->runner->location, |
179 | 178 | runner_config => Dancer2->runner->config, |
180 | postponed_hooks => Dancer2->runner->postponed_hooks, | |
179 | postponed_hooks => Dancer2->runner->server->postponed_hooks, | |
181 | 180 | api_version => 2, |
182 | 181 | ); |
183 | 182 | |
203 | 202 | |
204 | 203 | =head1 VERSION |
205 | 204 | |
206 | version 0.07 | |
205 | version 0.09 | |
207 | 206 | |
208 | 207 | =head1 SYNOPSIS |
209 | 208 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Error; |
3 | 3 | { |
4 | $Dancer2::Core::Error::VERSION = '0.07'; | |
4 | $Dancer2::Core::Error::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Carp; |
8 | 8 | use Dancer2::Core::Types; |
9 | use Dancer2::Core::HTTP; | |
9 | 10 | use Data::Dumper; |
10 | 11 | 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 | ); | |
68 | 12 | |
69 | 13 | |
70 | 14 | has show_errors => ( |
100 | 44 | sub _build_title { |
101 | 45 | my ($self) = @_; |
102 | 46 | 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 | } | |
105 | 50 | |
106 | 51 | return $title; |
107 | 52 | } |
503 | 448 | |
504 | 449 | =head1 VERSION |
505 | 450 | |
506 | version 0.07 | |
451 | version 0.09 | |
507 | 452 | |
508 | 453 | =head1 SYNOPSIS |
509 | 454 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Factory; |
3 | 3 | { |
4 | $Dancer2::Core::Factory::VERSION = '0.07'; | |
4 | $Dancer2::Core::Factory::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use strict; |
7 | 7 | use warnings; |
8 | 8 | |
9 | use Dancer2::ModuleLoader; | |
9 | use Dancer2::Core; | |
10 | use Class::Load 'try_load_class'; | |
10 | 11 | use Carp 'croak'; |
11 | 12 | |
12 | 13 | sub create { |
13 | 14 | my ( $class, $type, $name, %options ) = @_; |
14 | 15 | |
15 | $type = _camelize($type); | |
16 | $name = _camelize($name); | |
16 | $type = Dancer2::Core::camelize($type); | |
17 | $name = Dancer2::Core::camelize($name); | |
17 | 18 | my $component_class = "Dancer2::${type}::${name}"; |
18 | 19 | |
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"; | |
23 | 22 | |
24 | 23 | 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; | |
35 | 24 | } |
36 | 25 | |
37 | 26 | 1; |
46 | 35 | |
47 | 36 | =head1 VERSION |
48 | 37 | |
49 | version 0.07 | |
38 | version 0.09 | |
50 | 39 | |
51 | 40 | =head1 AUTHOR |
52 | 41 |
1 | 1 | |
2 | 2 | package Dancer2::Core::HTTP; |
3 | 3 | { |
4 | $Dancer2::Core::HTTP::VERSION = '0.07'; | |
4 | $Dancer2::Core::HTTP::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
10 | 10 | my $HTTP_CODES = { |
11 | 11 | |
12 | 12 | # 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 | |
15 | 16 | |
16 | # processed codes | |
17 | # processed | |
17 | 18 | 200 => 'OK', |
18 | 19 | 201 => 'Created', |
19 | 20 | 202 => 'Accepted', |
20 | ||
21 | # 203 => 'Non-Authoritative Information', # only on HTTP 1.1 | |
21 | 203 => 'Non-Authoritative Information', # only on HTTP 1.1 | |
22 | 22 | 204 => 'No Content', |
23 | 23 | 205 => 'Reset Content', |
24 | 24 | 206 => 'Partial Content', |
25 | 207 => 'Multi-Status', # WebDAV; RFC 4918 | |
26 | 208 => 'Already Reported', # WebDAV; RFC 5842 | |
27 | ||
28 | # 226 => 'IM Used' # RFC 3229 | |
25 | 29 | |
26 | 30 | # redirections |
27 | 31 | 301 => 'Moved Permanently', |
28 | 32 | 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 | |
29 | 38 | |
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 | |
37 | 40 | |
38 | 41 | # problems with request |
39 | 42 | 400 => 'Bad Request', |
54 | 57 | 415 => 'Unsupported Media Type', |
55 | 58 | 416 => 'Requested Range Not Satisfiable', |
56 | 59 | 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', | |
57 | 82 | |
58 | 83 | # problems with server |
59 | 84 | 500 => 'Internal Server Error', |
62 | 87 | 503 => 'Service Unavailable', |
63 | 88 | 504 => 'Gateway Timeout', |
64 | 89 | 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', | |
65 | 98 | }; |
66 | 99 | |
67 | 100 | for my $code ( keys %$HTTP_CODES ) { |
77 | 110 | |
78 | 111 | sub status { |
79 | 112 | my ( $class, $status ) = @_; |
113 | return if !defined $status; | |
80 | 114 | return $status if $status =~ /^\d+/; |
81 | 115 | if ( exists $HTTP_CODES->{$status} ) { |
82 | 116 | return $HTTP_CODES->{$status}; |
83 | 117 | } |
84 | 118 | 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}; | |
85 | 128 | } |
86 | 129 | |
87 | 130 | 1; |
96 | 139 | |
97 | 140 | =head1 VERSION |
98 | 141 | |
99 | version 0.07 | |
142 | version 0.09 | |
100 | 143 | |
101 | 144 | =head1 FUNCTIONS |
102 | 145 | |
112 | 155 | received, else it will try to find the appropriate alias and return the correct |
113 | 156 | status. |
114 | 157 | |
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 | ||
115 | 166 | =head1 AUTHOR |
116 | 167 | |
117 | 168 | Dancer Core Developers |
0 | 0 | # ABSTRACT: Manipulate hooks with Dancer2 |
1 | 1 | package Dancer2::Core::Hook; |
2 | 2 | { |
3 | $Dancer2::Core::Hook::VERSION = '0.07'; | |
3 | $Dancer2::Core::Hook::VERSION = '0.09'; | |
4 | 4 | } |
5 | 5 | use Moo; |
6 | 6 | use Dancer2::Core::Types; |
52 | 52 | |
53 | 53 | =head1 VERSION |
54 | 54 | |
55 | version 0.07 | |
55 | version 0.09 | |
56 | 56 | |
57 | 57 | =head1 SYNOPSIS |
58 | 58 |
1 | 1 | |
2 | 2 | package Dancer2::Core::MIME; |
3 | 3 | { |
4 | $Dancer2::Core::MIME::VERSION = '0.07'; | |
4 | $Dancer2::Core::MIME::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
92 | 92 | |
93 | 93 | =head1 VERSION |
94 | 94 | |
95 | version 0.07 | |
95 | version 0.09 | |
96 | 96 | |
97 | 97 | =head1 SYNOPSIS |
98 | 98 |
0 | 0 | package Dancer2::Core::Request::Upload; |
1 | 1 | { |
2 | $Dancer2::Core::Request::Upload::VERSION = '0.07'; | |
2 | $Dancer2::Core::Request::Upload::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Class representing file upload requests |
100 | 100 | |
101 | 101 | =head1 VERSION |
102 | 102 | |
103 | version 0.07 | |
103 | version 0.09 | |
104 | 104 | |
105 | 105 | =head1 DESCRIPTION |
106 | 106 |
0 | 0 | package Dancer2::Core::Request; |
1 | 1 | { |
2 | $Dancer2::Core::Request::VERSION = '0.07'; | |
2 | $Dancer2::Core::Request::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Interface for accessing incoming requests |
142 | 142 | isa => Num, |
143 | 143 | ); |
144 | 144 | |
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 | ||
145 | 196 | |
146 | 197 | has uploads => ( |
147 | 198 | is => 'rw', |
148 | 199 | isa => HashRef, |
149 | ); | |
150 | ||
151 | # Really needed? as we have is_ajax() ... | |
152 | has ajax => ( | |
153 | is => 'rw', | |
154 | isa => Bool, | |
155 | 200 | ); |
156 | 201 | |
157 | 202 | has body_is_parsed => ( |
228 | 273 | sub deserialize { |
229 | 274 | my $self = shift; |
230 | 275 | |
231 | return unless $self->serializer; | |
276 | return unless $self->has_serializer; | |
232 | 277 | |
233 | 278 | # Content-Type may contain additional parameters |
234 | 279 | # (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7) |
242 | 287 | unless grep { $self->method eq $_ } qw/ PUT POST PATCH /; |
243 | 288 | |
244 | 289 | # try to deserialize |
290 | my $body = $self->_read_to_end(); | |
245 | 291 | my $data = $self->serializer->deserialize( $self->body ); |
246 | 292 | return if !defined $data; |
247 | 293 | |
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).. | |
248 | 298 | $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(); | |
255 | 300 | |
256 | 301 | return $data; |
257 | 302 | } |
279 | 324 | |
280 | 325 | $self->{_chunk_size} = 4096; |
281 | 326 | $self->{_read_position} = 0; |
282 | $self->{_body_params} = undef; | |
283 | $self->{_query_params} = undef; | |
284 | $self->{_route_params} = {}; | |
285 | 327 | |
286 | 328 | $self->_init_request_headers(); |
287 | 329 | |
289 | 331 | HTTP::Body->new( $self->content_type, $self->content_length ); |
290 | 332 | $self->{_http_body}->cleanup(1); |
291 | 333 | |
292 | $self->_build_params(); | |
334 | $self->data; # Deserialize body | |
335 | $self->_params(); # Decode query and body prams | |
293 | 336 | $self->_build_uploads(); |
294 | ||
295 | $self->{ajax} = $self->is_ajax; | |
296 | 337 | } |
297 | 338 | |
298 | 339 | |
319 | 360 | $new_request->method( $options->{method} ); |
320 | 361 | } |
321 | 362 | |
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; | |
329 | 370 | |
330 | 371 | return $new_request; |
331 | 372 | } |
335 | 376 | my ( $self, $context, $url, $params, $options ) = @_; |
336 | 377 | my $new_request = $self->make_forward_to( $url, $params, $options ); |
337 | 378 | |
338 | return Dancer2->runner->server->dispatcher->dispatch( | |
379 | my $new_response = Dancer2->runner->server->dispatcher->dispatch( | |
339 | 380 | $new_request->env, |
340 | 381 | $new_request, |
341 | 382 | $context, |
342 | 383 | ); |
384 | $new_response->halt; | |
385 | $context->response($new_response); | |
386 | return $new_response; | |
343 | 387 | } |
344 | 388 | |
345 | 389 | sub _merge_params { |
390 | 434 | } |
391 | 435 | |
392 | 436 | |
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 | ||
393 | 458 | sub uri_for { |
394 | 459 | my ( $self, $part, $params, $dont_escape ) = @_; |
395 | 460 | |
409 | 474 | |
410 | 475 | sub params { |
411 | 476 | 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; | |
424 | 480 | |
425 | 481 | 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; | |
428 | 484 | } |
429 | 485 | 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; | |
432 | 488 | } |
433 | 489 | 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; | |
436 | 492 | } |
437 | 493 | else { |
438 | 494 | croak "Unknown source params \"$source\"."; |
488 | 544 | return ( ref($res) eq 'ARRAY' ) ? @$res : $res; |
489 | 545 | } |
490 | 546 | |
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 | ||
510 | 547 | sub _build_params { |
511 | 548 | my ($self) = @_; |
512 | 549 | |
513 | 550 | # params may have been populated by before filters |
514 | 551 | # _before_ we get there, so we have to save it first |
515 | my $previous = $self->{params} || {}; | |
552 | my $previous = $self->_has_params ? $self->_params : {}; | |
516 | 553 | |
517 | 554 | # now parse environement params... |
518 | 555 | $self->_parse_get_params(); |
519 | if ( $self->{body_is_parsed} ) { | |
556 | if ( $self->body_is_parsed ) { | |
520 | 557 | $self->{_body_params} ||= {}; |
521 | 558 | } |
522 | 559 | else { |
524 | 561 | } |
525 | 562 | |
526 | 563 | # 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 || {} }, | |
530 | 567 | }; |
531 | 568 | |
532 | 569 | } |
542 | 579 | |
543 | 580 | sub _parse_post_params { |
544 | 581 | my ($self) = @_; |
545 | return $self->{_body_params} if defined $self->{_body_params}; | |
582 | return $self->_body_params if defined $self->_body_params; | |
546 | 583 | |
547 | 584 | my $body = $self->_read_to_end(); |
548 | $self->{_body_params} = $self->{_http_body}->param; | |
585 | $self->_set_body_params( $self->{_http_body}->param ); | |
549 | 586 | } |
550 | 587 | |
551 | 588 | sub _parse_get_params { |
552 | 589 | 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 = {}; | |
556 | 593 | |
557 | 594 | my $source = $self->env->{QUERY_STRING}; |
558 | 595 | return if !defined $source || $source eq ''; |
559 | 596 | |
560 | 597 | 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; | |
563 | 601 | } |
564 | 602 | |
565 | 603 | foreach my $token ( split /[&;]/, $source ) { |
570 | 608 | $val = $self->_url_decode($val); |
571 | 609 | |
572 | 610 | # 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}; | |
575 | 613 | if ( ref($prev_val) && ref($prev_val) eq 'ARRAY' ) { |
576 | push @{ $self->{_query_params}{$key} }, $val; | |
614 | push @{ $query_params->{$key} }, $val; | |
577 | 615 | } |
578 | 616 | else { |
579 | $self->{_query_params}{$key} = [ $prev_val, $val ]; | |
617 | $query_params->{$key} = [ $prev_val, $val ]; | |
580 | 618 | } |
581 | 619 | } |
582 | 620 | |
583 | 621 | # simple value param (first time we see it) |
584 | 622 | else { |
585 | $self->{_query_params}{$key} = $val; | |
623 | $query_params->{$key} = $val; | |
586 | 624 | } |
587 | 625 | } |
588 | return $self->{_query_params}; | |
626 | $self->_set_query_params($query_params); | |
627 | return $self->_query_params; | |
589 | 628 | } |
590 | 629 | |
591 | 630 | sub _read_to_end { |
727 | 766 | |
728 | 767 | =head1 VERSION |
729 | 768 | |
730 | version 0.07 | |
769 | version 0.09 | |
731 | 770 | |
732 | 771 | =head1 SYNOPSIS |
733 | 772 | |
811 | 850 | It also accepts the C<body_is_parsed> boolean flag, if the new request object should |
812 | 851 | not parse request body. |
813 | 852 | |
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 | ||
830 | 853 | =head2 address() |
831 | 854 | |
832 | 855 | Return the IP address of the client. |
939 | 962 | |
940 | 963 | <link rel="stylesheet" href="[% request.uri_base %]/css/style.css" /> |
941 | 964 | |
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 | ||
942 | 970 | =head2 uri_for(path, params) |
943 | 971 | |
944 | 972 | Constructs a URI from the base and the passed path. If params (hashref) is |
1 | 1 | |
2 | 2 | package Dancer2::Core::Response; |
3 | 3 | { |
4 | $Dancer2::Core::Response::VERSION = '0.07'; | |
4 | $Dancer2::Core::Response::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
213 | 213 | |
214 | 214 | =head1 VERSION |
215 | 215 | |
216 | version 0.07 | |
216 | version 0.09 | |
217 | 217 | |
218 | 218 | =head1 ATTRIBUTES |
219 | 219 |
0 | # ABSTRACT: Config role for Dancer2 core objects | |
0 | 1 | package Dancer2::Core::Role::Config; |
1 | 2 | { |
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 | } | |
6 | 5 | |
7 | 6 | use Moo::Role; |
8 | 7 | |
9 | 8 | use Dancer2::Core::Factory; |
9 | use Dancer2::Core; | |
10 | 10 | use File::Spec; |
11 | 11 | use Config::Any; |
12 | 12 | use Dancer2::Core::Types; |
15 | 15 | use Carp 'croak', 'carp'; |
16 | 16 | |
17 | 17 | 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', | |
28 | 21 | ); |
29 | 22 | |
30 | 23 | has config_location => ( |
71 | 64 | builder => '_build_environment', |
72 | 65 | ); |
73 | 66 | |
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 | ||
74 | 97 | sub _build_environment { |
75 | 98 | $ENV{DANCER_ENVIRONMENT} || $ENV{PLACK_ENV} || 'development'; |
76 | 99 | } |
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 | ); | |
121 | 100 | |
122 | 101 | sub _build_config_files { |
123 | 102 | my ($self) = @_; |
146 | 125 | return [ sort @files ]; |
147 | 126 | } |
148 | 127 | |
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 | ||
149 | 199 | sub load_config_file { |
150 | 200 | my ( $self, $file ) = @_; |
151 | 201 | my $config; |
167 | 217 | sub get_postponed_hooks { |
168 | 218 | my ($self) = @_; |
169 | 219 | 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 | # : {}; | |
180 | 220 | } |
181 | 221 | |
182 | 222 | # 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 | } | |
238 | 223 | |
239 | 224 | my $_normalizers = { |
240 | 225 | charset => sub { |
342 | 327 | |
343 | 328 | # XXX we need to move the camilize function out from Core::Factory |
344 | 329 | # - 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) ) { | |
346 | 331 | $engine_config = $config->{engines}{$engine}{$config_key} |
347 | 332 | if defined $config->{engines}->{$engine}{$config_key}; |
348 | 333 | } |
374 | 359 | my $engine_options = |
375 | 360 | $self->_get_config_for_engine( logger => $value, $config ); |
376 | 361 | |
377 | return Dancer2::Core::Factory->create( | |
362 | my $logger = Dancer2::Core::Factory->create( | |
378 | 363 | logger => $value, |
379 | 364 | %{$engine_options}, |
380 | 365 | app_name => $self->name, |
381 | 366 | postponed_hooks => $self->get_postponed_hooks |
382 | 367 | ); |
368 | ||
369 | $logger->log_level( $config->{log} ) if exists $config->{log}; | |
370 | ||
371 | return $logger; | |
383 | 372 | } |
384 | 373 | |
385 | 374 | sub _build_engine_session { |
456 | 445 | |
457 | 446 | =head1 VERSION |
458 | 447 | |
459 | version 0.07 | |
448 | version 0.09 | |
460 | 449 | |
461 | 450 | =head1 DESCRIPTION |
462 | 451 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::DSL; |
3 | 3 | { |
4 | $Dancer2::Core::Role::DSL::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::DSL::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo::Role; |
7 | 7 | use Dancer2::Core::Types; |
13 | 13 | |
14 | 14 | has keywords => ( |
15 | 15 | is => 'rw', |
16 | isa => ArrayRef, | |
16 | isa => HashRef, | |
17 | 17 | lazy => 1, |
18 | 18 | builder => '_build_dsl_keywords', |
19 | 19 | ); |
24 | 24 | my ($self) = @_; |
25 | 25 | $self->can('dsl_keywords') |
26 | 26 | ? $self->dsl_keywords |
27 | : []; | |
27 | : {}; | |
28 | 28 | } |
29 | 29 | |
30 | 30 | sub register { |
31 | 31 | my ( $self, $keyword, $is_global ) = @_; |
32 | my $keywords = $self->keywords; | |
33 | my $pkg = ref($self); | |
34 | $pkg =~ s/__WITH__.+$//; | |
32 | 35 | |
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; | |
35 | 40 | |
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 }; | |
37 | 45 | } |
38 | 46 | |
39 | 47 | sub dsl { $_[0] } |
40 | ||
41 | sub dsl_keywords_as_list { | |
42 | map { $_->[0] } @{ shift->dsl_keywords() }; | |
43 | } | |
44 | 48 | |
45 | 49 | # exports new symbol to caller |
46 | 50 | sub export_symbols_to { |
65 | 69 | my ( $self, $keyword, $is_global ) = @_; |
66 | 70 | |
67 | 71 | my $compiled_code = sub { |
68 | Dancer2::core_debug( "[" | |
72 | Dancer2::Core::debug( "[" | |
69 | 73 | . $self->app->name |
70 | 74 | . "] -> $keyword(" |
71 | 75 | . join( ', ', map { defined() ? $_ : '<undef>' } @_ ) |
87 | 91 | |
88 | 92 | sub _construct_export_map { |
89 | 93 | my ( $self, $args ) = @_; |
94 | my $keywords = $self->keywords; | |
90 | 95 | my %map; |
91 | foreach my $keyword ( @{ $self->keywords } ) { | |
92 | my ( $keyword, $is_global ) = @{$keyword}; | |
96 | foreach my $keyword ( keys %$keywords ) { | |
93 | 97 | |
94 | 98 | # 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} ); | |
98 | 103 | } |
99 | 104 | return \%map; |
100 | 105 | } |
111 | 116 | |
112 | 117 | =head1 VERSION |
113 | 118 | |
114 | version 0.07 | |
119 | version 0.09 | |
115 | 120 | |
116 | 121 | =head1 AUTHOR |
117 | 122 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Engine; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Engine::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Engine::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo::Role; |
7 | 7 | use Dancer2::Core::Types; |
41 | 41 | |
42 | 42 | =head1 VERSION |
43 | 43 | |
44 | version 0.07 | |
44 | version 0.09 | |
45 | 45 | |
46 | 46 | =head1 DESCRIPTION |
47 | 47 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Handler; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Handler::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Handler::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo::Role; |
7 | 7 | use Dancer2::Core::Types; |
27 | 27 | |
28 | 28 | =head1 VERSION |
29 | 29 | |
30 | version 0.07 | |
30 | version 0.09 | |
31 | 31 | |
32 | 32 | =head1 ATTRIBUTES |
33 | 33 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Headers; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Headers::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Headers::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | |
82 | 82 | |
83 | 83 | =head1 VERSION |
84 | 84 | |
85 | version 0.07 | |
85 | version 0.09 | |
86 | 86 | |
87 | 87 | =head1 DESCRIPTION |
88 | 88 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Hookable; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Hookable::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Hookable::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo::Role; |
7 | use Dancer2::Core; | |
7 | 8 | use Dancer2::Core::Types; |
8 | 9 | use Carp 'croak'; |
9 | 10 | |
68 | 69 | $h_type = 'engine'; |
69 | 70 | } |
70 | 71 | |
71 | # Dancer2::core_debug("looking for hooks for $h_type/$h_name"); | |
72 | # Dancer2::Core::debug("looking for hooks for $h_type/$h_name"); | |
72 | 73 | # keep only the hooks we want |
73 | 74 | $postponed_hooks = $postponed_hooks->{$h_type}{$h_name}; |
74 | 75 | return unless defined $postponed_hooks; |
81 | 82 | or croak "$h_name $h_type does not support the hook `$name'. (" |
82 | 83 | . join( ", ", @{$caller} ) . ")"; |
83 | 84 | |
84 | # Dancer2::core_debug("Adding hook '$name' to $self"); | |
85 | # Dancer2::Core::debug("Adding hook '$name' to $self"); | |
85 | 86 | $self->add_hook($hook); |
86 | 87 | } |
87 | 88 | } |
157 | 158 | |
158 | 159 | =head1 VERSION |
159 | 160 | |
160 | version 0.07 | |
161 | version 0.09 | |
161 | 162 | |
162 | 163 | =head1 AUTHOR |
163 | 164 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Logger; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Logger::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Logger::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Dancer2::Core::Types; |
7 | 7 | |
34 | 34 | is => 'ro', |
35 | 35 | isa => Str, |
36 | 36 | ); |
37 | ||
38 | 37 | |
39 | 38 | has log_format => ( |
40 | 39 | is => 'rw', |
77 | 76 | $message = Encode::encode( $self->auto_encoding_charset, $message ) |
78 | 77 | if $self->auto_encoding_charset; |
79 | 78 | |
80 | my @stack = caller(2); | |
79 | my @stack = caller(6); | |
81 | 80 | |
82 | 81 | my $block_handler = sub { |
83 | 82 | my ( $block, $type ) = @_; |
177 | 176 | |
178 | 177 | =head1 VERSION |
179 | 178 | |
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 | |
181 | 307 | |
182 | 308 | =head1 AUTHOR |
183 | 309 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Serializer; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Serializer::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Serializer::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Dancer2::Core::Types; |
7 | 7 | |
17 | 17 | |
18 | 18 | sub _build_type {'Serializer'} |
19 | 19 | |
20 | ||
21 | 20 | requires 'serialize'; |
22 | 21 | requires 'deserialize'; |
23 | 22 | requires 'loaded'; |
29 | 28 | ); |
30 | 29 | |
31 | 30 | 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 ); }; | |
35 | 35 | |
36 | 36 | if ($@) { |
37 | 37 | $self->error($@); |
43 | 43 | }; |
44 | 44 | |
45 | 45 | 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 ); }; | |
48 | 48 | $self->error($@) if $@; |
49 | 49 | return $data; |
50 | 50 | }; |
51 | 51 | |
52 | 52 | # attribute vs method? |
53 | sub content_type {'text/plain'} | |
53 | has content_type => ( | |
54 | is => 'ro', | |
55 | isa => Str, | |
56 | required => 1, | |
57 | ); | |
54 | 58 | |
55 | 59 | # most serializer don't have to overload this one |
56 | 60 | sub support_content_type { |
74 | 78 | |
75 | 79 | =head1 VERSION |
76 | 80 | |
77 | version 0.07 | |
81 | version 0.09 | |
78 | 82 | |
79 | =head1 REQUIREMENTS | |
83 | =head1 DESCRIPTION | |
80 | 84 | |
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 | |
83 | 138 | |
84 | 139 | =head1 AUTHOR |
85 | 140 |
0 | 0 | # ABSTRACT: Role for Server classes |
1 | ||
2 | 1 | package Dancer2::Core::Role::Server; |
3 | 2 | { |
4 | $Dancer2::Core::Role::Server::VERSION = '0.07'; | |
3 | $Dancer2::Core::Role::Server::VERSION = '0.09'; | |
5 | 4 | } |
6 | 5 | use Moo::Role; |
7 | 6 | |
15 | 14 | use Dancer2::Core::Request; |
16 | 15 | use Dancer2::Core::Context; |
17 | 16 | |
17 | requires '_build_name'; | |
18 | 18 | |
19 | 19 | has name => ( |
20 | 20 | is => 'ro', |
22 | 22 | builder => 1, |
23 | 23 | ); |
24 | 24 | |
25 | ||
26 | 25 | has host => ( |
27 | 26 | is => 'rw', |
28 | 27 | isa => Str, |
29 | 28 | required => 1, |
30 | 29 | ); |
31 | ||
32 | 30 | |
33 | 31 | has port => ( |
34 | 32 | is => 'rw', |
36 | 34 | required => 1, |
37 | 35 | ); |
38 | 36 | |
39 | ||
40 | 37 | has is_daemon => ( |
41 | 38 | is => 'rw', |
42 | 39 | isa => Bool, |
43 | 40 | ); |
44 | ||
45 | 41 | |
46 | 42 | has apps => ( |
47 | 43 | is => 'ro', |
48 | 44 | isa => ArrayRef, |
49 | 45 | default => sub { [] }, |
50 | 46 | ); |
51 | ||
52 | has runner => ( | |
53 | is => 'ro', | |
54 | required => 1, | |
55 | isa => InstanceOf ['Dancer2::Core::Runner'], | |
56 | weak_ref => 1, | |
57 | ); | |
58 | ||
59 | 47 | |
60 | 48 | has dispatcher => ( |
61 | 49 | is => 'rw', |
64 | 52 | builder => '_build_dispatcher', |
65 | 53 | ); |
66 | 54 | |
67 | requires '_build_name'; | |
55 | has postponed_hooks => ( | |
56 | is => 'rw', | |
57 | isa => HashRef, | |
58 | default => sub { {} }, | |
59 | ); | |
68 | 60 | |
69 | 61 | sub _build_dispatcher { |
70 | 62 | my ($self) = @_; |
72 | 64 | $d->apps( $self->apps ); |
73 | 65 | return $d; |
74 | 66 | } |
75 | ||
76 | 67 | |
77 | 68 | # our PSGI application |
78 | 69 | sub psgi_app { |
94 | 85 | }; |
95 | 86 | } |
96 | 87 | |
97 | ||
98 | 88 | sub register_application { |
99 | 89 | my ( $self, $app ) = @_; |
100 | 90 | push @{ $self->apps }, $app; |
101 | 91 | $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}, | |
105 | 104 | } |
106 | 105 | ); |
107 | 106 | } |
118 | 117 | |
119 | 118 | =head1 VERSION |
120 | 119 | |
121 | version 0.07 | |
120 | version 0.09 | |
122 | 121 | |
123 | 122 | =head1 DESCRIPTION |
124 | 123 | |
157 | 156 | |
158 | 157 | It has a lazy builder that creates a new dispatcher with the server's apps. |
159 | 158 | |
159 | =head2 postponed_hooks | |
160 | ||
161 | Postponed hooks will be applied at the end, when the hookable objects are | |
162 | instantiated, not before. | |
163 | ||
160 | 164 | =head1 METHODS |
161 | 165 | |
162 | 166 | =head2 psgi_app |
0 | 0 | package Dancer2::Core::Role::SessionFactory::File; |
1 | 1 | { |
2 | $Dancer2::Core::Role::SessionFactory::File::VERSION = '0.07'; | |
2 | $Dancer2::Core::Role::SessionFactory::File::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | #ABSTRACT: Role for file-based session factories |
114 | 114 | |
115 | 115 | =head1 VERSION |
116 | 116 | |
117 | version 0.07 | |
117 | version 0.09 | |
118 | 118 | |
119 | 119 | =head1 DESCRIPTION |
120 | 120 |
0 | 0 | package Dancer2::Core::Role::SessionFactory; |
1 | 1 | { |
2 | $Dancer2::Core::Role::SessionFactory::VERSION = '0.07'; | |
2 | $Dancer2::Core::Role::SessionFactory::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | #ABSTRACT: Role for session factories |
8 | 8 | use strict; |
9 | 9 | use warnings; |
10 | 10 | use Carp 'croak'; |
11 | use Class::Load 'try_load_class'; | |
11 | 12 | use Dancer2::Core::Session; |
12 | 13 | use Dancer2::Core::Types; |
13 | use Dancer2::ModuleLoader; | |
14 | 14 | use Digest::SHA 'sha1'; |
15 | 15 | use List::Util 'shuffle'; |
16 | 16 | use MIME::Base64 'encode_base64url'; |
112 | 112 | |
113 | 113 | { |
114 | 114 | 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'); | |
117 | 117 | |
118 | 118 | # don't initialize until generate_id is called so the ISAAC algorithm |
119 | 119 | # is seeded after any pre-forking |
271 | 271 | |
272 | 272 | =head1 VERSION |
273 | 273 | |
274 | version 0.07 | |
274 | version 0.09 | |
275 | 275 | |
276 | 276 | =head1 DESCRIPTION |
277 | 277 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::StandardResponses; |
3 | 3 | { |
4 | $Dancer2::Core::Role::StandardResponses::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::StandardResponses::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo::Role; |
7 | 7 | |
43 | 43 | |
44 | 44 | =head1 VERSION |
45 | 45 | |
46 | version 0.07 | |
46 | version 0.09 | |
47 | 47 | |
48 | 48 | =head1 METHODS |
49 | 49 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Role::Template; |
3 | 3 | { |
4 | $Dancer2::Core::Role::Template::VERSION = '0.07'; | |
4 | $Dancer2::Core::Role::Template::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use Dancer2::Core::Types; |
11 | 11 | use Data::Dumper; |
12 | 12 | use Moo::Role; |
13 | 13 | with 'Dancer2::Core::Role::Engine'; |
14 | ||
15 | 14 | |
16 | 15 | sub supported_hooks { |
17 | 16 | qw/ |
26 | 25 | |
27 | 26 | requires 'render'; |
28 | 27 | |
29 | ||
30 | 28 | has name => ( |
31 | 29 | is => 'ro', |
32 | 30 | lazy => 1, |
38 | 36 | $name; |
39 | 37 | } |
40 | 38 | |
41 | ||
42 | 39 | has charset => ( |
43 | 40 | is => 'ro', |
44 | 41 | isa => Str, |
45 | 42 | default => sub {'UTF-8'}, |
46 | 43 | ); |
47 | 44 | |
48 | ||
49 | 45 | has default_tmpl_ext => ( |
50 | 46 | is => 'rw', |
51 | 47 | isa => Str, |
52 | 48 | default => sub { shift->config->{extension} || 'tt' }, |
53 | 49 | ); |
54 | 50 | |
55 | ||
56 | 51 | has views => ( |
57 | 52 | is => 'rw', |
58 | 53 | isa => Maybe [Str], |
59 | 54 | ); |
60 | 55 | |
61 | ||
62 | 56 | has layout => ( |
63 | 57 | is => 'rw', |
64 | 58 | isa => Maybe [Str], |
65 | 59 | ); |
66 | ||
67 | 60 | |
68 | 61 | has engine => ( |
69 | 62 | is => 'ro', |
79 | 72 | return $view; |
80 | 73 | } |
81 | 74 | |
82 | ||
83 | 75 | sub view_pathname { |
84 | 76 | my ( $self, $view ) = @_; |
85 | 77 | |
86 | 78 | $view = $self->_template_name($view); |
87 | 79 | return path( $self->views, $view ); |
88 | 80 | } |
89 | ||
90 | 81 | |
91 | 82 | sub layout_pathname { |
92 | 83 | my ( $self, $layout ) = @_; |
94 | 85 | return path( $self->views, 'layouts', $layout ); |
95 | 86 | } |
96 | 87 | |
97 | ||
98 | 88 | sub render_layout { |
99 | 89 | my ( $self, $layout, $tokens, $content ) = @_; |
100 | 90 | |
103 | 93 | # FIXME: not sure if I can "just call render" |
104 | 94 | $self->render( $layout, { %$tokens, content => $content } ); |
105 | 95 | } |
106 | ||
107 | 96 | |
108 | 97 | sub apply_renderer { |
109 | 98 | my ( $self, $view, $tokens ) = @_; |
119 | 108 | defined $content and return $content; |
120 | 109 | return; |
121 | 110 | } |
122 | ||
123 | 111 | |
124 | 112 | sub apply_layout { |
125 | 113 | my ( $self, $content, $tokens, $options ) = @_; |
178 | 166 | return $tokens; |
179 | 167 | } |
180 | 168 | |
181 | ||
182 | 169 | sub process { |
183 | 170 | my ( $self, $view, $tokens, $options ) = @_; |
184 | 171 | my ( $content, $full_content ); |
216 | 203 | |
217 | 204 | =head1 VERSION |
218 | 205 | |
219 | version 0.07 | |
206 | version 0.09 | |
220 | 207 | |
221 | 208 | =head1 DESCRIPTION |
222 | 209 | |
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: | |
227 | 214 | |
228 | 215 | =over 4 |
229 | 216 | |
235 | 222 | |
236 | 223 | =back |
237 | 224 | |
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 | ||
238 | 251 | =head1 METHODS |
239 | 252 | |
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 | ||
264 | 253 | =head2 view_pathname($view) |
265 | 254 | |
266 | 255 | Returns the full path to the requested view. |
269 | 258 | |
270 | 259 | Returns the full path to the requested layout. |
271 | 260 | |
272 | =head2 render_layout($layout, $tokens, \$content) | |
261 | =head2 render_layout($layout, \%tokens, \$content) | |
273 | 262 | |
274 | 263 | Render the layout with the applied tokens |
275 | 264 | |
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 | |
281 | 272 | |
282 | 273 | =head1 AUTHOR |
283 | 274 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Route; |
3 | 3 | { |
4 | $Dancer2::Core::Route::VERSION = '0.07'; | |
4 | $Dancer2::Core::Route::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
71 | 71 | ); |
72 | 72 | |
73 | 73 | has _match_data => ( |
74 | is => 'rw', | |
75 | isa => HashRef, | |
76 | trigger => sub { | |
77 | my ( $self, $value ) = @_; | |
78 | }, | |
74 | is => 'rw', | |
75 | isa => HashRef, | |
79 | 76 | ); |
80 | 77 | |
81 | 78 | has _params => ( |
93 | 90 | } |
94 | 91 | |
95 | 92 | my %params; |
96 | my @values = $request->path =~ $self->regexp; | |
93 | my @values = $request->dispatch_path =~ $self->regexp; | |
97 | 94 | |
98 | 95 | # the regex comments are how we know if we captured |
99 | 96 | # a splat or a megasplat |
164 | 161 | # init prefix |
165 | 162 | if ($prefix) { |
166 | 163 | $args{regexp} = |
167 | ref($regexp) eq 'Regexp' ? qr{\Q${prefix}\E${regexp}} | |
164 | ref($regexp) eq 'Regexp' ? qr{^\Q${prefix}\E${regexp}$} | |
168 | 165 | : $regexp eq '/' ? qr{^\Q${prefix}\E/?$} |
169 | 166 | : $prefix . $regexp; |
170 | 167 | } |
238 | 235 | |
239 | 236 | =head1 VERSION |
240 | 237 | |
241 | version 0.07 | |
238 | version 0.09 | |
242 | 239 | |
243 | 240 | =head1 ATTRIBUTES |
244 | 241 |
0 | 0 | # ABSTRACT: Top-layer class to start a dancer app |
1 | 1 | package Dancer2::Core::Runner; |
2 | 2 | { |
3 | $Dancer2::Core::Runner::VERSION = '0.07'; | |
3 | $Dancer2::Core::Runner::VERSION = '0.09'; | |
4 | 4 | } |
5 | 5 | |
6 | 6 | use Moo; |
7 | use Carp 'croak'; | |
8 | use Class::Load 'try_load_class'; | |
7 | 9 | use Dancer2::Core::Types; |
8 | 10 | use Dancer2::Core::MIME; |
9 | use Carp 'croak'; | |
10 | ||
11 | ||
12 | use File::Spec; | |
13 | use File::Basename; | |
11 | 14 | use Dancer2::FileUtils; |
12 | use Dancer2::ModuleLoader; | |
13 | use File::Basename; | |
14 | use File::Spec; | |
15 | 15 | |
16 | 16 | with 'Dancer2::Core::Role::Config'; |
17 | ||
18 | ||
19 | has postponed_hooks => ( | |
20 | is => 'rw', | |
21 | isa => HashRef, | |
22 | default => sub { {} }, | |
23 | ); | |
24 | ||
25 | 17 | |
26 | 18 | # the path to the caller script that is starting the app |
27 | 19 | # mandatory, because we use that to determine where the appdir is. |
29 | 21 | is => 'ro', |
30 | 22 | isa => Str, |
31 | 23 | required => 1, |
32 | trigger => sub { | |
33 | my ( $self, $script ) = @_; | |
34 | $self->_build_location($script); | |
35 | }, | |
36 | 24 | ); |
37 | 25 | |
38 | ||
39 | 26 | has server => ( |
40 | is => 'rw', | |
27 | is => 'ro', | |
41 | 28 | isa => ConsumerOf ['Dancer2::Core::Role::Server'], |
42 | 29 | lazy => 1, |
43 | 30 | 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(); }, | |
44 | 37 | ); |
45 | 38 | |
46 | 39 | # when the runner is created, it has to init the server instance |
50 | 43 | my $server_name = $self->config->{apphandler}; |
51 | 44 | my $server_class = "Dancer2::Core::Server::${server_name}"; |
52 | 45 | |
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"; | |
55 | 48 | |
56 | 49 | return $server_class->new( |
57 | 50 | host => $self->config->{host}, |
58 | 51 | port => $self->config->{port}, |
59 | 52 | is_daemon => $self->config->{is_daemon}, |
60 | runner => $self, | |
61 | 53 | ); |
62 | 54 | } |
63 | ||
64 | ||
65 | has mime_type => ( | |
66 | is => 'rw', | |
67 | isa => InstanceOf ["Dancer2::Core::MIME"], | |
68 | default => sub { Dancer2::Core::MIME->new(); }, | |
69 | ); | |
70 | ||
71 | 55 | |
72 | 56 | # our Config role needs a default_config hash |
73 | 57 | sub default_config { |
80 | 64 | content_type => ( $ENV{DANCER_CONTENT_TYPE} || 'text/html' ), |
81 | 65 | charset => ( $ENV{DANCER_CHARSET} || '' ), |
82 | 66 | warnings => ( $ENV{DANCER_WARNINGS} || 0 ), |
83 | startup_info => ( $ENV{DANCER_STARTUP_INFO} || 1 ), | |
67 | startup_info => ( $ENV{DANCER_STARTUP_INFO} || 0 ), | |
84 | 68 | traces => ( $ENV{DANCER_TRACES} || 0 ), |
85 | 69 | logger => ( $ENV{DANCER_LOGGER} || 'console' ), |
86 | 70 | host => ( $ENV{DANCER_SERVER} || '0.0.0.0' ), |
93 | 77 | }; |
94 | 78 | } |
95 | 79 | |
96 | ||
97 | 80 | sub _build_location { |
98 | my ( $self, $script ) = @_; | |
81 | my $self = shift; | |
82 | my $script = $self->caller; | |
99 | 83 | |
100 | 84 | # default to the dir that contains the script... |
101 | 85 | my $location = Dancer2::FileUtils::dirname($script); |
114 | 98 | #try to find .dancer_app file to determine the root of dancer app |
115 | 99 | my $dancerdir = Dancer2::FileUtils::path( $subdir, '.dancer' ); |
116 | 100 | |
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 | { | |
119 | 106 | $subdir_found = 1; |
120 | 107 | last; |
121 | 108 | } |
122 | $subdir = Dancer2::FileUtils::path( $subdir, '..' ); | |
109 | ||
110 | $subdir = Dancer2::FileUtils::path( $subdir, '..' ) || '.'; | |
123 | 111 | last if File::Spec->rel2abs($subdir) eq File::Spec->rootdir; |
124 | 112 | |
125 | 113 | } |
126 | 114 | |
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 | } | |
130 | 139 | |
131 | 140 | sub start { |
132 | 141 | my ($self) = @_; |
133 | 142 | my $server = $self->server; |
134 | 143 | |
135 | $_->finish for @{ $server->apps }; | |
144 | foreach my $app ( @{ $server->apps } ) { | |
145 | $app->finish; | |
146 | } | |
136 | 147 | |
137 | 148 | # update the server config if needed |
138 | 149 | my $port = $self->setting('server_port'); |
149 | 160 | sub name {"runner"} |
150 | 161 | |
151 | 162 | 1; |
152 | ||
153 | 163 | |
154 | 164 | #still exists? |
155 | 165 | #=method BUILD |
171 | 181 | |
172 | 182 | =head1 VERSION |
173 | 183 | |
174 | version 0.07 | |
184 | version 0.09 | |
175 | 185 | |
176 | 186 | =head1 DESCRIPTION |
177 | 187 | |
194 | 204 | =back |
195 | 205 | |
196 | 206 | =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. | |
202 | 207 | |
203 | 208 | =head2 caller |
204 | 209 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Server::PSGI; |
3 | 3 | { |
4 | $Dancer2::Core::Server::PSGI::VERSION = '0.07'; | |
4 | $Dancer2::Core::Server::PSGI::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Carp; |
29 | 29 | |
30 | 30 | =head1 VERSION |
31 | 31 | |
32 | version 0.07 | |
32 | version 0.09 | |
33 | 33 | |
34 | 34 | =head1 DESCRIPTION |
35 | 35 |
1 | 1 | |
2 | 2 | package Dancer2::Core::Server::Standalone; |
3 | 3 | { |
4 | $Dancer2::Core::Server::Standalone::VERSION = '0.07'; | |
4 | $Dancer2::Core::Server::Standalone::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use Moo; |
41 | 41 | my $pid = $$; #Todo:how to get background pid? |
42 | 42 | |
43 | 43 | # 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; | |
45 | 45 | |
46 | 46 | # bare minimum |
47 | 47 | print STDERR ">> Dancer2 v$Dancer2::VERSION server $pid listening " |
72 | 72 | |
73 | 73 | =head1 VERSION |
74 | 74 | |
75 | version 0.07 | |
75 | version 0.09 | |
76 | 76 | |
77 | 77 | =head1 DESCRIPTION |
78 | 78 |
0 | 0 | package Dancer2::Core::Session; |
1 | 1 | { |
2 | $Dancer2::Core::Session::VERSION = '0.07'; | |
2 | $Dancer2::Core::Session::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | #ABSTRACT: class to represent any session object |
13 | 13 | |
14 | 14 | |
15 | 15 | has id => ( |
16 | is => 'rw', | |
16 | is => 'ro', | |
17 | 17 | isa => Str, |
18 | 18 | required => 1, |
19 | 19 | ); |
75 | 75 | |
76 | 76 | =head1 VERSION |
77 | 77 | |
78 | version 0.07 | |
78 | version 0.09 | |
79 | 79 | |
80 | 80 | =head1 DESCRIPTION |
81 | 81 |
0 | 0 | package Dancer2::Core::Time; |
1 | 1 | { |
2 | $Dancer2::Core::Time::VERSION = '0.07'; | |
2 | $Dancer2::Core::Time::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | #ABSTRACT: class to handle common helpers for time manipulations |
145 | 145 | |
146 | 146 | =head1 VERSION |
147 | 147 | |
148 | version 0.07 | |
148 | version 0.09 | |
149 | 149 | |
150 | 150 | =head1 SYNOPSIS |
151 | 151 |
0 | 0 | package Dancer2::Core::Types; |
1 | 1 | { |
2 | $Dancer2::Core::Types::VERSION = '0.07'; | |
2 | $Dancer2::Core::Types::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Moo types for Dancer2 core. |
147 | 147 | |
148 | 148 | =head1 VERSION |
149 | 149 | |
150 | version 0.07 | |
150 | version 0.09 | |
151 | 151 | |
152 | 152 | =head1 DESCRIPTION |
153 | 153 |
0 | # ABSTRACT: Core libraries for Dancer2 2.0 | |
1 | ||
0 | 2 | package Dancer2::Core; |
1 | 3 | { |
2 | $Dancer2::Core::VERSION = '0.07'; | |
4 | $Dancer2::Core::VERSION = '0.09'; | |
3 | 5 | } |
4 | 6 | |
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 | ||
6 | 36 | |
7 | 37 | 1; |
8 | 38 | |
16 | 46 | |
17 | 47 | =head1 VERSION |
18 | 48 | |
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. | |
20 | 61 | |
21 | 62 | =head1 AUTHOR |
22 | 63 |
1 | 1 | |
2 | 2 | package Dancer2::FileUtils; |
3 | 3 | { |
4 | $Dancer2::FileUtils::VERSION = '0.07'; | |
4 | $Dancer2::FileUtils::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
85 | 85 | # by Mitch Frazier |
86 | 86 | my $path = shift or return; |
87 | 87 | 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 | |
90 | 90 | }x; |
91 | 91 | |
92 | 92 | $path =~ s{/\./}{/}g; |
110 | 110 | |
111 | 111 | =head1 VERSION |
112 | 112 | |
113 | version 0.07 | |
113 | version 0.09 | |
114 | 114 | |
115 | 115 | =head1 SYNOPSIS |
116 | 116 |
1 | 1 | |
2 | 2 | package Dancer2::Handler::AutoPage; |
3 | 3 | { |
4 | $Dancer2::Handler::AutoPage::VERSION = '0.07'; | |
4 | $Dancer2::Handler::AutoPage::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Carp 'croak'; |
26 | 26 | sub { |
27 | 27 | my $ctx = shift; |
28 | 28 | |
29 | my $template = $ctx->app->config->{template}; | |
29 | my $page = $ctx->request->path_info; | |
30 | ||
31 | my $template = $ctx->app->engine('template'); | |
30 | 32 | if ( !defined $template ) { |
31 | 33 | $ctx->response->has_passed(1); |
32 | 34 | return; |
33 | 35 | } |
34 | 36 | |
35 | my $page = $ctx->request->params->{'page'}; | |
36 | my $view_path = $template->view($page); | |
37 | my $view_path = $template->view_pathname($page); | |
38 | ||
37 | 39 | if ( !-f $view_path ) { |
38 | 40 | $ctx->response->has_passed(1); |
39 | 41 | return; |
45 | 47 | }; |
46 | 48 | } |
47 | 49 | |
48 | sub regexp {'/:page'} | |
50 | sub regexp {'/**'} | |
49 | 51 | |
50 | 52 | sub methods {qw(head get)} |
51 | 53 | |
61 | 63 | |
62 | 64 | =head1 VERSION |
63 | 65 | |
64 | version 0.07 | |
66 | version 0.09 | |
65 | 67 | |
66 | 68 | =head1 DESCRIPTION |
67 | 69 | |
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. | |
72 | 74 | |
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. | |
75 | 81 | |
76 | 82 | If there's no view with the name request, the route passes, allowing |
77 | 83 | other matching routes to be dispatched. |
1 | 1 | |
2 | 2 | package Dancer2::Handler::File; |
3 | 3 | { |
4 | $Dancer2::Handler::File::VERSION = '0.07'; | |
4 | $Dancer2::Handler::File::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Carp 'croak'; |
7 | 7 | use Moo; |
42 | 42 | |
43 | 43 | sub BUILD { |
44 | 44 | my ($self) = @_; |
45 | ||
46 | 45 | if ( !defined $self->public_dir ) { |
47 | 46 | my $public = |
48 | 47 | $self->app->config->{public} |
92 | 91 | } |
93 | 92 | |
94 | 93 | my $file_path = path( $self->public_dir, @tokens ); |
95 | $self->execute_hook( 'handler.file.before_render', $file_path ); | |
96 | 94 | |
97 | 95 | if ( !-f $file_path ) { |
98 | 96 | $ctx->response->has_passed(1); |
102 | 100 | if ( !-r $file_path ) { |
103 | 101 | return $self->response_403($ctx); |
104 | 102 | } |
103 | ||
104 | # Now we are sure we can render the file... | |
105 | $self->execute_hook( 'handler.file.before_render', $file_path ); | |
105 | 106 | |
106 | 107 | # Read file content as bytes |
107 | 108 | my $fh = open_file( "<", $file_path ); |
147 | 148 | |
148 | 149 | =head1 VERSION |
149 | 150 | |
150 | version 0.07 | |
151 | version 0.09 | |
151 | 152 | |
152 | 153 | =head1 AUTHOR |
153 | 154 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Capture::Trap; |
3 | 3 | { |
4 | $Dancer2::Logger::Capture::Trap::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Capture::Trap::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Dancer2::Core::Types; |
37 | 37 | |
38 | 38 | =head1 VERSION |
39 | 39 | |
40 | version 0.07 | |
40 | version 0.09 | |
41 | 41 | |
42 | 42 | =head1 SYNOPSIS |
43 | 43 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Capture; |
3 | 3 | { |
4 | $Dancer2::Logger::Capture::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Capture::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Dancer2::Logger::Capture::Trap; |
36 | 36 | |
37 | 37 | =head1 VERSION |
38 | 38 | |
39 | version 0.07 | |
39 | version 0.09 | |
40 | 40 | |
41 | 41 | =head1 SYNOPSIS |
42 | 42 | |
43 | The basics: | |
44 | ||
43 | 45 | set logger => "capture"; |
44 | 46 | |
45 | my $trap = Dancer2::Logger::Capture->trap; | |
47 | my $trap = dancer_app->engine('logger')->trapper; | |
46 | 48 | my $logs = $trap->read; |
47 | 49 | |
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'; | |
51 | 54 | |
52 | 55 | set logger => 'capture'; |
53 | 56 | |
54 | 57 | warning "Danger! Warning!"; |
55 | 58 | debug "I like pie."; |
56 | 59 | |
57 | my $trap = Dancer2::Logger::Capture->trap; | |
60 | my $trap = dancer_app->engine('logger')->trapper; | |
61 | ||
58 | 62 | is_deeply $trap->read, [ |
59 | 63 | { level => "warning", message => "Danger! Warning!" }, |
60 | 64 | { level => "debug", message => "I like pie.", } |
78 | 82 | |
79 | 83 | =head1 SEE ALSO |
80 | 84 | |
81 | L<Dancer2::Logger>, L<Dancer2::Logger::Capture::Trap> | |
85 | L<Dancer2::Core::Role::Logger>, L<Dancer2::Logger::Capture::Trap> | |
82 | 86 | |
83 | 87 | =head1 AUTHOR |
84 | 88 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Console; |
3 | 3 | { |
4 | $Dancer2::Logger::Console::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Console::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | with 'Dancer2::Core::Role::Logger'; |
23 | 23 | |
24 | 24 | =head1 VERSION |
25 | 25 | |
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> | |
27 | 51 | |
28 | 52 | =head1 AUTHOR |
29 | 53 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Diag; |
3 | 3 | { |
4 | $Dancer2::Logger::Diag::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Diag::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Test::More; |
26 | 26 | |
27 | 27 | =head1 VERSION |
28 | 28 | |
29 | version 0.07 | |
30 | ||
31 | =head1 SYNOPSIS | |
29 | version 0.09 | |
32 | 30 | |
33 | 31 | =head1 DESCRIPTION |
34 | 32 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::File; |
3 | 3 | { |
4 | $Dancer2::Logger::File::VERSION = '0.07'; | |
4 | $Dancer2::Logger::File::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Carp 'carp'; |
7 | 7 | use Moo; |
84 | 84 | |
85 | 85 | =head1 VERSION |
86 | 86 | |
87 | version 0.07 | |
87 | version 0.09 | |
88 | 88 | |
89 | 89 | =head1 DESCRIPTION |
90 | 90 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Note; |
3 | 3 | { |
4 | $Dancer2::Logger::Note::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Note::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Test::More; |
26 | 26 | |
27 | 27 | =head1 VERSION |
28 | 28 | |
29 | version 0.07 | |
29 | version 0.09 | |
30 | 30 | |
31 | 31 | =head1 DESCRIPTION |
32 | 32 |
1 | 1 | |
2 | 2 | package Dancer2::Logger::Null; |
3 | 3 | { |
4 | $Dancer2::Logger::Null::VERSION = '0.07'; | |
4 | $Dancer2::Logger::Null::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | with 'Dancer2::Core::Role::Logger'; |
21 | 21 | |
22 | 22 | =head1 VERSION |
23 | 23 | |
24 | version 0.07 | |
24 | version 0.09 | |
25 | 25 | |
26 | 26 | =head1 DESCRIPTION |
27 | 27 |
10 | 10 | |
11 | 11 | =head1 VERSION |
12 | 12 | |
13 | version 0.07 | |
13 | version 0.09 | |
14 | 14 | |
15 | 15 | =head1 DESCRIPTION |
16 | 16 | |
19 | 19 | It's a complete rewrite of L<Dancer>, based on L<Moo> and using a more robust |
20 | 20 | and extensible fully-OO design. |
21 | 21 | |
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 | |
23 | 23 | 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 | |
25 | 25 | common tasks easy mean you can do what you want to do, your way, easily. |
26 | 26 | |
27 | 27 | =encoding utf8 |
143 | 143 | =head2 Route Handlers |
144 | 144 | |
145 | 145 | 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. | |
147 | 147 | This hashref is a merge of the route pattern matches and the request params. |
148 | 148 | |
149 | 149 | 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. | |
151 | 151 | |
152 | 152 | =head3 Named Matching |
153 | 153 | |
162 | 162 | Tokens can be optional, for example: |
163 | 163 | |
164 | 164 | get '/hello/:name?' => sub { |
165 | "Hello there " . param('name') || "whoever you are!"; | |
165 | defined param('name') ? "Hello there ".param('name') : "whoever you are!"; | |
166 | 166 | }; |
167 | 167 | |
168 | 168 | =head3 Wildcards Matching |
169 | 169 | |
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. | |
172 | 172 | |
173 | 173 | get '/download/*.*' => sub { |
174 | 174 | my ($file, $ext) = splat; |
240 | 240 | "I say a number: ".params->{number}; |
241 | 241 | }; |
242 | 242 | |
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 | ||
281 | 243 | =head2 Default Error Pages |
282 | 244 | |
283 | 245 | When an error is rendered (the action responded with a status code different |
320 | 282 | |
321 | 283 | hook before => sub { |
322 | 284 | var note => 'Hi there'; |
323 | request->path_info('/foo/oversee') | |
324 | 285 | }; |
325 | 286 | |
326 | 287 | get '/foo/*' => sub { |
332 | 293 | give non-logged-in users a login page: |
333 | 294 | |
334 | 295 | hook before => sub { |
335 | if (!session('user') && request->path_info !~ m{^/login}) { | |
296 | if (!session('user') && request->dispatch_path !~ m{^/login}) { | |
336 | 297 | # 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 }; | |
339 | 299 | } |
340 | 300 | }; |
341 | 301 | |
475 | 435 | |
476 | 436 | =head2 Serializers |
477 | 437 | |
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 | |
479 | 439 | argument the content to serialize. |
480 | 440 | |
481 | 441 | hook before_serializer => sub { |
482 | my @data = @_; | |
483 | $response->content->{start_time} = time(); | |
442 | my $content = shift; | |
443 | ... | |
484 | 444 | }; |
485 | 445 | |
486 | 446 | C<after_serializer> is called after the payload was serialized, and receives |
487 | 447 | the serialized content as an argument. |
488 | 448 | |
489 | hook before_deserializer => sub { | |
449 | hook after_serializer => sub { | |
490 | 450 | my $content = shift; |
451 | ... | |
491 | 452 | }; |
492 | 453 | |
493 | 454 | =head1 CONFIGURATION AND ENVIRONMENTS |
554 | 515 | |
555 | 516 | More usefully, settings can be defined in a configuration file. |
556 | 517 | 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 | |
558 | 519 | logging in development). See the cookbook for examples. |
559 | 520 | |
560 | 521 | =head2 Serializers |
797 | 758 | |
798 | 759 | =head2 cookies |
799 | 760 | |
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: | |
801 | 763 | |
802 | 764 | get '/some_action' => sub { |
803 | 765 | my $cookie = cookies->{name}; |
930 | 892 | reached. If it was a B<GET>, it will remain a B<GET> (but if you do need to |
931 | 893 | change the method, you can do so; read on below for details.) |
932 | 894 | |
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>. | |
936 | 898 | |
937 | 899 | get '/foo/:article_id' => sub { |
938 | 900 | if ($condition) { |
939 | 901 | forward "/articles/" . params->{article_id}; |
940 | # The following code is never executed | |
902 | # The following code WILL BE executed | |
941 | 903 | do_stuff(); |
942 | 904 | } |
943 | 905 | |
944 | 906 | more_stuff(); |
945 | 907 | }; |
946 | ||
947 | So it's not necessary anymore to use C<return> with forward. | |
948 | 908 | |
949 | 909 | Note that forward doesn't parse GET arguments. So, you can't use |
950 | 910 | something like: |
966 | 926 | |
967 | 927 | Deserializes a Data::Dumper structure. |
968 | 928 | |
969 | =head2 from_json ($structure, %options) | |
929 | =head2 from_json ($structure, \%options) | |
970 | 930 | |
971 | 931 | Deserializes a JSON structure. Can receive optional arguments. Those arguments |
972 | 932 | are valid L<JSON> arguments to change the behaviour of the default |
975 | 935 | =head2 from_yaml ($structure) |
976 | 936 | |
977 | 937 | 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. | |
984 | 938 | |
985 | 939 | =head2 get |
986 | 940 | |
1049 | 1003 | Adds a hook at some position. For example : |
1050 | 1004 | |
1051 | 1005 | hook before_serializer => sub { |
1052 | my $response = shift; | |
1053 | $response->content->{generated_at} = localtime(); | |
1006 | my $content = shift; | |
1007 | ... | |
1054 | 1008 | }; |
1055 | 1009 | |
1056 | 1010 | There can be multiple hooks assigned to a given position, and each will be |
1057 | 1011 | executed in order. |
1058 | 1012 | |
1059 | All the hooks are documented in L<Dancer2::Manual::Hooks>. | |
1060 | ||
1061 | 1013 | =head2 info |
1062 | 1014 | |
1063 | 1015 | Logs a message of info level: |
1073 | 1025 | sugar around Perl's C<require>: |
1074 | 1026 | |
1075 | 1027 | 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'; | |
1094 | 1028 | |
1095 | 1029 | =head2 mime |
1096 | 1030 | |
1493 | 1427 | # destroy session |
1494 | 1428 | get '/logout' => sub { |
1495 | 1429 | ... |
1496 | session->destroy; | |
1430 | context->destroy_session; | |
1497 | 1431 | ... |
1498 | 1432 | }; |
1499 | 1433 | |
1636 | 1570 | |
1637 | 1571 | Serializes a structure with Data::Dumper. |
1638 | 1572 | |
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) | |
1640 | 1576 | |
1641 | 1577 | Serializes a structure to JSON. Can receive optional arguments. Thoses arguments |
1642 | 1578 | are valid L<JSON> arguments to change the behaviour of the default |
1643 | 1579 | C<JSON::to_json> function. |
1644 | 1580 | |
1581 | Calling this function will B<not> trigger the serialization's hooks. | |
1582 | ||
1645 | 1583 | =head2 to_yaml ($structure) |
1646 | 1584 | |
1647 | 1585 | Serializes a structure to YAML. |
1648 | 1586 | |
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. | |
1654 | 1588 | |
1655 | 1589 | =head2 true |
1656 | 1590 |
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 |
1 | 1 | |
2 | 2 | package Dancer2::Plugin::Ajax; |
3 | 3 | { |
4 | $Dancer2::Plugin::Ajax::VERSION = '0.07'; | |
4 | $Dancer2::Plugin::Ajax::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
62 | 62 | |
63 | 63 | =head1 VERSION |
64 | 64 | |
65 | version 0.07 | |
65 | version 0.09 | |
66 | 66 | |
67 | 67 | =head1 SYNOPSIS |
68 | 68 |
0 | 0 | package Dancer2::Plugin; |
1 | 1 | { |
2 | $Dancer2::Plugin::VERSION = '0.07'; | |
2 | $Dancer2::Plugin::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Extending Dancer2's DSL with plugins |
29 | 29 | . " (it should match ^[a-zA-Z_]+[a-zA-Z0-9_]*$ )"; |
30 | 30 | |
31 | 31 | if (grep { $_ eq $keyword } |
32 | map { s/^(?:\$|%|&|@|\*)//; $_ } | |
33 | ( map { $_->[0] } @{ Dancer2::Core::DSL->dsl_keywords } ) | |
32 | keys %{ Dancer2::Core::DSL->dsl_keywords } | |
34 | 33 | ) |
35 | 34 | { |
36 | 35 | croak "You can't use '$keyword', this is a reserved keyword"; |
209 | 208 | # their first argument). |
210 | 209 | # These modified versions of the DSL are then exported in the namespace of the |
211 | 210 | # plugin. |
212 | for my $symbol ( $dsl->dsl_keywords_as_list ) { | |
211 | for my $symbol ( keys %{ $dsl->keywords } ) { | |
213 | 212 | |
214 | 213 | # get the original symbol from the real DSL |
215 | 214 | no strict 'refs'; |
216 | no warnings 'redefine'; | |
215 | no warnings qw( redefine once ); | |
217 | 216 | my $code = *{"Dancer2::Core::DSL::$symbol"}{CODE}; |
218 | 217 | |
219 | 218 | # compile it with $caller->dsl |
260 | 259 | |
261 | 260 | =head1 VERSION |
262 | 261 | |
263 | version 0.07 | |
262 | version 0.09 | |
264 | 263 | |
265 | 264 | =head1 DESCRIPTION |
266 | 265 | |
289 | 288 | |
290 | 289 | The coderef receives as its first argument the Dancer2::Core::DSL object. |
291 | 290 | |
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. | |
294 | 293 | |
295 | 294 | sub { |
296 | 295 | my $dsl = shift; |
1 | 1 | |
2 | 2 | package Dancer2::Serializer::Dumper; |
3 | 3 | { |
4 | $Dancer2::Serializer::Dumper::VERSION = '0.07'; | |
4 | $Dancer2::Serializer::Dumper::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use Moo; |
10 | 10 | |
11 | 11 | with 'Dancer2::Core::Role::Serializer'; |
12 | 12 | |
13 | has '+content_type' => ( default => 'text/x-data-dumper' ); | |
13 | 14 | |
14 | 15 | # helpers |
15 | 16 | sub from_dumper { |
25 | 26 | # class definition |
26 | 27 | sub loaded {1} |
27 | 28 | |
28 | ||
29 | 29 | sub serialize { |
30 | 30 | my ( $self, $entity ) = @_; |
31 | 31 | |
35 | 35 | } |
36 | 36 | } |
37 | 37 | |
38 | ||
39 | 38 | sub deserialize { |
40 | 39 | my ( $self, $content ) = @_; |
41 | 40 | |
43 | 42 | croak "unable to deserialize : $@" if $@; |
44 | 43 | return $res; |
45 | 44 | } |
46 | ||
47 | ||
48 | sub content_type {'text/x-data-dumper'} | |
49 | 45 | |
50 | 46 | 1; |
51 | 47 | |
59 | 55 | |
60 | 56 | =head1 VERSION |
61 | 57 | |
62 | version 0.07 | |
63 | ||
64 | =head1 SYNOPSIS | |
58 | version 0.09 | |
65 | 59 | |
66 | 60 | =head1 DESCRIPTION |
67 | 61 | |
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' | |
69 | 70 | |
70 | 71 | =head1 METHODS |
71 | 72 | |
72 | =head2 serialize | |
73 | =head2 serialize($content) | |
73 | 74 | |
74 | Serialize a Perl data structure into a Dumper string. | |
75 | Serializes a Perl data structure into a Dumper string. | |
75 | 76 | |
76 | =head2 deserialize | |
77 | =head2 deserialize($content) | |
77 | 78 | |
78 | Deserialize a Dumper string into a Perl data structure | |
79 | Deserialize a Dumper string into a Perl data structure. | |
79 | 80 | |
80 | =head2 content_type | |
81 | =head1 FUNCTIONS | |
81 | 82 | |
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 | |
83 | 96 | |
84 | 97 | =head1 AUTHOR |
85 | 98 |
1 | 1 | |
2 | 2 | package Dancer2::Serializer::JSON; |
3 | 3 | { |
4 | $Dancer2::Serializer::JSON::VERSION = '0.07'; | |
4 | $Dancer2::Serializer::JSON::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use JSON (); |
8 | 8 | |
9 | 9 | with 'Dancer2::Core::Role::Serializer'; |
10 | 10 | |
11 | has '+content_type' => ( default => 'application/json' ); | |
11 | 12 | |
12 | 13 | # helpers |
13 | 14 | sub from_json { |
22 | 23 | |
23 | 24 | # class definition |
24 | 25 | sub loaded {1} |
25 | ||
26 | 26 | |
27 | 27 | sub serialize { |
28 | 28 | my ( $self, $entity, $options ) = @_; |
41 | 41 | JSON::to_json( $entity, $options ); |
42 | 42 | } |
43 | 43 | |
44 | ||
45 | 44 | sub deserialize { |
46 | 45 | my ( $self, $entity, $options ) = @_; |
47 | 46 | |
48 | 47 | $options->{utf8} = 1 if !defined $options->{utf8}; |
49 | 48 | JSON::from_json( $entity, $options ); |
50 | 49 | } |
51 | ||
52 | ||
53 | sub content_type {'application/json'} | |
54 | 50 | |
55 | 51 | 1; |
56 | 52 | |
64 | 60 | |
65 | 61 | =head1 VERSION |
66 | 62 | |
67 | version 0.07 | |
68 | ||
69 | =head1 SYNOPSIS | |
63 | version 0.09 | |
70 | 64 | |
71 | 65 | =head1 DESCRIPTION |
72 | 66 | |
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' | |
74 | 75 | |
75 | 76 | =head1 METHODS |
76 | 77 | |
77 | =head2 serialize | |
78 | =head2 serialize($content) | |
78 | 79 | |
79 | Serialize a Perl data structure into a JSON string. | |
80 | Serializes a Perl data structure into a JSON string. | |
80 | 81 | |
81 | =head2 deserialize | |
82 | =head2 deserialize($content) | |
82 | 83 | |
83 | Deserialize a JSON string into a Perl data structure | |
84 | Deserializes a JSON string into a Perl data structure. | |
84 | 85 | |
85 | =head2 content_type | |
86 | =head1 FUNCTIONS | |
86 | 87 | |
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 | |
88 | 99 | |
89 | 100 | =head1 AUTHOR |
90 | 101 |
1 | 1 | |
2 | 2 | package Dancer2::Serializer::YAML; |
3 | 3 | { |
4 | $Dancer2::Serializer::YAML::VERSION = '0.07'; | |
4 | $Dancer2::Serializer::YAML::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Carp 'croak'; |
8 | 8 | with 'Dancer2::Core::Role::Serializer'; |
9 | ||
10 | has '+content_type' => ( default => 'text/x-yaml' ); | |
9 | 11 | |
10 | 12 | # helpers |
11 | 13 | |
36 | 38 | YAML::Any::Load($content); |
37 | 39 | } |
38 | 40 | |
39 | sub content_type {'text/x-yaml'} | |
40 | ||
41 | 41 | 1; |
42 | 42 | |
43 | 43 | __END__ |
50 | 50 | |
51 | 51 | =head1 VERSION |
52 | 52 | |
53 | version 0.07 | |
54 | ||
55 | =head1 SYNOPSIS | |
53 | version 0.09 | |
56 | 54 | |
57 | 55 | =head1 DESCRIPTION |
58 | 56 | |
59 | =head1 METHODS | |
57 | This is a serializer engine that allows you to turn Perl data structures into | |
58 | YAML output and vice-versa. | |
60 | 59 | |
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 | |
68 | 61 | |
69 | 62 | =head2 content_type |
70 | 63 | |
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 | |
72 | 89 | |
73 | 90 | =head1 AUTHOR |
74 | 91 |
1 | 1 | |
2 | 2 | package Dancer2::Session::Simple; |
3 | 3 | { |
4 | $Dancer2::Session::Simple::VERSION = '0.07'; | |
4 | $Dancer2::Session::Simple::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use Moo; |
7 | 7 | use Dancer2::Core::Types; |
50 | 50 | |
51 | 51 | =head1 VERSION |
52 | 52 | |
53 | version 0.07 | |
53 | version 0.09 | |
54 | 54 | |
55 | 55 | =head1 DESCRIPTION |
56 | 56 |
0 | 0 | package Dancer2::Session::YAML; |
1 | 1 | { |
2 | $Dancer2::Session::YAML::VERSION = '0.07'; | |
2 | $Dancer2::Session::YAML::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: YAML-file-based session backend for Dancer2 |
39 | 39 | |
40 | 40 | =head1 VERSION |
41 | 41 | |
42 | version 0.07 | |
42 | version 0.09 | |
43 | 43 | |
44 | 44 | =head1 DESCRIPTION |
45 | 45 |
0 | 0 | package Dancer2::Template::Implementation::ForkedTiny; |
1 | 1 | { |
2 | $Dancer2::Template::Implementation::ForkedTiny::VERSION = '0.07'; | |
2 | $Dancer2::Template::Implementation::ForkedTiny::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Dancer2 own implementation of Template::Tiny |
229 | 229 | |
230 | 230 | =head1 VERSION |
231 | 231 | |
232 | version 0.07 | |
232 | version 0.09 | |
233 | 233 | |
234 | 234 | =head1 SYNOPSIS |
235 | 235 |
1 | 1 | |
2 | 2 | package Dancer2::Template::Simple; |
3 | 3 | { |
4 | $Dancer2::Template::Simple::VERSION = '0.07'; | |
4 | $Dancer2::Template::Simple::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use strict; |
7 | 7 | use warnings; |
8 | use Carp; | |
9 | ||
10 | 8 | use Moo; |
11 | 9 | use Dancer2::FileUtils 'read_file_content'; |
12 | 10 | |
13 | 11 | with 'Dancer2::Core::Role::Template'; |
14 | ||
15 | 12 | |
16 | 13 | has start_tag => ( |
17 | 14 | is => 'rw', |
159 | 156 | |
160 | 157 | =head1 VERSION |
161 | 158 | |
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 | |
163 | 166 | |
164 | 167 | =head1 DESCRIPTION |
165 | 168 | |
171 | 174 | template processing. |
172 | 175 | |
173 | 176 | 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. | |
175 | 187 | |
176 | 188 | =head1 SYNTAX |
177 | 189 | |
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. | |
180 | 192 | |
181 | 193 | =over 4 |
182 | 194 | |
186 | 198 | |
187 | 199 | <% var1 %> |
188 | 200 | |
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. | |
190 | 202 | |
191 | 203 | =back |
192 | 204 | |
193 | 205 | =head1 SEE ALSO |
194 | 206 | |
195 | L<Dancer2>, L<Dancer2::Template> | |
207 | L<Dancer2>, L<Dancer2::Core::Role::Template>, | |
208 | L<Dancer2::Template::TemplateToolkit>. | |
196 | 209 | |
197 | 210 | =head1 AUTHOR |
198 | 211 |
1 | 1 | |
2 | 2 | package Dancer2::Template::TemplateToolkit; |
3 | 3 | { |
4 | $Dancer2::Template::TemplateToolkit::VERSION = '0.07'; | |
4 | $Dancer2::Template::TemplateToolkit::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | |
7 | 7 | use strict; |
8 | 8 | use warnings; |
9 | use Carp; | |
9 | use Carp qw/croak/; | |
10 | 10 | use Moo; |
11 | 11 | use Dancer2::Core::Types; |
12 | 12 | use Template; |
13 | 13 | |
14 | 14 | with 'Dancer2::Core::Role::Template'; |
15 | ||
16 | 15 | |
17 | 16 | has '+engine' => ( isa => InstanceOf ['Template'], ); |
18 | 17 | |
38 | 37 | return Template->new(%tt_config); |
39 | 38 | } |
40 | 39 | |
41 | ||
42 | 40 | sub render { |
43 | 41 | my ( $self, $template, $tokens ) = @_; |
44 | 42 | |
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"; | |
49 | 45 | |
50 | 46 | my $content = ""; |
51 | 47 | my $charset = $self->charset; |
67 | 63 | |
68 | 64 | =head1 VERSION |
69 | 65 | |
70 | version 0.07 | |
66 | version 0.09 | |
71 | 67 | |
72 | 68 | =head1 SYNOPSIS |
73 | 69 | |
81 | 77 | # code code code |
82 | 78 | set template => 'template_toolkit'; |
83 | 79 | |
80 | =head1 DESCRIPTION | |
81 | ||
82 | This template engine allows you to use L<Template::Toolkit> in L<Dancer2>. | |
83 | ||
84 | 84 | =head1 METHODS |
85 | 85 | |
86 | =head2 render TEMPLATE, TOKENS | |
86 | =head2 render($template, \%tokens) | |
87 | 87 | |
88 | 88 | Renders the template. The first arg is a filename for the template file |
89 | 89 | or a reference to a string that contains the template. The second arg |
90 | 90 | is a hashref for the tokens that you wish to pass to |
91 | 91 | L<Template::Toolkit> for rendering. |
92 | ||
93 | =head1 SEE ALSO | |
94 | ||
95 | L<Dancer2>, L<Dancer2::Core::Role::Template>, L<Template::Toolkit>. | |
92 | 96 | |
93 | 97 | =head1 AUTHOR |
94 | 98 |
0 | 0 | package Dancer2::Template::Tiny; |
1 | 1 | { |
2 | $Dancer2::Template::Tiny::VERSION = '0.07'; | |
2 | $Dancer2::Template::Tiny::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Template::Tiny engine for Dancer2 |
6 | 6 | |
7 | 7 | use strict; |
8 | 8 | use warnings; |
9 | use Carp; | |
9 | use Carp qw/croak/; | |
10 | 10 | use Moo; |
11 | 11 | use Dancer2::Core::Types; |
12 | 12 | use Dancer2::Template::Implementation::ForkedTiny; |
13 | 13 | use Dancer2::FileUtils 'read_file_content'; |
14 | 14 | |
15 | 15 | with 'Dancer2::Core::Role::Template'; |
16 | ||
17 | 16 | |
18 | 17 | has '+engine' => |
19 | 18 | ( isa => InstanceOf ['Dancer2::Template::Implementation::ForkedTiny'], ); |
22 | 21 | Dancer2::Template::Implementation::ForkedTiny->new( %{ $_[0]->config } ); |
23 | 22 | } |
24 | 23 | |
25 | ||
26 | 24 | sub render { |
27 | 25 | my ( $self, $template, $tokens ) = @_; |
28 | 26 | |
29 | 27 | ( 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"; | |
31 | 29 | |
32 | 30 | my $template_data = |
33 | 31 | ref $template |
54 | 52 | |
55 | 53 | =head1 VERSION |
56 | 54 | |
57 | version 0.07 | |
55 | version 0.09 | |
58 | 56 | |
59 | 57 | =head1 SYNOPSIS |
60 | 58 | |
93 | 91 | |
94 | 92 | Since L<Dancer2> has internal support for a wrapper-like option with the |
95 | 93 | 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. | |
97 | 95 | |
98 | 96 | =head1 METHODS |
99 | 97 | |
100 | =head2 render | |
98 | =head2 render($template, \%tokens) | |
101 | 99 | |
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>. | |
104 | 109 | |
105 | 110 | =head1 AUTHOR |
106 | 111 |
1 | 1 | |
2 | 2 | package Dancer2::Test; |
3 | 3 | { |
4 | $Dancer2::Test::VERSION = '0.07'; | |
4 | $Dancer2::Test::VERSION = '0.09'; | |
5 | 5 | } |
6 | 6 | use strict; |
7 | 7 | use warnings; |
462 | 462 | } |
463 | 463 | |
464 | 464 | # 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 | ); | |
466 | 472 | |
467 | 473 | $class->export_to_level( 1, $class, @EXPORT ); |
468 | 474 | } |
565 | 571 | |
566 | 572 | =head1 VERSION |
567 | 573 | |
568 | version 0.07 | |
574 | version 0.09 | |
569 | 575 | |
570 | 576 | =head1 DESCRIPTION |
571 | 577 | |
601 | 607 | state of the application and cause Schrodinger's cat to die. |
602 | 608 | |
603 | 609 | 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", | |
606 | 612 | "response content looks good for first POST /widgets"; |
607 | 613 | |
608 | 614 | $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", | |
611 | 617 | "response content looks good for second POST /widgets"; |
612 | 618 | |
613 | 619 | It's possible to test file uploads: |
10 | 10 | |
11 | 11 | =head1 VERSION |
12 | 12 | |
13 | version 0.07 | |
13 | version 0.09 | |
14 | 14 | |
15 | 15 | =head1 What is Dancer2? |
16 | 16 | |
44 | 44 | |
45 | 45 | So "Dancr" was born. |
46 | 46 | |
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.) | |
48 | 50 | |
49 | 51 | =head1 Required perl modules |
50 | 52 | |
57 | 59 | |
58 | 60 | We're not going to spend a lot of time on the database, as it's not really the point of this particular |
59 | 61 | tutorial. |
62 | Open your favorite L<text editor|http://www.vim.org> and create a schema | |
63 | definition called 'schema.sql' as follows: | |
60 | 64 | |
61 | 65 | create table if not exists entries ( |
62 | 66 | id integer primary key autoincrement, |
67 | 71 | Here we have a single table with three columns: id, title, and text. The 'id' field is the primary key and will |
68 | 72 | automatically get an ID assigned by the database engine when a row is inserted. |
69 | 73 | |
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: | |
73 | 79 | |
74 | 80 | sub connect_db { |
75 | 81 | my $dbh = DBI->connect("dbi:SQLite:dbname=".setting('database')) or |
107 | 113 | }; |
108 | 114 | }; |
109 | 115 | |
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 | |
112 | 118 | conditions have been satisfied. Something you might not notice right away is |
113 | 119 | the semicolon at the end of the route handler. Since the subroutine actually |
114 | 120 | is a coderef, it requires a semicolon. |
245 | 251 | configuration directives toward the top of our dancr.pl file. But there are |
246 | 252 | more options than just the session engine we want to set. |
247 | 253 | |
254 | set 'database' => File::Spec->catfile(File::Spec->tmpdir(), 'dancr.db'); | |
248 | 255 | set 'session' => 'Simple'; |
249 | 256 | set 'template' => 'template_toolkit'; |
250 | 257 | set 'logger' => 'console'; |
342 | 349 | logout procedure. |
343 | 350 | |
344 | 351 | get '/logout' => sub { |
345 | session->destroy; | |
352 | context->destroy_session; | |
346 | 353 | set_flash('You are logged out.'); |
347 | 354 | redirect '/'; |
348 | 355 | }; |
349 | 356 | |
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. | |
352 | 360 | |
353 | 361 | =head1 Layout and static files |
354 | 362 | |
431 | 439 | time since you only need to update the values in this one place instead of |
432 | 440 | everywhere you render a template. |
433 | 441 | |
434 | before_template sub { | |
442 | hook before_template => sub { | |
435 | 443 | my $tokens = shift; |
436 | 444 | |
437 | 445 | $tokens->{'css_url'} = request->base . 'css/style.css'; |
494 | 502 | $db->do($schema) or die $db->errstr; |
495 | 503 | } |
496 | 504 | |
497 | before_template sub { | |
505 | hook before_template => sub { | |
498 | 506 | my $tokens = shift; |
499 | 507 | |
500 | 508 | $tokens->{'css_url'} = request->base . 'css/style.css'; |
554 | 562 | }; |
555 | 563 | |
556 | 564 | get '/logout' => sub { |
557 | session->destroy; | |
565 | context->destroy_session; | |
558 | 566 | set_flash('You are logged out.'); |
559 | 567 | redirect '/'; |
560 | 568 | }; |
606 | 614 | |
607 | 615 | The CSS stylesheet is copied verbatim from the Flaskr example application and is subject to their license: |
608 | 616 | |
609 | Copyright (c) 2010 by Armin Ronacher and contributors. | |
617 | Copyright (c) 2010, 2013 by Armin Ronacher and contributors. | |
610 | 618 | |
611 | 619 | Some rights reserved. |
612 | 620 |
0 | 0 | package Dancer2; |
1 | 1 | { |
2 | $Dancer2::VERSION = '0.07'; | |
2 | $Dancer2::VERSION = '0.09'; | |
3 | 3 | } |
4 | 4 | |
5 | 5 | # ABSTRACT: Lightweight yet powerful web application framework |
6 | 6 | |
7 | 7 | use strict; |
8 | 8 | use warnings; |
9 | use Data::Dumper; | |
9 | use Class::Load 'load_class'; | |
10 | use Dancer2::Core; | |
10 | 11 | use Dancer2::Core::Runner; |
11 | 12 | use Dancer2::Core::App; |
12 | 13 | use Dancer2::FileUtils; |
13 | use Dancer2::ModuleLoader; | |
14 | 14 | |
15 | 15 | our $AUTHORITY = 'SUKRIA'; |
16 | 16 | |
17 | 17 | # set version in dist.ini now |
18 | 18 | # but we still need a basic version for |
19 | 19 | # 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; | |
24 | 24 | |
25 | 25 | sub runner {$runner} |
26 | 26 | |
33 | 33 | utf8->import; |
34 | 34 | |
35 | 35 | my @final_args; |
36 | my $syntax_only = 0; | |
37 | my $as_script = 0; | |
36 | my $as_script = 0; | |
38 | 37 | foreach (@args) { |
39 | 38 | if ( $_ eq ':tests' ) { |
40 | 39 | push @final_args, '!pass' => 1; |
41 | } | |
42 | elsif ( $_ eq ':syntax' ) { | |
43 | $syntax_only = 1; | |
44 | 40 | } |
45 | 41 | elsif ( $_ eq ':script' ) { |
46 | 42 | $as_script = 1; |
68 | 64 | $runner = Dancer2::Core::Runner->new( caller => $script, ); |
69 | 65 | } |
70 | 66 | |
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; | |
73 | 68 | |
74 | 69 | # the app object |
70 | # populating with the server's postponed hooks in advance | |
75 | 71 | my $app = Dancer2::Core::App->new( |
76 | 72 | name => $caller, |
77 | 73 | environment => $runner->environment, |
78 | 74 | location => $runner->location, |
79 | 75 | runner_config => $runner->config, |
80 | postponed_hooks => $runner->postponed_hooks, | |
76 | postponed_hooks => $server->postponed_hooks, | |
81 | 77 | ); |
82 | 78 | |
83 | core_debug("binding import method to $caller"); | |
79 | Dancer2::Core::debug("binding import method to $caller"); | |
84 | 80 | _set_import_method_to_caller($caller); |
85 | 81 | |
86 | 82 | # 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"); | |
91 | 87 | |
92 | 88 | # 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} ); | |
95 | 90 | my $dsl = $final_args{dsl}->new( app => $app ); |
96 | 91 | $dsl->export_symbols_to( $caller, \%final_args ); |
97 | 92 | |
98 | # | |
99 | # # if :syntax option exists, don't change settings | |
100 | # return if $syntax_only; | |
101 | 93 | # |
102 | 94 | # $as_script = 1 if $ENV{PLACK_ENV}; |
103 | 95 | # |
123 | 115 | } |
124 | 116 | } |
125 | 117 | |
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 | ||
141 | 118 | 1; |
142 | 119 | |
143 | 120 | __END__ |
150 | 127 | |
151 | 128 | =head1 VERSION |
152 | 129 | |
153 | version 0.07 | |
130 | version 0.09 | |
154 | 131 | |
155 | 132 | =head1 DESCRIPTION |
156 | 133 | |
224 | 201 | you control over the keywords that will be imported into your webapp and other |
225 | 202 | things: |
226 | 203 | |
227 | use Dancer2 ':syntax'; | |
204 | use Dancer2 ':script'; | |
228 | 205 | |
229 | 206 | =head3 Import Options |
230 | 207 | |
235 | 212 | No importing of C<pass> function. This is to prevent conflict with |
236 | 213 | L<Test::More> et al. |
237 | 214 | |
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 | ||
243 | 215 | =item C<:script> |
244 | 216 | |
245 | 217 | Do not process arguments. |
251 | 223 | =head2 my $runner=runner(); |
252 | 224 | |
253 | 225 | 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> | |
259 | 226 | |
260 | 227 | =head1 AUTHOR |
261 | 228 |
45 | 45 | my $DANCER_APP_DIR = get_application_path($path, $name); |
46 | 46 | my $DANCER_SCRIPT = get_script_path($name); |
47 | 47 | my ($LIB_FILE, $LIB_PATH) = get_lib_path($name); |
48 | ||
49 | my $AUTO_RELOAD = eval "require Module::Refresh and require Clone" ? 1 : 0; | |
50 | 48 | |
51 | 49 | require Dancer2; |
52 | 50 | my $DANCER_VERSION = $Dancer2::VERSION; |
344 | 342 | clean => { FILES => '$cleanfiles-*' }, |
345 | 343 | ); |
346 | 344 | ", |
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 | |
352 | 350 | built with a scaffolded application. |
353 | 351 | |
354 | 352 | Thanks a lot to them for their work. |
369 | 367 | <li><a href="https://github.com/PerlDancer/Dancer2/">GitHub Community</a></li> |
370 | 368 | </ul> |
371 | 369 | </li> |
372 | ||
370 | ||
373 | 371 | <li> |
374 | 372 | <h3>Browse the documentation</h3> |
375 | 373 | |
408 | 406 | <div id="getting-started"> |
409 | 407 | <h1>Getting started</h1> |
410 | 408 | <h2>Here’s how to get dancing:</h2> |
411 | ||
409 | ||
412 | 410 | <h3><a href="#" id="about_env_link">About your application\'s environment</a></h3> |
413 | 411 | |
414 | 412 | <div id="about-content" style="display: none;"> |
456 | 454 | </script> |
457 | 455 | |
458 | 456 | |
459 | <ol> | |
457 | <ol> | |
460 | 458 | <li> |
461 | 459 | <h2>Tune your application</h2> |
462 | 460 | |
474 | 472 | <p> |
475 | 473 | The default route that displays this page can be removed, |
476 | 474 | 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 | |
478 | 476 | <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>. | |
480 | 478 | </p> |
481 | 479 | </li> |
482 | 480 | |
520 | 518 | |
521 | 519 | "dispatch.cgi" => |
522 | 520 | "$PERL_INTERPRETER |
523 | use Dancer2 ':syntax'; | |
521 | use Dancer2; | |
524 | 522 | use FindBin '\$RealBin'; |
525 | 523 | use Plack::Runner; |
526 | 524 | |
527 | 525 | # 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 | |
529 | 527 | # is safer. |
530 | 528 | set apphandler => 'PSGI'; |
531 | 529 | set environment => 'production'; |
539 | 537 | |
540 | 538 | "dispatch.fcgi" => |
541 | 539 | qq{$PERL_INTERPRETER |
542 | use Dancer2 ':syntax'; | |
540 | use Dancer2; | |
543 | 541 | use FindBin '\$RealBin'; |
544 | 542 | use Plack::Handler::FCGI; |
545 | 543 | |
546 | 544 | # 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 | |
548 | 546 | # is safer. |
549 | 547 | set apphandler => 'PSGI'; |
550 | 548 | set environment => 'production'; |
568 | 566 | "$LIB_FILE" => |
569 | 567 | |
570 | 568 | "package $appname; |
571 | use Dancer2 ':syntax'; | |
569 | use Dancer2; | |
572 | 570 | |
573 | 571 | our \$VERSION = '0.1'; |
574 | 572 | |
945 | 943 | # should Dancer2 show a stacktrace when an error is caught? |
946 | 944 | show_errors: 1 |
947 | 945 | |
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 | ||
957 | 946 | # print the banner |
958 | 947 | startup_info: 1 |
959 | 948 | ", |
970 | 959 | # don\'t consider warnings critical |
971 | 960 | warnings: 0 |
972 | 961 | |
973 | # hide errors | |
962 | # hide errors | |
974 | 963 | show_errors: 0 |
975 | 964 | |
976 | 965 | # cache route resolution for maximum performance |
1206 | 1195 | |
1207 | 1196 | =head1 VERSION |
1208 | 1197 | |
1209 | version 0.07 | |
1198 | version 0.09 | |
1210 | 1199 | |
1211 | 1200 | =head1 SYNOPSIS |
1212 | 1201 |
0 | 0 | use strict; |
1 | 1 | use warnings; |
2 | 2 | |
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 | |
4 | 4 | |
5 | use Test::More 0.88; | |
5 | use Test::More tests => 58 + ( $ENV{AUTHOR_TESTING} ? 1 : 0 ); | |
6 | 6 | |
7 | 7 | |
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' | |
69 | 66 | ); |
70 | 67 | |
71 | my @scripts = qw( | |
72 | script/dancer2 | |
73 | ); | |
68 | my @scripts = ( 'script/dancer2' ); | |
74 | 69 | |
75 | 70 | # no fake home requested |
76 | 71 | |
72 | use IPC::Open3; | |
73 | use IO::Handle; | |
74 | ||
77 | 75 | my @warnings; |
78 | 76 | 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 | } | |
85 | 91 | } |
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 | ||
86 | 117 | |
87 | 118 | is( scalar(@warnings), 0, 'no warnings found' ) if $ENV{AUTHOR_TESTING}; |
88 | 119 | |
89 | use Test::Script 1.05; | |
90 | foreach my $file (@scripts) { | |
91 | script_compiles( $file, "$file compiles" ); | |
92 | } | |
93 | 120 | |
94 | ||
95 | done_testing; |
12 | 12 | CGI::Deurl::XS |
13 | 13 | Capture::Tiny |
14 | 14 | Carp |
15 | Class::Load | |
16 | Class::Load::XS | |
15 | 17 | Config::Any |
16 | 18 | Crypt::URandom |
17 | 19 | Cwd |
36 | 38 | HTTP::Server::Simple::PSGI |
37 | 39 | Hash::Merge::Simple |
38 | 40 | IO::File |
41 | IO::Handle | |
42 | IPC::Open3 | |
39 | 43 | JSON |
40 | 44 | JSON::XS |
41 | 45 | LWP::UserAgent |
44 | 48 | MIME::Types |
45 | 49 | Math::Random::ISAAC::XS |
46 | 50 | Module::Build |
47 | Module::Runtime | |
48 | 51 | Moo |
49 | 52 | Moo::Role |
50 | 53 | MooX::Types::MooseLike |
54 | 57 | Pod::Simple::Search |
55 | 58 | Pod::Simple::SimpleTree |
56 | 59 | Pod::Usage |
60 | Role::Tiny | |
57 | 61 | Scalar::Util |
58 | 62 | Template |
59 | 63 | Template::Tiny |
68 | 72 | URL::Encode::XS |
69 | 73 | YAML |
70 | 74 | YAML::Any |
71 | blib | |
72 | 75 | constant |
73 | 76 | lib |
74 | 77 | overload |
180 | 180 | }; |
181 | 181 | $app->add_route(%$regexp_route); |
182 | 182 | |
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 | ||
183 | 193 | 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; |
2 | 2 | use Test::More import => ['!pass']; |
3 | 3 | |
4 | 4 | use FindBin qw($Bin); |
5 | use lib "$Bin/t/lib"; | |
5 | use lib "$Bin/lib"; | |
6 | 6 | use Dancer2 dsl => 'MyDancerDSL'; |
7 | 7 | use Dancer2::Test; |
8 | 8 |
4 | 4 | |
5 | 5 | use Test::More tests => 49; |
6 | 6 | |
7 | use Dancer2 ':syntax'; | |
7 | use Dancer2; | |
8 | 8 | use Dancer2::Test; |
9 | 9 | use Dancer2::Core::Request; |
10 | 10 | use File::Temp; |
0 | 0 | use strict; |
1 | 1 | use warnings; |
2 | 2 | |
3 | use Test::More tests => 6; | |
3 | use Test::More tests => 9; | |
4 | 4 | |
5 | 5 | { |
6 | 6 | |
19 | 19 | my $p = request->data; |
20 | 20 | return join " : ", map { $_ => $p->{$_} } sort keys %$p; |
21 | 21 | }; |
22 | ||
23 | post '/from/:town' => sub { | |
24 | my $p = params; | |
25 | return $p; | |
26 | }; | |
22 | 27 | } |
23 | 28 | |
24 | 29 | use utf8; |
25 | 30 | use JSON; |
26 | 31 | use Encode; |
27 | 32 | use Dancer2::Test apps => ['MyApp']; |
33 | use Class::Load 'load_class'; | |
28 | 34 | |
29 | 35 | is dancer_response( |
30 | 36 | Dancer2::Core::Request->new( |
37 | 43 | )->content => 'bar : 2 : foo : 1', "using $_" |
38 | 44 | for qw/ params data /; |
39 | 45 | |
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( | |
43 | 99 | method => 'PUT', |
44 | 100 | path => '/from_params', |
45 | 101 | 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 | ); | |
50 | 105 | |
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 | } |
2 | 2 | use Test::More; |
3 | 3 | use Test::Fatal; |
4 | 4 | |
5 | use Dancer2::Core; | |
5 | 6 | use Dancer2::Core::Factory; |
6 | 7 | |
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'; | |
9 | 10 | |
10 | 11 | like( |
11 | 12 | exception { my $l = Dancer2::Core::Factory->create( unknown => 'stuff' ) }, |
0 | 0 | use strict; |
1 | 1 | use warnings; |
2 | use Test::More tests => 11; | |
2 | use Test::More tests => 14; | |
3 | 3 | use Test::Fatal; |
4 | 4 | use File::Spec; |
5 | 5 | BEGIN { @File::Spec::ISA = ("File::Spec::Unix") } |
31 | 31 | my $content = Dancer2::FileUtils::read_file_content(); |
32 | 32 | is $content, undef; |
33 | 33 | |
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 | } | |
35 | 44 | |
36 | 45 | my $p = Dancer2::FileUtils::dirname('/somewhere'); |
37 | 46 | 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(); |
3 | 3 | use File::Spec; |
4 | 4 | use Carp; |
5 | 5 | |
6 | use Class::Load 'try_load_class'; | |
6 | 7 | use Capture::Tiny 0.12 'capture_stderr'; |
7 | 8 | |
8 | Dancer2::ModuleLoader->require('Template') | |
9 | try_load_class('Template') | |
9 | 10 | or plan skip_all => 'Template::Toolkit not present'; |
10 | 11 | |
11 | 12 | my @hooks = qw( |
27 | 28 | my $tests_flags = {}; |
28 | 29 | { |
29 | 30 | use Dancer2; |
30 | ||
31 | 31 | |
32 | 32 | for my $hook (@hooks) { |
33 | 33 | hook $hook => sub { |
97 | 97 | like $response->content, qr/Internal Server Error/; |
98 | 98 | }; |
99 | 99 | |
100 | # make sure we compile all the apps without starting a webserver | |
101 | main->dancer_app->finish; | |
102 | 100 | } |
103 | 101 | |
104 | 102 | 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 |
11 | 11 | my $orig = shift; |
12 | 12 | my $keywords = $orig->(@_); |
13 | 13 | |
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 | |
20 | 20 | |
21 | 21 | return $keywords; |
22 | 22 | }; |
6 | 6 | # Freeze time at Tue, 15-Jun-2010 00:00:00 GMT |
7 | 7 | *CORE::GLOBAL::time = sub { return 1276560000 } |
8 | 8 | } |
9 | ||
9 | 10 | |
10 | 11 | my $_logs = []; |
11 | 12 | |
25 | 26 | |
26 | 27 | is $logger->log_level, 'debug'; |
27 | 28 | $logger->debug("foo"); |
28 | like $_logs->[0], qr{debug \@2010-06-1\d \d\d:00:00> foo in t/logger.t}; | |
29 | 29 | |
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 { | |
31 | 35 | use Dancer2::Logger::Capture; |
32 | 36 | use Dancer2; |
33 | 37 | |
38 | # NOTE: this will read the config.yml under t/ that defines log level as info | |
34 | 39 | set logger => 'capture'; |
35 | 40 | |
36 | 41 | warning "Danger! Warning!"; |
37 | 42 | info "Tango, Foxtrot"; |
38 | 43 | debug "I like pie."; |
39 | 44 | |
40 | my $app = dancer_app; | |
41 | my $trap = $app->setting('logger')->trapper; | |
45 | my $trap = dancer_app->engine('logger')->trapper; | |
42 | 46 | is_deeply $trap->read, |
43 | 47 | [ { level => "warning", message => "Danger! Warning!" }, |
44 | 48 | { level => "info", message => "Tango, Foxtrot" }, |
45 | { level => "debug", message => "I like pie.", } | |
46 | 49 | ]; |
47 | 50 | |
48 | 51 | # each call to read cleans the trap |
9 | 9 | |
10 | 10 | for my $level (qw{core debug warning error}) { |
11 | 11 | 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"; | |
13 | 16 | } |
14 | 17 | done_testing; |
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(); |
3 | 3 | |
4 | 4 | use Dancer2::Core::Request; |
5 | 5 | |
6 | diag "If you want extract speed, install URL::Encode::XS" | |
6 | diag "If you want extra speed, install URL::Encode::XS" | |
7 | 7 | 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" | |
9 | 9 | if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING; |
10 | 10 | |
11 | 11 | sub run_test { |
89 | 89 | is $req->base, 'http://oddhostname:5000/foo'; |
90 | 90 | } |
91 | 91 | |
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( | |
95 | 95 | env => $env, |
96 | 96 | is_behind_proxy => 1 |
97 | 97 | ); |
100 | 100 | is $req->scheme, 'https'; |
101 | 101 | } |
102 | 102 | |
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 | } | |
132 | 184 | |
133 | 185 | note "testing forward"; |
134 | 186 | $env = { |
2 | 2 | use warnings FATAL => 'all'; |
3 | 3 | use Dancer2::Core::Request; |
4 | 4 | |
5 | diag "If you want extract speed, install URL::Encode::XS" | |
5 | diag "If you want extra speed, install URL::Encode::XS" | |
6 | 6 | 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" | |
8 | 8 | if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING; |
9 | 9 | |
10 | 10 | sub run_test { |
10 | 10 | use File::Spec; |
11 | 11 | use Encode qw(encode_utf8); |
12 | 12 | |
13 | diag "If you want extract speed, install URL::Encode::XS" | |
13 | diag "If you want extra speed, install URL::Encode::XS" | |
14 | 14 | 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" | |
16 | 16 | if !$Dancer2::Core::Request::XS_PARSE_QUERY_STRING; |
17 | 17 | |
18 | 18 | sub test_path { |
0 | 0 | use strict; |
1 | 1 | use warnings; |
2 | 2 | |
3 | use Test::More tests => 3; | |
3 | use Test::More tests => 7; | |
4 | use Dancer2::Serializer::Dumper; | |
4 | 5 | |
5 | 6 | { |
6 | 7 | |
10 | 11 | |
11 | 12 | set serializer => 'JSON'; |
12 | 13 | |
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 } ) }; | |
14 | 16 | } |
15 | 17 | |
16 | 18 | use Dancer2::Test apps => ['MyApp']; |
17 | 19 | |
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; | |
22 | 23 | response_content_is $resp => '{"bar":"baz"}'; |
23 | ||
24 | 24 | response_headers_include $resp, [ 'Content-Type' => 'application/json' ]; |
25 | 25 | |
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'; |
5 | 5 | |
6 | 6 | use Dancer2::Core::Session; |
7 | 7 | use Dancer2::Session::Simple; |
8 | use Class::Load 'try_load_class'; | |
8 | 9 | |
9 | 10 | my $ENGINE = Dancer2::Session::Simple->new; |
10 | 11 | |
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'); | |
13 | 14 | |
14 | 15 | diag $CPRNG_AVAIL |
15 | 16 | ? "Crypto strength tokens" |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | use Test::More; |
5 | use Dancer2::ModuleLoader; | |
5 | use Class::Load 'try_load_class'; | |
6 | 6 | |
7 | 7 | my $mocked_epoch = 1355676244; # "Sun, 16-Dec-2012 16:44:04 GMT" |
8 | 8 | |
9 | 9 | # The order is important! |
10 | Dancer2::ModuleLoader->require('Test::MockTime') | |
10 | try_load_class('Test::MockTime') | |
11 | 11 | or plan skip_all => 'Test::MockTime not present'; |
12 | 12 | |
13 | 13 | Test::MockTime::set_fixed_time($mocked_epoch); |
0 | Hey! This is Auto Page working. |
0 | Page under folder. |