File Coverage

blib/lib/Mongoose.pm
Criterion Covered Total %
statement 46 90 51.1
branch 8 40 20.0
condition 6 28 21.4
subroutine 11 16 68.7
pod 7 7 100.0
total 78 181 43.0


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