File Coverage

blib/lib/Mongoose.pm
Criterion Covered Total %
statement 45 89 50.5
branch 8 40 20.0
condition 5 22 22.7
subroutine 11 16 68.7
pod 7 7 100.0
total 76 174 43.6


line stmt bran cond sub pod time code
1             package Mongoose;
2             $Mongoose::VERSION = '2.01';
3 25     25   5035180 use MooseX::Singleton;
  25         9624822  
  25         4817  
4 25     25   888713 use Class::MOP;
  25         50  
  25         611  
5 25     25   13249 use MongoDB;
  25         43313931  
  25         838  
6 25     25   215 use Carp;
  25         50  
  25         1360  
7 25     25   132 use version;
  25         44  
  25         140  
8              
9             with 'Mongoose::Role::Naming';
10              
11             has _db => (
12             is => 'rw',
13             isa => 'HashRef',
14             default => sub {{}},
15             );
16              
17             has _client => (
18             is => 'rw',
19             isa => "HashRef",
20             lazy => 1,
21             default => sub {{}},
22             clearer => 'disconnect'
23             );
24              
25             has _args => (
26             is => 'rw',
27             isa => 'HashRef',
28             default => sub{{}}
29             );
30              
31             has _alias => ( # keep track of aliased() document classes.
32             is => 'rw',
33             isa => 'HashRef',
34             default => sub{{}}
35             );
36              
37             has _config => ( # Store document classes configuration
38             is => 'rw',
39             isa => 'HashRef',
40             default => sub{{}}
41             );
42              
43             has _ns => ( # Selected namespace/tenant
44             is => 'rw',
45             isa => 'Str',
46             default => sub{'default'}
47             );
48              
49             sub namespace {
50 0     0 1 0 my $self = shift;
51 0 0       0 if ( my $name = shift ) { $self->_ns($name) }
  0         0  
52 0         0 $self->_ns;
53             }
54              
55             sub db {
56 24     24 1 766225 my $self = shift;
57 24 100       169 my %p = @_ == 1 ? (db_name=>shift) : @_;
58 24   33     175 my $now = delete($p{'-now'}) || defined wantarray;
59 24         117 my $name = $self->_add_args( \%p );
60              
61 24 50       196 return $self->connect($name) if $now;
62             }
63              
64             sub class_config {
65 2     2 1 4 my ( $self, $class_name, $config ) = @_;
66              
67             # Set
68 2 50       56 return $self->_config->{$class_name} = $config if $config;
69              
70             # Get
71 0   0     0 my $class = $self->aliased(ref $class_name || $class_name);
72             confess "Document class '$class' is not registered. Registered classes are: ".
73 0 0       0 join(', ', keys %{$self->_config}) unless exists $self->_config->{$class};
  0         0  
74 0         0 $self->_config->{$class};
75             }
76              
77             # setup db config and class to db mapping if class exists.
78             sub _add_args {
79 24     24   70 my ( $self, $args ) = @_;
80 24         50 my $name = 'default';
81 24   33     1285 my $ns = delete $args->{namespace} || $self->_ns;
82 24 50       148 $ns = [$ns] unless ref $ns eq 'ARRAY';
83              
84 24 50       170 if ( my $class = delete $args->{class} ) {
85 0 0       0 $class = [$class] unless ref $class eq 'ARRAY';
86 0         0 $name = join "-", @$class;
87 0         0 $self->_args->{class}{$_} = $name for @$class;
88             }
89              
90             # Keep track of config for every namespace
91 24         689 $self->_args->{db}{$_}{$name} = $args for @$ns;
92              
93 24         822 $name;
94             }
95              
96             # Connection/db name for a given class
97             sub _name_for_class {
98 0     0   0 my ( $self, $class ) = @_;
99 0 0       0 return 'default' unless $class;
100 0 0       0 $self->_args->{class}{$self->aliased($class)} || 'default';
101             }
102              
103             # Go thru class-db mapping and ensure to get a connected db.
104             sub connection {
105 0     0 1 0 my ( $self, $class ) = @_;
106 0         0 my $name = $self->_name_for_class($class);
107 0 0       0 $self->_db->{$self->_ns}{$name} || $self->connect($name);
108             }
109              
110             sub connect {
111 24     24 1 69 my ( $self, $name ) = @_;
112 24   50     74 $name ||= 'default';
113 24         540 my $ns = $self->_ns;
114              
115 24 50       998 confess "Namespace `$ns` is not defined" unless $self->_args->{db}{$ns};
116              
117             # Ensure we have a config for $ns and $name or fallback to defaults
118 24 50       995 unless ( exists $self->_args->{db}{$ns}{$name} ) {
119 0 0       0 if ( exists $self->_args->{db}{$ns}{default} ) { $name = 'default' }
  0 0       0  
120 0         0 elsif ( exists $self->_args->{db}{default}{$name} ) { $ns = 'default' }
121 0         0 else { ($ns, $name) = ('default', 'default') }
122             }
123              
124 24         564 my %conf = %{ $self->_args->{db}{$ns}{$name} };
  24         466  
125 24         796 my $db_name = delete $conf{db_name};
126 24         135 my $client = $self->_get_client(%conf);
127              
128 24         1708749 $self->_db->{$ns}{$name} = $client->get_database( $db_name );
129             }
130              
131             sub _get_client {
132 24     24   108 my ( $self, %conf ) = @_;
133 24   50     221 $conf{host} ||= 'mongodb://localhost:27017';
134 24   33     655 $self->_client->{$conf{host}} ||= MongoDB::MongoClient->new(%conf);
135             }
136              
137             sub load_schema {
138 0     0 1   my ( $self, %args ) = @_;
139 0           my $shorten = delete $args{shorten};
140 0           my $search_path = delete $args{search_path};
141              
142 0           require Module::Pluggable;
143 0           Module::Pluggable->import( search_path => $search_path );
144 0           for my $module ( $self->plugins ) {
145 0           eval "require $module";
146 0 0         croak $@ if $@;
147 0 0 0       if ( $shorten && $module =~ m/$search_path\:\:(.*?)$/ ) {
148 0           my $short_name = $1;
149 25     25   25850 no strict 'refs';
  25         56  
  25         6503  
150 0           *{ $short_name . "::" } = \*{ $module . "::" };
  0            
  0            
151 0           Class::MOP::store_metaclass_by_name( $short_name, $module->meta );
152 0           Class::MOP::weaken_metaclass( $short_name );
153 0           $self->aliased($short_name => $module);
154             }
155             }
156              
157             # Resolve class names on configured database per loaded class
158 0 0 0       if ( $shorten && ( my $class_map = $self->_args->{class} ) ) {
159 0           for ( keys %$class_map ) {
160 0           $class_map->{$self->aliased($_)} = delete $class_map->{$_};
161             }
162             }
163              
164 0           $self;
165             }
166              
167             sub aliased {
168 0     0 1   my ( $self, $alias, $class ) = @_;
169 0 0         $self->_alias->{$alias} = $class if $class;
170 0 0         exists $self->_alias->{$alias} ? $self->_alias->{$alias} : $alias;
171             }
172              
173             __PACKAGE__->meta->make_immutable();
174              
175             =head1 NAME
176              
177             Mongoose - MongoDB document to Moose object mapper
178              
179             =head1 SYNOPSIS
180              
181             package Person;
182             use Moose;
183             with 'Mongoose::Document';
184             has 'name' => ( is => 'rw', isa => 'Str' );
185              
186             package main;
187             use Mongoose;
188              
189             Mongoose->db('mydb');
190              
191             my $person = Person->new( name => 'Jack' );
192             $person->save;
193              
194             $person = Person->find_one({ name => 'Jack' });
195             say $person->name; # Jack
196              
197             Person->find({ name => qr/^J/' })->each(sub{
198             say "Found ", $person->name;
199             });
200              
201             $person->delete;
202              
203             =head1 DESCRIPTION
204              
205             This is a L<MongoDB> to L<Moose> object mapper. This module allows you to use the full
206             power of MongoDB within your Moose classes, without sacrificing MongoDB's
207             power, flexibility and speed.
208              
209             It's loosely inspired by Ruby's MongoMapper,
210             which is in turn loosely based on the ActiveRecord pattern.
211              
212             Start by reading the introduction L<Mongoose::Intro>.
213              
214             Or proceed directly to the L<Mongoose::Cookbook> for many day-to-day recipes.
215              
216             =head1 WARNING
217              
218             Since version 2.00 Mongoose only support the new L<MongoDB> driver v2.x.x which it's now required.
219              
220             Please let me know if you find anything strange using this new driver.
221              
222             =head1 METHODS
223              
224             =head2 db
225              
226             Sets the current MongoDB connection and/or db name.
227              
228             Mongoose->db( 'mydb' );
229              
230             The connection defaults to whatever MongoDB defaults are
231             (typically localhost:27017).
232              
233             For more control over the connection, C<db> takes the same parameters as
234             L<MongoDB::MongoClient>.
235              
236             my $db = Mongoose->db(
237             host => 'mongodb://somehost:27017',
238             read_pref_mode => 'secondaryPreferred',
239             db_name => 'mydb',
240             username => 'myuser',
241             password => 'mypass',
242             ssl => 1
243             );
244              
245             This will, in turn, instantiate a L<MongoDB::MongoClient> and return
246             a L<MongoDB::Database> object for C<mydb>.
247              
248             B<Important>: Mongoose will always defer connecting to Mongo
249             until the last possible moment. This is done to prevent
250             using the MongoDB driver in a forked environment (ie. with a
251             prefork appserver like Starman, Hypnotoad or Catalyst's
252             HTTP::Prefork).
253              
254             If you prefer to connect while setting the connection string,
255             use one of these options:
256              
257             Mongoose->db( db_name=>'mydb', -now=>1 ); # connect now
258              
259             # or by waiting for a return value
260              
261             my $db = Mongoose->db( 'mydb' );
262              
263             # or explicitly:
264              
265             Mongoose->db( 'mydb' );
266             Mongoose->connect;
267              
268             You can separate your classes storage on multiple hosts/databases
269             by calling db() several times:
270              
271             # Default host/database (connect now!)
272             my $db = Mongoose->db( 'mydb' );
273              
274             # Other database for some class (defer connection)
275             Mongoose->db( db_name => 'my_other_db', class => 'Log' );
276              
277             # Other database on other host for several classes
278             Mongoose->db(
279             db_name => 'my_remote_db',
280             host => 'mongodb://192.168.1.23:27017',
281             class => [qw/ Author Post /]
282             );
283              
284             There is one more level of abstraction called C<namespace> so you can implement multitenant schemas,
285             with that you can map different database configuration to your clases and your schema will select
286             the ones corresponding to the current C<namespace>. In most of the use cases it will just defalt to
287             the "default" namespace.
288              
289             # Default host/database for all loaded classes
290             Mongoose->db( 'mydb' );
291              
292             # Other database for some classes on a different namespace
293             Mongoose->db(
294             db_name => 'other_db',
295             class => [qw/ Category Post /],
296             namespace => 'blog_b'
297             );
298              
299             =head2 connect
300              
301             Connects to Mongo using the connection arguments passed to the C<db> method.
302              
303             =head2 connection
304              
305             Returns a connection to the database for the provided class name or the default
306             connection when no class name is provided.
307              
308             =head2 load_schema
309              
310             Uses L<Module::Pluggable> to C<require> all modules under a given search path
311             or search dir.
312              
313             All arguments will be sent to Module::Pluggable's C<import>, except for
314             Mongoose specific ones.
315              
316             package main;
317             use Mongoose;
318              
319             # to load a schema from a namespace path:
320             Mongoose->load_schema( search_path=>'MyApp::Schema' );
321              
322              
323             This method can be used to shorten class names, aliasing them for
324             convenience if you wish:
325              
326             Mongoose->load_schema( search_path=>'MyApp::Schema', shorten=>1 );
327              
328             Will shorten the module name to it's last bit:
329              
330             MyApp::Schema::Author->new( ... );
331              
332             # becomes
333              
334             Author->new( ... );
335              
336             =head2 disconnect
337              
338             Unsets the Mongoose connection handler/s.
339              
340             =head2 namespace
341              
342             The current namespace. You will use this in case your schema db's are configured using namespaces as described in C<db>.
343             You can switch the namespace by setting it like:
344              
345             Mongoose->namespace('my_namespace');
346              
347             It defauls to the "default" namespace.
348              
349             =head2 class_config
350              
351             Keep track of document classes config solving aliasing indirection.
352              
353             =head2 aliased
354              
355             Keep track of aliasing classes. Useful to retrieve full document class from a shortened one.
356              
357             =head1 COLLECTION NAMING
358              
359             By default, Mongoose composes the Mongo collection name from your package name
360             by replacing double-colon C<::> with underscores C<_>, separating camel-case,
361             such as C<aB> with C<a_b> and uppercase with lowercase letters.
362              
363             This behaviour can be changed by choosing other named method or by setting
364             the collection naming routine with a C<closure> as explained in L<Mongoose::Role::Naming>.
365              
366             =head1 REPOSITORY
367              
368             Fork me on github: L<http://github.com/rodrigolive/mongoose>
369              
370             =head1 BUGS
371              
372             This is a WIP. Please report bugs via Github Issue reporting L<https://github.com/rodrigolive/mongoose/issues>.
373             Test cases highly desired and appreciated.
374              
375             =head1 TODO
376              
377             * Better error control
378              
379             * Allow query->fields to control which fields get expanded into the object.
380              
381             * Better documentation.
382              
383             =head1 AUTHOR
384              
385             Rodrigo de Oliveira (rodrigolive), C<rodrigolive@gmail.com>
386              
387             =head1 MAINTAINER
388              
389             Diego Kuperman (diegok)
390              
391             =head1 CONTRIBUTORS
392              
393             Arthur Wolf
394             Solli Moreira Honorio (shonorio)
395             Michael Gentili (gentili)
396             Kang-min Liu (gugod)
397             Allan Whiteford (allanwhiteford)
398             Kartik Thakore (kthakore)
399             David Golden (dagolden)
400             Mohammad S Anwar (manwar)
401              
402             =head1 LICENSE
403              
404             This library is free software. You can redistribute it and/or modify it under
405             the same terms as Perl itself.
406