File Coverage

blib/lib/Catalyst/TraitFor/Model/DBIC/Schema/SchemaProxy.pm
Criterion Covered Total %
statement 44 44 100.0
branch 9 10 90.0
condition n/a
subroutine 7 7 100.0
pod n/a
total 60 61 98.3


line stmt bran cond sub pod time code
1             package Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy;
2              
3 1     1   4674 use namespace::autoclean;
  1         3  
  1         15  
4 1     1   99 use Moose::Role;
  1         3  
  1         10  
5 1     1   6357 use Carp::Clan '^Catalyst::Model::DBIC::Schema';
  1         3  
  1         10  
6 1     1   124 use Catalyst::Model::DBIC::Schema::Types 'Schema';
  1         2  
  1         18  
7              
8             =head1 NAME
9              
10             Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy - Proxy Schema Methods and
11             Options from Model
12              
13             =head1 DESCRIPTION
14              
15             Allows you to call your L<DBIx::Class::Schema> methods directly on the Model
16             instance, and passes config options to your L<DBIx::Class::Schema> and
17             L<DBIx::Class::ResultSet> attributes at C<BUILD> time.
18              
19             Methods and attributes local to your C<Model> take precedence over
20             L<DBIx::Class::Schema> or L<DBIx::Class::ResultSet> methods and attributes.
21              
22             =head1 CREATING SCHEMA CONFIG ATTRIBUTES
23              
24             To create attributes in your C<Schema.pm>, use either Moose or
25             L<Class::Accessor::Grouped>, which is inherited from by all L<DBIx::Class>
26             classes automatically. E.g.:
27              
28             __PACKAGE__->mk_group_accessors(simple => qw/
29             config_key1
30             config_key2
31             ...
32             /);
33              
34             Or with L<Moose>:
35              
36             use Moose;
37             has config_key1 => (is => 'rw', default => 'default_value');
38              
39             This code can be added after the md5sum on L<DBIx::Class::Schema::Loader>
40             generated schemas.
41              
42             At app startup, any non-local options will be passed to these accessors, and can
43             be accessed as usual via C<< $schema->config_key1 >>.
44              
45             These config values go into your C<Model::DB> block, along with normal config
46             values.
47              
48             =head1 CREATING RESULTSET CONFIG ATTRIBUTES
49              
50             You can create classdata on L<DBIx::Class::ResultSet> classes to hold values
51             from L<Catalyst> config.
52              
53             The code for this looks something like this:
54              
55             package MySchema::ResultSet::Foo;
56              
57             use base 'DBIx::Class::ResultSet';
58              
59             __PACKAGE__->mk_group_accessors(inherited => qw/
60             rs_config_key1
61             rs_config_key2
62             ...
63             /);
64             __PACKAGE__->rs_config_key1('default_value');
65              
66             Or, if you prefer L<Moose>:
67              
68             package MySchema::ResultSet::Foo;
69              
70             use Moose;
71             use MooseX::NonMoose;
72             use MooseX::ClassAttribute;
73             extends 'DBIx::Class::ResultSet';
74              
75             sub BUILDARGS { $_[2] } # important
76              
77             class_has rs_config_key1 => (is => 'rw', default => 'default_value');
78              
79             ...
80              
81             __PACKAGE__->meta->make_immutable;
82              
83             1;
84              
85             In your catalyst config, use the generated Model name as the config key, e.g.:
86              
87             <Model::DB::Users>
88             strict_passwords 1
89             </Model::DB::Users>
90              
91             =cut
92              
93             after setup => sub {
94             my ($self, $args) = @_;
95              
96             my $schema = $self->schema;
97              
98             my $was_mutable = $self->meta->is_mutable;
99              
100             $self->meta->make_mutable;
101             $self->meta->add_attribute('schema',
102             is => 'rw',
103             isa => Schema,
104             handles => $self->_delegates # this removes the attribute too
105             );
106             $self->meta->make_immutable unless $was_mutable;
107              
108             $self->schema($schema) if $schema;
109             };
110              
111             after BUILD => sub {
112             my ($self, $args) = @_;
113              
114             $self->_pass_options_to_schema($args);
115              
116             for my $source ($self->schema->sources) {
117             my $config_key = 'Model::' . $self->model_name . '::' . $source;
118             my $config = $self->app_class->config->{$config_key};
119             next unless $config;
120             $self->_pass_options_to_resultset($source, $config);
121             }
122             };
123              
124             sub _delegates {
125 4     4   674 my $self = shift;
126              
127 4         173 my $schema_meta = Class::MOP::Class->initialize($self->schema_class);
128 4         510 my @schema_methods = $schema_meta->get_all_method_names;
129              
130             # combine with any already added by other schemas
131 4         10206 my @handles = eval {
132 4         10 @{ $self->meta->find_attribute_by_name('schema')->handles }
  4         19  
133             };
134              
135             # now kill the attribute, otherwise add_attribute in BUILD will not do the right
136             # thing (it clears the handles for some reason.) May be a Moose bug.
137 4         550 eval { $self->meta->remove_attribute('schema') };
  4         19  
138              
139 4         52530 my %schema_methods;
140 4         163 @schema_methods{ @schema_methods, @handles } = ();
141 4         71 @schema_methods = keys %schema_methods;
142              
143 4         27 my @my_methods = $self->meta->get_all_method_names;
144 4         8052 my %my_methods;
145 4         108 @my_methods{@my_methods} = ();
146              
147 4         10 my @delegates;
148 4         19 for my $method (@schema_methods) {
149 370 100       848 push @delegates, $method unless exists $my_methods{$method};
150             }
151              
152 4         125 return \@delegates;
153             }
154              
155             sub _pass_options_to_schema {
156 2     2   8 my ($self, $args) = @_;
157              
158             my @attributes = map {
159 2 100       13 $_->init_arg || ()
  30         426  
160             } $self->meta->get_all_attributes;
161              
162 2         8 my %attributes;
163 2         13 @attributes{@attributes} = ();
164              
165 2         13 for my $opt (keys %$args) {
166 11 100       84 if (not exists $attributes{$opt}) {
167 1 50       6 next unless $self->schema->can($opt);
168 1         21 $self->schema->$opt($args->{$opt});
169             }
170             }
171             }
172              
173             sub _pass_options_to_resultset {
174 2     2   9 my ($self, $source, $args) = @_;
175              
176 2         10 for my $opt (keys %$args) {
177 2         11 my $rs_class = $self->schema->source($source)->resultset_class;
178 2 100       307 next unless $rs_class->can($opt);
179 1         36 $rs_class->$opt($args->{$opt});
180             }
181             }
182              
183             =head1 SEE ALSO
184              
185             L<Catalyst::Model::DBIC::Schema>, L<DBIx::Class::Schema>
186              
187             =head1 AUTHOR
188              
189             See L<Catalyst::Model::DBIC::Schema/AUTHOR> and
190             L<Catalyst::Model::DBIC::Schema/CONTRIBUTORS>.
191              
192             =head1 COPYRIGHT
193              
194             See L<Catalyst::Model::DBIC::Schema/COPYRIGHT>.
195              
196             =head1 LICENSE
197              
198             This program is free software, you can redistribute it and/or modify it
199             under the same terms as Perl itself.
200              
201             =cut
202              
203             1;