| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package CatalystX::RequestModel::DoesRequestModel; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 6 |  |  | 6 |  | 3304 | use Moo::Role; | 
|  | 6 |  |  |  |  | 22 |  | 
|  | 6 |  |  |  |  | 66 |  | 
| 4 | 6 |  |  | 6 |  | 23701 | use Scalar::Util; | 
|  | 6 |  |  |  |  | 16 |  | 
|  | 6 |  |  |  |  | 325 |  | 
| 5 | 6 |  |  | 6 |  | 2753 | use CatalystX::RequestModel::Utils::BadRequest; | 
|  | 6 |  |  |  |  | 2211 |  | 
|  | 6 |  |  |  |  | 9315 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | has ctx => (is=>'ro'); | 
| 8 |  |  |  |  |  |  | has current_namespace => (is=>'ro', predicate=>'has_current_namespace'); | 
| 9 |  |  |  |  |  |  | has current_parser => (is=>'ro', predicate=>'has_current_parser'); | 
| 10 |  |  |  |  |  |  | has catalyst_component_name => (is=>'ro'); | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | sub namespace { | 
| 13 | 51 |  |  | 51 |  | 187 | my ($class_or_self, @data) = @_; | 
| 14 | 51 | 100 |  |  |  | 189 | my $class = ref($class_or_self) ? ref($class_or_self) : $class_or_self; | 
| 15 | 51 | 100 |  |  |  | 194 | if(@data) { | 
| 16 | 18 |  |  |  |  | 51 | @data = map { split /\./, $_ } @data; | 
|  | 18 |  |  |  |  | 93 |  | 
| 17 | 18 |  |  |  |  | 88 | CatalystX::RequestModel::_add_metadata($class, 'namespace', @data); | 
| 18 |  |  |  |  |  |  | } | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 51 | 100 |  |  |  | 450 | return $class_or_self->namespace_metadata if $class_or_self->can('namespace_metadata'); | 
| 21 |  |  |  |  |  |  | } | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub content_in { | 
| 24 | 51 |  |  | 51 |  | 146 | my ($class_or_self, $ct) = @_; | 
| 25 | 51 | 100 |  |  |  | 146 | my $class = ref($class_or_self) ? ref($class_or_self) : $class_or_self; | 
| 26 | 51 | 100 |  |  |  | 137 | CatalystX::RequestModel::_add_metadata($class, 'content_in', $ct) if $ct; | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 51 | 100 |  |  |  | 334 | if($class_or_self->can('content_in_metadata')) { | 
| 29 | 11 |  |  |  |  | 46 | my ($ct) = $class_or_self->content_in_metadata;  # needed because this returns an array but we only want the first one | 
| 30 | 11 | 50 |  |  |  | 69 | return $ct if $ct; | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  | } | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | sub get_content_in { | 
| 35 | 45 |  |  | 45 | 0 | 920 | my $self = shift; | 
| 36 | 45 |  |  |  |  | 197 | my $ct = $self->content_in; | 
| 37 | 45 | 100 |  |  |  | 160 | return lc($ct) if $ct; | 
| 38 | 40 |  |  |  |  | 196 | return 'body'; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | sub content_type { | 
| 42 | 63 |  |  | 63 |  | 279 | my ($class_or_self, $ct) = @_; | 
| 43 | 63 | 100 |  |  |  | 214 | my $class = ref($class_or_self) ? ref($class_or_self) : $class_or_self; | 
| 44 | 63 | 100 |  |  |  | 298 | CatalystX::RequestModel::_add_metadata($class, 'content_type', $ct) if $ct; | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 63 | 50 |  |  |  | 408 | if($class_or_self->can('content_type_metadata')) { | 
| 47 | 63 |  |  |  |  | 233 | my ($ct) = $class_or_self->content_type_metadata;  # needed because this returns an array but we only want the first one | 
| 48 | 63 |  |  |  |  | 287 | return $ct; | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub property { | 
| 53 | 354 |  |  | 354 |  | 924 | my ($class_or_self, $attr, $data_proto, $options) = @_; | 
| 54 | 354 | 50 |  |  |  | 848 | my $class = ref($class_or_self) ? ref($class_or_self) : $class_or_self; | 
| 55 | 354 | 50 |  |  |  | 857 | if(defined $data_proto) { | 
| 56 | 354 | 50 | 50 |  |  | 1182 | my $data = (ref($data_proto)||'') eq 'HASH' ? $data_proto : +{ name => $attr }; | 
| 57 | 354 | 100 |  |  |  | 965 | $data->{name} = $attr unless exists($data->{name}); | 
| 58 | 354 |  |  |  |  | 1621 | CatalystX::RequestModel::_add_metadata($class, 'property_data', +{$attr => $data}); | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | sub properties { | 
| 63 | 63 |  |  | 63 |  | 140 | my ($class_or_self, @data) = @_; | 
| 64 | 63 | 50 |  |  |  | 163 | my $class = ref($class_or_self) ? ref($class_or_self) : $class_or_self; | 
| 65 | 63 |  |  |  |  | 212 | while(@data) { | 
| 66 | 0 |  |  |  |  | 0 | my $attr = shift(@data); | 
| 67 | 0 | 0 | 0 |  |  | 0 | my $data = (ref($data[0])||'') eq 'HASH' ? shift(@data) : +{ name => $attr }; | 
| 68 | 0 | 0 |  |  |  | 0 | $data->{name} = $attr unless exists($data->{name}); | 
| 69 | 0 |  |  |  |  | 0 | CatalystX::RequestModel::_add_metadata($class, 'property_data', +{$attr => $data}); | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 63 | 50 |  |  |  | 416 | return $class_or_self->property_data_metadata if $class_or_self->can('property_data_metadata'); | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub COMPONENT { | 
| 76 | 84 |  |  | 84 | 0 | 918486 | my ($class, $app, $args) = @_; | 
| 77 | 84 |  |  |  |  | 811 | $args = $class->merge_config_hashes($class->config, $args); | 
| 78 | 84 |  |  |  |  | 15967 | return bless $args, $class; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | ## TODO handle if we are wrapping a model that already does ACCEPT_CONTEXT | 
| 82 |  |  |  |  |  |  | sub ACCEPT_CONTEXT { | 
| 83 | 33 |  |  | 33 | 0 | 2253 | my $self = shift; | 
| 84 | 33 |  |  |  |  | 68 | my $c = shift; | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 33 |  |  |  |  | 237 | my %args = (%$self, @_); | 
| 87 | 33 |  |  |  |  | 171 | my %request_args = $self->parse_content_body($c, %args); | 
| 88 | 32 |  |  |  |  | 194 | my %init_args = (%args, %request_args, ctx=>$c); | 
| 89 | 32 |  |  |  |  | 90 | my $class = ref($self); | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 32 |  |  |  |  | 173 | return my $request_model = $self->build_request_model($c, $class, %init_args); | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | sub build_request_model { | 
| 95 | 32 |  |  | 32 | 0 | 128 | my ($self, $c, $class, %init_args) = @_; | 
| 96 |  |  |  |  |  |  | my $request_model = eval { | 
| 97 |  |  |  |  |  |  | $class->new(%init_args) | 
| 98 | 32 |  | 66 |  |  | 67 | } || do { | 
| 99 |  |  |  |  |  |  | CatalystX::RequestModel::Utils::BadRequest->throw(class=>$class, error_trace=>$@); | 
| 100 |  |  |  |  |  |  | }; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 31 |  |  |  |  | 10698 | return $request_model; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub parse_content_body { | 
| 106 | 33 |  |  | 33 | 0 | 133 | my ($self, $c, %args) = @_; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 33 |  |  |  |  | 166 | my @rules = $self->properties; | 
| 109 | 33 |  |  |  |  | 139 | my @ns = $self->get_namespace(%args); | 
| 110 | 33 |  |  |  |  | 149 | my $parser_class = $self->get_content_body_parser_class($self->get_content_type($c)); | 
| 111 |  |  |  |  |  |  | my $parser = exists($args{current_parser}) ? | 
| 112 |  |  |  |  |  |  | $args{current_parser} : | 
| 113 | 33 | 100 |  |  |  | 291 | $parser_class->new(ctx=>$c, request_model=>$self ); | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 32 | 100 |  |  |  | 108 | $parser->{context} = $args{context} if exists $args{context}; ## TODO ulgy | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 32 |  |  |  |  | 199 | return my %request_args = $parser->parse(\@ns, \@rules); | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub get_content_type { | 
| 121 | 33 |  |  | 33 | 0 | 82 | my ($self, $c) = @_; | 
| 122 | 33 |  |  |  |  | 114 | my $ct = $c->req->content_type; | 
| 123 | 33 | 100 | 66 |  |  | 3251 | return 'application/x-www-form-urlencoded' if !$ct && $c->req->method eq 'GET'; | 
| 124 | 32 | 100 |  |  |  | 131 | return 'application/x-www-form-urlencoded' if $self->get_content_in eq 'query'; | 
| 125 | 30 |  |  |  |  | 118 | return $ct; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub get_namespace { | 
| 129 | 33 |  |  | 33 | 0 | 107 | my ($self, %args) = @_; | 
| 130 | 33 | 50 |  |  |  | 114 | return @{$args{current_namespace}} if exists($args{current_namespace}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 131 | 33 |  |  |  |  | 178 | return grep { defined $_ } $self->namespace; | 
|  | 33 |  |  |  |  | 153 |  | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub get_content_body_parser_class { | 
| 135 | 33 |  |  | 33 | 0 | 153 | my ($self, $content_type) = @_; | 
| 136 | 33 |  |  |  |  | 144 | return my $parser_class = CatalystX::RequestModel::content_body_parser_for($content_type); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub get_attribute_value_for { | 
| 140 | 96 |  |  | 96 | 0 | 202 | my ($self, $attr) = @_; | 
| 141 | 96 |  |  |  |  | 3264 | return $self->$attr; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub nested_params { | 
| 145 | 30 |  |  | 30 | 1 | 1022 | my $self = shift; | 
| 146 | 30 |  |  |  |  | 70 | my %return; | 
| 147 | 30 |  |  |  |  | 109 | foreach my $p ($self->properties) { | 
| 148 | 105 |  |  |  |  | 290 | my ($attr, $meta) = %$p; | 
| 149 | 105 | 100 |  |  |  | 295 | if(my $predicate = $meta->{attr_predicate}) { | 
| 150 | 92 | 100 |  |  |  | 213 | if($meta->{omit_empty}) { | 
| 151 | 88 | 100 |  |  |  | 3785 | next unless $self->$predicate;  # skip empties when omit_empty=>1 | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 96 |  |  |  |  | 258 | my $value = $self->get_attribute_value_for($attr); | 
| 156 | 96 | 100 | 100 |  |  | 532 | if( (ref($value)||'') eq 'ARRAY') { | 
|  |  | 100 | 66 |  |  |  |  | 
| 157 | 12 |  |  |  |  | 27 | my @gathered = (); | 
| 158 | 12 |  |  |  |  | 30 | foreach my $v (@$value) { | 
| 159 | 23 | 100 |  |  |  | 69 | if(Scalar::Util::blessed($v)) { | 
| 160 | 14 |  |  |  |  | 37 | my $params = $v->nested_params; | 
| 161 | 14 | 100 |  |  |  | 55 | push @gathered, $params if keys(%$params); | 
| 162 |  |  |  |  |  |  | } else { | 
| 163 | 9 |  |  |  |  | 23 | push @gathered, $v; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | } | 
| 167 | 12 |  |  |  |  | 50 | $return{$attr} = \@gathered; | 
| 168 |  |  |  |  |  |  | } elsif(Scalar::Util::blessed($value) && $value->can('nested_params')) { | 
| 169 | 2 |  |  |  |  | 15 | my $params = $value->nested_params; | 
| 170 | 2 | 50 |  |  |  | 11 | next unless keys(%$params); | 
| 171 | 2 |  |  |  |  | 7 | $return{$attr} = $params; | 
| 172 |  |  |  |  |  |  | } else { | 
| 173 | 82 |  |  |  |  | 247 | $return{$attr} = $value; | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 30 |  |  |  |  | 183 | return \%return; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub get { | 
| 180 | 0 |  |  | 0 | 1 |  | my ($self, @fields) = @_; | 
| 181 | 0 |  |  |  |  |  | my $p = $self->nested_params; | 
| 182 | 0 |  |  |  |  |  | my @got = @$p{@fields}; | 
| 183 | 0 |  |  |  |  |  | return @got; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | 1; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | =head1 NAME | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | CatalystX::RequestModel::DoesRequestModel - Role to provide request model API | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | Generally you will apply this role via L<CatalystX::RequestModel> | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | package Example::Model::AccountRequest; | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | use Moose; | 
| 199 |  |  |  |  |  |  | use CatalystX::RequestModel; | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | extends 'Catalyst::Model'; | 
| 202 |  |  |  |  |  |  | namespace 'person'; | 
| 203 |  |  |  |  |  |  | content_type 'application/x-www-form-urlencoded'; | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | has username => (is=>'ro', property=>{always_array=>1}); | 
| 206 |  |  |  |  |  |  | has first_name => (is=>'ro', property=>1); | 
| 207 |  |  |  |  |  |  | has last_name => (is=>'ro', property=>1); | 
| 208 |  |  |  |  |  |  | has notes => (is=>'ro', property=>+{ expand=>'JSON' }); | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | See L<CatalystX::RequestModel> for a more general overview. | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | A role that gives a L<Catalyst::Model> the ability to indicate which of its attributes should be | 
| 215 |  |  |  |  |  |  | consider request model data, as well as additional need meta data so that we can process it | 
| 216 |  |  |  |  |  |  | properly. | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | Since we need to wrap C<has> you should never apply this role manually but rather instead use | 
| 219 |  |  |  |  |  |  | L<CatalystX::RequestModel> to apply it for you.   If you need to customize this role you will | 
| 220 |  |  |  |  |  |  | also need to subclass L<CatalystX::RequestModel> and have that new subclass apply you custom | 
| 221 |  |  |  |  |  |  | role.   Please ping me if you really need this since I guess we could change L<CatalystX::RequestModel> | 
| 222 |  |  |  |  |  |  | to make it easier to supply a custom role, just let me know your use case. | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | =head1 METHODS | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | This class defines the following public API | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =head2 nested_params | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | Returns all the attributes marked as request properties in the form of a hashref.  If any of the | 
| 231 |  |  |  |  |  |  | properties refer to an array or indexed value, or an object, we automatically follow that to | 
| 232 |  |  |  |  |  |  | return all the property data below. | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | Attributes that are empty will be left out of the return data structure. | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | Easiest way to get all your data but then again you get a structure that is very tightly tied to | 
| 237 |  |  |  |  |  |  | your request model. | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | =head2 get | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | Accepts a list of attributes that refer to request properties and returns their values.  In the case | 
| 242 |  |  |  |  |  |  | when the attribute listed has no value, you will instead get an C<undef>. | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | =head1 EXCEPTIONS | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | This class can throw the following exceptions: | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | =head2 Invalid Request Content Body | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | If we can't create an instance of the request model we throw a L<CatalystX::RequestModel::Utils::BadRequest>. | 
| 251 |  |  |  |  |  |  | This will get interpretated as an HTTP 400 status client error if you are using L<CatalystX::Errors>. | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =head1 AUTHOR | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | See L<CatalystX::RequestModel>. | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | See L<CatalystX::RequestModel>. | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | =head1 LICENSE | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | See L<CatalystX::RequestModel>. | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | =cut | 
| 266 |  |  |  |  |  |  |  |