| 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 | ||||||