File Coverage

blib/lib/Mandel/Model.pm
Criterion Covered Total %
statement 101 109 92.6
branch 36 44 81.8
condition 6 6 100.0
subroutine 22 22 100.0
pod 4 5 80.0
total 169 186 90.8


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