File: | lib/Railsish/Dispatcher.pm |
Coverage: | 88.5% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Railsish::Dispatcher; | ||||||
2 | # ABSTRACT: The first handler for requests. | ||||||
3 | |||||||
4 | 7 7 7 | 143 23 592 | use Railsish::Router; | ||||
5 | 7 7 7 | 704 30 56 | use YAML::Any; | ||||
6 | 7 7 7 | 574 32 104 | use Hash::Merge qw(merge); | ||||
7 | 7 7 7 | 152 24 100 | use Encode; | ||||
8 | 7 7 7 | 141 22 98 | use Railsish::CoreHelpers; | ||||
9 | 7 7 7 | 557 35 122 | use MIME::Base64; | ||||
10 | 7 7 7 | 569 37 142 | use Crypt::CBC; | ||||
11 | 7 7 7 | 153 22 129 | use JSON::XS; | ||||
12 | |||||||
13 | sub dispatch { | ||||||
14 | 19 | 0 | 115 | my ($class, $request) = @_; | |||
15 | 19 | 160 | my $path = $request->path; | ||||
16 | |||||||
17 | 19 | 86 | $path =~ s/\.([a-z]+)$//; | ||||
18 | 19 | 212 | my $format = $1 || "html"; | ||||
19 | |||||||
20 | 19 | 151 | my $method = lc($request->method); | ||||
21 | 19 | 588 | if ($method eq 'post') { | ||||
22 | 2 | 15 | if (my $m = $request->param("_method")) { | ||||
23 | 0 | 0 | $method = lc($m); | ||||
24 | } | ||||||
25 | } | ||||||
26 | 19 | 518 | my $matched = Railsish::Router->match( | ||||
27 | $path, conditions => { method => $method } | ||||||
28 | ); | ||||||
29 | |||||||
30 | 19 | 420 | my $response = HTTP::Engine::Response->new; | ||||
31 | 19 | 1502 | unless($matched) { | ||||
32 | 4 | 28 | $response->body("internal server error"); | ||||
33 | 4 | 261 | $response->status(500); | ||||
34 | 4 | 210 | return $response; | ||||
35 | } | ||||||
36 | |||||||
37 | 15 | 149 | my $mapping = $matched->mapping; | ||||
38 | |||||||
39 | 15 | 292 | my $controller = $mapping->{controller}; | ||||
40 | 15 | 104 | my $action = $mapping->{action} || "index"; | ||||
41 | |||||||
42 | 15 | 90 | my $controller_class = ucfirst(lc($controller)) . "Controller"; | ||||
43 | 15 | 256 | my $sub = $controller_class->can($action); | ||||
44 | |||||||
45 | 15 | 191 | die "action $action is not defined in $controller_class." unless $sub; | ||||
46 | |||||||
47 | 15 | 70 | my %params = _preprocessed_parameters($request); | ||||
48 | |||||||
49 | 15 | 114 | my $params = merge(\%params, $mapping); | ||||
50 | |||||||
51 | 15 | 5213 | $Railsish::Controller::params = $params; | ||||
52 | 15 | 65 | $Railsish::Controller::request = $request; | ||||
53 | 15 | 50 | $Railsish::Controller::response = $response; | ||||
54 | 15 | 45 | $Railsish::Controller::controller = $controller; | ||||
55 | 15 | 410 | $Railsish::Controller::action = $action; | ||||
56 | 15 | 42 | $Railsish::Controller::format = $format; | ||||
57 | |||||||
58 | 15 | 73 | my $session = $Railsish::Controller::session = _load_session($request); | ||||
59 | |||||||
60 | 15 | 157 | logger->debug(Dump({ | ||||
61 | request_path => $path, | ||||||
62 | method => $method, | ||||||
63 | controller => $controller, | ||||||
64 | action => $action, | ||||||
65 | params => $params, | ||||||
66 | session => $session | ||||||
67 | })); | ||||||
68 | |||||||
69 | 15 | 2668 | $sub->(); | ||||
70 | |||||||
71 | 15 | 1157 | _store_session($response, $session); | ||||
72 | |||||||
73 | 15 | 47 | return $response; | ||||
74 | } | ||||||
75 | |||||||
76 | sub _preprocessed_parameters { | ||||||
77 | 15 | 63 | my ($request) = @_; | ||||
78 | 15 15 | 46 128 | my %params = %{$request->parameters}; | ||||
79 | 15 | 1300 | for (keys %params) { | ||||
80 | 0 | 0 | $params{$_} = Encode::decode_utf8( $params{$_} ); | ||||
81 | |||||||
82 | 0 | 0 | if (/^(\w+)\[(\w+)\]$/) { | ||||
83 | 0 | 0 | $params{$1} ||= {}; | ||||
84 | 0 | 0 | $params{$1}->{$2}= delete $params{$_}; | ||||
85 | } | ||||||
86 | } | ||||||
87 | |||||||
88 | 15 | 91 | return %params; | ||||
89 | } | ||||||
90 | |||||||
91 | sub _load_session { | ||||||
92 | 15 | 57 | my ($request) = @_; | ||||
93 | |||||||
94 | # XXX: the -key here should be given from app config. | ||||||
95 | 15 | 368 | my $cipher = Crypt::CBC->new(-key => "railsish", -cipher => "Rijndael"); | ||||
96 | 15 | 5154 | my $session = {}; | ||||
97 | 15 | 139 | my $session_cookie = $request->cookies->{_railsish_session}; | ||||
98 | 15 | 626 | if ($session_cookie) { | ||||
99 | 1 | 9 | my $ciphertext_base64 = $session_cookie->value; | ||||
100 | 1 | 9 | my $ciphertext_unbase64 = decode_base64($ciphertext_base64); | ||||
101 | 1 | 7 | my $json = $cipher->decrypt($ciphertext_unbase64); | ||||
102 | 1 | 793 | $session = decode_json($json); | ||||
103 | } | ||||||
104 | 15 | 184 | return $session; | ||||
105 | } | ||||||
106 | |||||||
107 | sub _store_session { | ||||||
108 | 15 | 73 | my ($response, $session) = @_; | ||||
109 | 15 | 159 | my $cipher = Crypt::CBC->new(-key => "railsish", -cipher => "Rijndael"); | ||||
110 | 15 | 7151 | my $json = encode_json($session); | ||||
111 | 15 | 131 | my $ciphertext = $cipher->encrypt($json); | ||||
112 | 15 | 36581 | my $ciphertext_base64 = encode_base64($ciphertext, ''); | ||||
113 | 15 | 186 | $response->cookies->{_railsish_session} = { | ||||
114 | value => $ciphertext_base64 | ||||||
115 | }; | ||||||
116 | } | ||||||
117 | |||||||
118 | 1; | ||||||
119 | |||||||
120 - 124 | =head1 DESCRIPTION This class contains the first handler for requests. =cut |