File Coverage

blib/lib/Data/ResourceSet.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # $Id: /mirror/coderepos/lang/perl/Data-ResourceSet/trunk/lib/Data/ResourceSet.pm 54071 2008-05-19T06:52:45.149433Z daisuke $
2              
3             package Data::ResourceSet;
4 2     2   1047 use Moose;
  0            
  0            
5              
6             has 'resources' => (
7             is => 'rw',
8             isa => 'HashRef',
9             required => 1,
10             default => sub { +{} }
11             );
12              
13             has 'resources_config' => (
14             is => 'rw',
15             isa => 'HashRef',
16             default => sub { +{} },
17             );
18              
19             our $VERSION = '0.00003';
20              
21             sub resource
22             {
23             my ($self, $type, $name, @args) = @_;
24              
25             my $resource = $self->resources->{$type}->{$name};
26             if (! $resource) {
27             my $config = $self->find_config($type, $name);
28             if ($config) {
29             $resource = $self->construct_resource($name, $config, @args);
30             if ($resource) {
31             $self->resources->{$type}->{$name} = $resource;
32             }
33             }
34             }
35              
36             if ($resource && $resource->can('ACCEPT_CONTEXT')) {
37             return $resource->ACCEPT_CONTEXT($self, @args);
38             }
39              
40             return $resource;
41             }
42              
43             sub find_config
44             {
45             my ($self, $type, $name) = @_;
46              
47             my $config;
48             # find a per-instance config
49             $config = $self->resources_config->{$type}->{$name};
50             if ($config) {
51             return $config;
52             }
53              
54             # find a per-package config
55             $config = $self->can('config') ?
56             $self->config('resources')->{$type}->{$name} : ();
57             if ($config) {
58             return $config;
59             }
60              
61             return ();
62             }
63              
64             sub construct_resource
65             {
66             my ($self, $name, $config, @args) = @_;
67              
68             my $pkg = $config->{module};
69             if ($pkg !~ s/^\+//) {
70             $pkg = join('::', blessed $self, $pkg);
71             }
72             if (! Class::MOP::is_class_loaded($pkg)) {
73             eval "require $pkg";
74             die if $@;
75             }
76              
77             my $constructor = $config->{constructor} || 'new';
78             my $args = $config->{args} || {};
79             my $deref = $config->{deref};
80             my $ref = ref $args;
81             $pkg->$constructor( ($deref && $ref) ?
82             ($ref eq 'ARRAY' ? @$args :
83             $ref eq 'HASH' ? %$args : $args) :
84             $args
85             );
86             }
87              
88             1;
89              
90             __END__
91              
92             =head1 NAME
93              
94             Data::ResourceSet - A Bundle Of Resources
95              
96             =head1 SYNOPSIS
97              
98             my $cluster = Data::ResourceSet->new(
99             resources => {
100             schema => {
101             name1 => $dbic_schema1
102             name2 => $dbic_schema2
103             },
104             s3bucket => {
105             name1 => $s3bucket1,
106             name2 => $s3bucket2,
107             }
108             }
109             );
110              
111             my $photo_meta =
112             $cluster->resource('schema', 'Name')->resultset('Photo')->find($photo_id);
113             my $photo_file =
114             $cluster->resource('s3bucket', 'Name')->get_key($key, $filename);
115              
116             =head1 DESCRIPTION
117              
118             Data::ResourceSet is a bag of "stuff", where you can refer to the "stuff"
119             by name, and the "stuff" will be initialized for you.
120              
121             For example, say you have multiple DBIx::Class::Schema objects in your
122             app. You would like to make the reference to each resource as abstract
123             as possible so you don't hard code anything. Then you can create an
124             instance of Data::ResourceSet and refer to these schemas by name.
125              
126             Here are two ways to do it. First is to simply create a resource set from
127             already instantiated schemas:
128              
129             my $schema1 = MyCluster1->connect($dsn, $user, $pass);
130             my $schema2 = MyCluster2->connect($dsn, $user, $pass);
131             my $resources = Data::ResourceSet->new({
132             resources => {
133             schema => {
134             cluster1 => $schema1,
135             cluster2 => $schema2,
136             }
137             }
138             });
139              
140             $resources->resource('schema', 'cluster1')->resultset('FooBar')->search(...)
141              
142             The other way to do it is by giving a similar hash, but give only the config
143              
144             my $resources = Data::ResourceSet->new({
145             resources_config => {
146             schema => {
147             cluster1 => {
148             module => '+DBIx::Class::Schema',
149             consturctor => 'connect',
150             args => [ $dsn, $user, $pass ],
151             },
152             cluster2 => {
153             module => '+DBIx::Class::Schema',
154             consturctor => 'connect',
155             args => [ $dsn, $user, $pass ],
156             }
157             }
158             }
159             });
160             $resources->resource('schema', 'cluster1')->resultset('FooBar')->search(...)
161              
162             The difference between the first and the second example above is that
163             the latter does a lazy initialization. So if you don't want to connect
164             until you actually use the connection, then the second way is the way to go.
165              
166             You can also specify this config on a per-package level, say, when you subclass
167             Data::ResourceSet:
168              
169             package MyApp::ResourceSet;
170             use base qw(Data::ResourceSet);
171              
172             __PACKAGE__->config(
173             resources => {
174             schema => {
175             cluster1 => {
176             module => '+DBIx::Class::Schema',
177             consturctor => 'connect',
178             args => [ $dsn, $user, $pass ],
179             },
180             cluster2 => {
181             module => '+DBIx::Class::Schema',
182             consturctor => 'connect',
183             args => [ $dsn, $user, $pass ],
184             }
185             }
186             }
187             });
188              
189             my $resources = MyApp::ResourceSet->new;
190             $resources->resource('schema', 'cluster1')->resultset('FooBar')->search(...)
191              
192             You can also use Data::ResourceSet::Adaptor, which can be a proxy between
193             Data::ResourceSet and your actual resource.
194              
195             package MyProxy;
196             use base qw(Data::ResourceSet::Adaptor);
197              
198             sub ACCEPT_CONTEXT
199             {
200             my($self, $c, @args) = @_;
201             ...
202             return $whatever;
203             }
204              
205             my $resource = Data::ResourceSet->new({
206             resource_config => {
207             foo => {
208             bar => {
209             module => '+MyProxy',
210             args => \%whatever
211             }
212             }
213             }
214             });
215              
216             =head1 APPLICATION
217              
218             Data::ResourceSet is inspired by Catalyst and its method of being a glue
219             mediator between components. You can use it in applications where you have
220             multiple components, and you don't want to refer to a hardcoded resource.
221              
222             Also, it's quite handy when you want to partition your storage on large
223             applications. In such cases, you should create multiple Data::ResourceSet
224             objects with the same keys:
225              
226             my $schema1 = DBIx::Class::Schema->connect(...);
227             my $schema2 = DBIx::Class::Schema->connect(...);
228              
229             my $cluster1 = Data::ResourceSet->new({
230             resources => {
231             schema => {
232             name => $schema1,
233             }
234             }
235             });
236            
237             my $cluster2 = Data::ResourceSet->new({
238             resources => {
239             schema => {
240             name => $schema2,
241             }
242             }
243             });
244              
245             # For whichever cluster above...
246             $cluster->resource('schema', 'name')->resultset(...)
247            
248             =head1 METHODS
249              
250             =head2 new(\%args)
251              
252             =over 4
253              
254             =item resources => \%data
255              
256             =item resources_config => \%config
257              
258             =back
259              
260             =head2 resource($type, $name)
261              
262             Gets a resource by its type and name
263              
264             =head2 construct_resource
265              
266             Constructs a resource by its config
267              
268             =head2 find_config
269              
270             Find the configuration for a resource
271              
272             =head1 AUTHOR
273              
274             Copyright (c) 2008 Daisuke Maki C<< daisuke@endeworks.jp >>
275              
276             =head1 LICENSE
277              
278             This program is free software; you can redistribute it and/or modify it
279             under the same terms as Perl itself (Artistic License v1.0/v2.0).
280              
281             See http://www.perl.com/perl/misc/Artistic.html
282              
283             =cut
284