File Coverage

blib/lib/Mandel/Relationship/HasMany.pm
Criterion Covered Total %
statement 25 61 40.9
branch 0 22 0.0
condition 0 8 0.0
subroutine 7 16 43.7
pod 1 1 100.0
total 33 108 30.5


line stmt bran cond sub pod time code
1             package Mandel::Relationship::HasMany;
2              
3             =head1 NAME
4              
5             Mandel::Relationship::HasMany - A field relates to many other mongodb document
6              
7             =head1 DESCRIPTION
8              
9             L is a class used to describe the relationship
10             between one document that has a relationship to many other documents.
11             The connection between the documents is described in the database using
12             L.
13              
14             =head1 DATABASE STRUCTURE
15              
16             A "person" that I "cats" will look like this in the database:
17              
18             mongodb# db.persons.find();
19             { "_id" : ObjectId("53529f28c5483e4977020000") }
20              
21             mongodb# db.cats.find({ "person.$id": ObjectId("53529f28c5483e4977020000") })
22             {
23             "_id" : ObjectId("53529f28c5483e5077040000"),
24             "person" : DBRef("persons", ObjectId("53529f28c5483e4977020000"))
25             }
26             {
27             "_id" : ObjectId("6432574384483e4978010000"),
28             "person" : DBRef("persons", ObjectId("53529f28c5483e4977020000"))
29             }
30              
31             A "has many" on one side is L on the other
32             side.
33              
34             =head1 SYNOPSIS
35              
36             =head2 Using DSL
37              
38             package MyModel::Person;
39             use Mandel::Document;
40             has_many cats => 'MyModel::Cat';
41              
42             =head2 Using object oriented interface
43              
44             MyModel::Person->model->relationship(
45             "has_many",
46             "cats",
47             "MyModel::Cat",
48             );
49              
50             See also L.
51              
52             =head2 Methods generated
53              
54             # non-blocking
55             $person = MyModel::Person->new->add_cats(\%constructor_args, sub {
56             my($person, $err, $cat_obj) = @_;
57             # ...
58             });
59              
60             $person = MyModel::Person->new->add_cats($cat_obj, sub {
61             my($person, $err, $cat_obj) = @_;
62             # ...
63             });
64              
65             $person = MyModel::Cat->new->cats(sub {
66             my($self, $err, $array_of_cats) = @_;
67             # ...
68             });
69              
70             # blocking
71             $cat_obj = MyModel::Person->new->add_cats(\%args);
72             $cat_obj = MyModel::Person->new->add_cats($cat_obj);
73             $array_of_cats = MyModel::Person->new->cats;
74              
75             $cat_collection = MyModel::Person->new->search_cats;
76              
77             =cut
78              
79 1     1   698 use Mojo::Base 'Mandel::Relationship';
  1         2  
  1         6  
80 1     1   128 use Mojo::Util;
  1         1  
  1         36  
81 1     1   5 use Mango::BSON 'bson_dbref';
  1         2  
  1         1002  
82              
83             =head1 ATTRIBUTES
84              
85             L inherits all attributes from
86             L and implements the following new ones.
87              
88             =head2 add_method_name
89              
90             The name of the method used to add another document to the relationship.
91              
92             =head2 search_method_name
93              
94             The name of the method used to search related documents.
95              
96             =cut
97              
98             has add_method_name => sub { sprintf 'add_%s', shift->accessor };
99             has search_method_name => sub { sprintf 'search_%s', shift->accessor };
100              
101             =head1 METHODS
102              
103             L inherits all methods from
104             L and implements the following new ones.
105              
106             =head2 monkey_patch
107              
108             Add methods to L.
109              
110             =cut
111              
112             sub monkey_patch {
113 1     1 1 28 shift->_monkey_patch_all_method->_monkey_patch_add_method->_monkey_patch_search_method;
114             }
115              
116             sub _monkey_patch_all_method {
117 1     1   2 my $self = shift;
118 1         3 my $search = $self->search_method_name;
119 1         12 my $accessor = $self->accessor;
120              
121             Mojo::Util::monkey_patch(
122             $self->document_class,
123             $accessor,
124             sub {
125 0     0   0 my ($doc, $cb) = @_;
        0      
126 0 0       0 my $cached = delete $doc->{fresh} ? undef : $doc->_cache($accessor);
127              
128             # Blocking
129 0 0       0 unless ($cb) {
130 0 0       0 return $cached if $cached;
131 0         0 return $doc->_cache($accessor => $doc->$search->all);
132             }
133              
134 0 0       0 if ($cached) {
135 0         0 $doc->$cb('', $cached);
136             }
137             else {
138             $doc->$search->all(
139             sub {
140 0     0   0 my ($collection, $err, $objs) = @_;
141 0 0       0 $doc->_cache($accessor => $objs) unless $err;
142 0         0 $doc->$cb($err, $objs);
143             }
144 0         0 );
145             }
146              
147 0         0 return $doc;
148             }
149 1         11 );
150              
151 1         30 return $self;
152             }
153              
154             sub _monkey_patch_add_method {
155 1     1   31 my $self = shift;
156 1         7 my $foreign_field = $self->foreign_field;
157 1         9 my $accessor = $self->accessor;
158              
159             Mojo::Util::monkey_patch(
160             $self->document_class,
161             $self->add_method_name,
162             sub {
163 0     0   0 my ($doc, $obj, $cb) = @_;
        0      
164 0         0 my $cached = $doc->_cache($accessor);
165              
166 0 0       0 if (ref $obj eq 'HASH') {
167 0         0 $obj = $self->_related_model->new_collection($doc->connection)->create($obj);
168             }
169              
170 0         0 $obj->data->{$foreign_field} = bson_dbref $doc->model->collection_name, $doc->id;
171 0 0       0 $obj->dirty->{$foreign_field} = 1 if $obj->dirty;
172              
173             # Blocking
174 0 0       0 unless ($cb) {
175 0 0       0 push @$cached, $obj if $cached;
176 0         0 $obj->save;
177 0         0 $doc->save;
178 0         0 return $obj;
179             }
180              
181             # Non-blocking
182             Mojo::IOLoop->delay(
183             sub {
184 0     0   0 my ($delay) = @_;
185 0         0 $obj->save($delay->begin);
186 0         0 $doc->save($delay->begin);
187             },
188             sub {
189 0     0   0 my ($delay, $o_err, $d_err) = @_;
190 0   0     0 my $err = $o_err || $d_err;
191 0 0 0     0 push @$cached, $obj if !$err and $cached;
192 0         0 $doc->$cb($err, $obj);
193             },
194 0         0 );
195              
196 0         0 return $doc;
197             }
198 1         7 );
199              
200 1         33 return $self;
201             }
202              
203             sub _monkey_patch_search_method {
204 1     1   1 my $self = shift;
205 1         4 my $foreign_field = $self->foreign_field;
206 1         11 my $related_class = $self->related_class;
207              
208             Mojo::Util::monkey_patch(
209             $self->document_class,
210             $self->search_method_name,
211             sub {
212 0     0   0 my ($doc, $query, $extra) = @_;
        0      
213 0         0 my $related_model = $self->_related_model;
214              
215             return $related_model->new_collection(
216             $doc->connection,
217             extra => $extra || {},
218 0 0 0     0 query => {%{$query || {}}, sprintf('%s.$id', $foreign_field) => $doc->id,},
  0         0  
219             );
220             }
221 1         8 );
222              
223 1         29 return $self;
224             }
225              
226             =head1 SEE ALSO
227              
228             L, L, L
229              
230             =head1 AUTHOR
231              
232             Jan Henning Thorsen - C
233              
234             =cut
235              
236             1;