File Coverage

blib/lib/Mandel/Model.pm
Criterion Covered Total %
statement 100 109 91.7
branch 33 42 78.5
condition 6 6 100.0
subroutine 22 22 100.0
pod 4 5 80.0
total 165 184 89.6


line stmt bran cond sub pod time code
1             package Mandel::Model;
2              
3             =head1 NAME
4              
5             Mandel::Model - An object modelling a document
6              
7             =head1 DESCRIPTION
8              
9             This class is used to descrieb the structure of L
10             in mongodb.
11              
12             =cut
13              
14 27     27   171 use Mojo::Base -base;
  27         64  
  27         351  
15 27     27   10309 use Mojo::Loader 'load_class';
  27         42577  
  27         1447  
16 27     27   153 use Mojo::Util;
  27         54  
  27         1077  
17 26     26   13708 use Mandel::Model::Field;
  26         63  
  26         231  
18 26     26   768 use Carp 'confess';
  26         92  
  26         16315  
19              
20             my $ANON = 1;
21              
22             =head1 ATTRIBUTES
23              
24             =head2 collection_name
25              
26             The name of the collection in the database. Default is the plural form of L.
27              
28             =head2 collection_class
29              
30             The class name of the collection class. This default to L.
31              
32             =head2 document_class
33              
34             The class name of the document this description is attached to. Default to
35             an autogenerated class name.
36              
37             =head2 name
38              
39             The name of this model. Same as given to L and
40             L.
41              
42             =cut
43              
44             has collection_name => sub {
45             my $self = shift;
46             my $name = $self->name;
47              
48             return $name =~ /s$/ ? $name : $name . 's' if $name;
49             confess "collection_name or name required in constructor";
50             };
51              
52             has collection_class => 'Mandel::Collection';
53              
54             has document_class => sub {
55             my $self = shift;
56             my $name = ucfirst $self->name || 'AnonDoc';
57             my $class = "Mandel::Document::__ANON_${ANON}__::$name"; # this might change
58              
59 5     5   30 eval <<" PACKAGE" or confess $@;
  5     5   10  
  5     1   23  
  5     4   28  
  5         10  
  5         20  
  1         33  
  4         108  
60             package $class;
61             use Mojo::Base "Mandel::Document";
62             sub model { \$self }
63             \$INC{"Mandel/Document/__ANON__$ANON.pm"} = "GENERATED";
64             PACKAGE
65              
66             $ANON++;
67             $class;
68             };
69              
70             has name => '';
71              
72             =head1 METHODS
73              
74             =head2 field
75              
76             $field_obj = $self->field('name');
77             $self = $self->field(name => \%meta);
78             $self = $self->field(['name1', 'name2'], \%meta);
79              
80             Used to define new field(s) or retrieve a defined L
81             object.
82              
83             =cut
84              
85             sub field {
86 17     17 1 39 my ($self, $name, $meta) = @_;
87              
88 17 100       50 if ($meta) {
89 12         37 return $self->_add_field($name => $meta); # $name might be an array-ref
90             }
91              
92 5 50       6 for (@{$self->{fields} || []}) {
  5         21  
93 15 100       37 return $_ if $name eq $_->name;
94             }
95              
96 0         0 return;
97             }
98              
99             sub _add_field {
100 12     12   24 my ($self, $fields, $meta) = @_;
101 12         48 my $class = $self->document_class;
102              
103             # Compile fieldibutes
104 12 100       113 for my $name (@{ref $fields eq 'ARRAY' ? $fields : [$fields]}) {
  12         51  
105 13         37 local $meta->{name} = $name;
106 13         147 my $field = Mandel::Model::Field->new($meta);
107 13         157 my $builder = $field->builder;
108 13         36 my $code = "";
109              
110 13         51 $code .= "package $class;\nsub $name {\n my \$raw = \$_[0]->data;\n";
111              
112 13 100       34 if ($builder) {
113 2         7 $code
114             .= "return exists \$raw->{'$name'} ? (\$raw->{'$name'}) : (\$raw->{'$name'} = \$_[0]->\$builder) if \@_ == 1;\n";
115             }
116             else {
117 11         28 $code .= "return \$raw->{'$name'} if \@_ == 1;\n";
118             }
119              
120 13         21 $code .= "local \$_ = \$_[1];\n";
121 13 100       84 $code .= $self->_field_type($meta->{isa}) if $meta->{isa};
122 13         36 $code .= "\$_[0]->{dirty}{$name} = 1;";
123 13         27 $code .= "\$raw->{'$name'} = \$_;\n";
124 13         20 $code .= "return \$_[0];\n}";
125              
126             # We compile custom attribute code for speed
127 26     26   144 no strict 'refs';
  26         45  
  26         6506  
128 13 50       42 warn "-- Attribute $name in $class\n$code\n\n" if $ENV{MOJO_BASE_DEBUG};
129 13 50   7 0 2190 Carp::croak "Mandel::Document error: $@ ($code)" unless eval "$code;1";
  6 100   2   66  
  6 100   1   98  
  5 50   1   4740  
  3 50   1   60  
  3 50       8  
  3 50       9  
  4 50       35  
  3         62  
  2         6  
  2         5  
  2         5  
  2         901  
  2         67  
  2         10  
  1         2  
  1         5  
  1         3  
  1         2  
  2         9  
  1         32  
  0         0  
  0         0  
  0         0  
  0         0  
  1         511  
  1         34  
  0            
  0            
  0            
  0            
130              
131 13         43 push @{$self->{fields}}, $field;
  13         61  
132             }
133              
134 12         45 $self;
135             }
136              
137             =head2 fields
138              
139             @fields = $self->fields;
140              
141             Get list of L objects in the order they were added to
142             thie model.
143              
144             =cut
145              
146             sub fields {
147 4 100   4 1 75 @{$_[0]->{fields} || []};
  4         49  
148             }
149              
150             sub _field_type {
151 7     7   43 my ($self, $type) = @_;
152 7         17 my $code = "";
153              
154 26     26   23789 use Types::Standard qw( Num );
  26         1857672  
  26         275  
155              
156 7 100 100     26 if ($type->has_coercion and $type->coercion->can_be_inlined) {
157 3         113 $code .= '$_ = '.$type->coercion->inline_coercion('$_').";\n";
158             }
159 7 100       355 if ($type->can_be_inlined) {
160 7         135 $code .= $type->inline_assert('$_')."\n";
161             }
162 8 100       390 if ($type->is_a_type_of(Num)) {
163 5         376 $code .= "\$_ += 0;\n";
164             }
165              
166 7         3769 return $code;
167             }
168              
169             =head2 relationship
170              
171             $rel_obj = $self->relationship($type => $accessor => 'Other::Document::Class', %args);
172             $rel_obj = $self->relationship($accessor);
173              
174             This method is used to describe a relationship between two documents.
175              
176             See L, L or
177             L.
178              
179             C<$accessor> will be used as l,
180             "Other::Document::Class" will be used as L
181             and C will be used as
182             L.
183              
184             C<%args> is passed on the the L constructor.
185              
186             =cut
187              
188             sub relationship {
189 6     4 1 2968 my $self = shift;
190              
191 6 50       37 if (@_ == 1) {
192 1         3 return $self->{relationship}{$_[0]};
193             }
194              
195 5         17 my ($type, $field, $other, %args) = @_;
196 5         30 my $class = 'Mandel::Relationship::' . Mojo::Util::camelize($type);
197 5         99 my $e = load_class $class;
198              
199 4 100       69 confess $e if ref $e;
200              
201 4         23 $self->{relationship}{$field}
202             = $class->new(accessor => $field, document_class => $self->document_class, related_class => $other, %args,);
203             }
204              
205             =head2 new_collection
206              
207             $self->new_collection($connection);
208              
209             Returns a new instance of L.
210              
211             =cut
212              
213             sub new_collection {
214 3     5 1 19 my ($self, $connection, %args) = @_;
215              
216 3   100     13 $self->collection_class->new(
217             {connection => $connection || confess('$model->new_collection($connection)'), model => $self, %args,});
218             }
219              
220             =head1 SEE ALSO
221              
222             L, L, L
223              
224             =head1 AUTHOR
225              
226             Jan Henning Thorsen - C
227              
228             =cut
229              
230             1;