File Coverage

blib/lib/Class/Scaffold/Environment.pm
Criterion Covered Total %
statement 87 131 66.4
branch 14 44 31.8
condition 8 36 22.2
subroutine 23 35 65.7
pod 19 19 100.0
total 151 265 56.9


line stmt bran cond sub pod time code
1 2     2   37 use 5.008;
  2         7  
  2         254  
2 2     2   20 use warnings;
  2         5  
  2         64  
3 2     2   10 use strict;
  2         3  
  2         99  
4              
5             package Class::Scaffold::Environment;
6             BEGIN {
7 2     2   36 $Class::Scaffold::Environment::VERSION = '1.102280';
8             }
9              
10             # ABSTRACT: Base class for framework environment classes
11 2     2   1866 use Error::Hierarchy::Util 'load_class';
  2         24341  
  2         153  
12 2     2   1384 use Class::Scaffold::Factory::Type;
  2         7  
  2         15  
13 2     2   1989 use Property::Lookup;
  2         136847  
  2         29  
14 2     2   1996 use Data::Storage; # for AutoPrereq
  2         11296  
  2         23  
15 2     2   87 use parent 'Class::Scaffold::Base';
  2         4  
  2         9  
16             Class::Scaffold::Base->add_autoloaded_package('Class::Scaffold::');
17              
18             # ptags: /(\bconst\b[ \t]+(\w+))/
19             __PACKAGE__->mk_scalar_accessors(qw(test_mode context))
20             ->mk_boolean_accessors(qw(rollback_mode))
21             ->mk_class_hash_accessors(qw(storage_cache multiplex_transaction_omit))
22             ->mk_object_accessors(
23             'Property::Lookup' => {
24             slot => 'configurator',
25             comp_mthds => [
26             qw(
27             get_config
28             core_storage_name
29             core_storage_args
30             memory_storage_name
31             )
32             ]
33             },
34             );
35 2   33     577 use constant DEFAULTS =>
36 2     2   248 (test_mode => (defined $ENV{TEST_MODE} && $ENV{TEST_MODE} == 1),);
  2         5  
37             Class::Scaffold::Factory::Type->register_factory_type(
38             exception_container => 'Class::Scaffold::Exception::Container',
39             result => 'Data::Storage::DBI::Result',
40             storage_statement => 'Data::Storage::Statement',
41             test_util_loader => 'Class::Scaffold::Test::UtilLoader',
42             );
43             { # closure over $env so that it really is private
44             my $env;
45 34     34 1 182 sub getenv { $env }
46              
47             sub setenv {
48 2     2 1 217 my ($self, $newenv, @args) = @_;
49 2 50 33     15 return $env = $newenv
50             if ref $newenv
51             && UNIVERSAL::isa($newenv, 'Class::Scaffold::Environment');
52 2 50       9 unless (ref $newenv) {
53              
54             # it's a string containing the class name
55 2         14 load_class $newenv, 1;
56 2         77 return $env = $newenv->new(@args);
57             }
58             throw Error::Hierarchy::Internal::CustomMessage(
59 0         0 custom_message => "Invalid environment specification [$newenv]",);
60             }
61             } # end of closure
62              
63             sub setup {
64 2     2 1 5 my $self = shift;
65 2         16 $self->configurator->default_layer->hash(
66             $self->every_hash('CONFIGURATOR_DEFAULTS'));
67             }
68              
69             # ----------------------------------------------------------------------
70             # class name-related code
71 2         561 use constant STORAGE_CLASS_NAME_HASH => (
72              
73             # storage names
74             STG_NULL => 'Data::Storage::Null',
75             STG_NULL_DBI => 'Data::Storage::DBI', # for testing
76 2     2   10 );
  2         5  
77              
78             sub make_obj {
79 3     3 1 7 my $self = shift;
80 3         22 Class::Scaffold::Factory::Type->make_object_for_type(@_);
81             }
82              
83             sub get_class_name_for {
84 0     0 1 0 my ($self, $object_type) = @_;
85 0         0 Class::Scaffold::Factory::Type->get_factory_class($object_type);
86             }
87              
88             sub isa_type {
89 0     0 1 0 my ($self, $object, $object_type) = @_;
90 0 0       0 return unless UNIVERSAL::can($object, 'get_my_factory_type');
91 0         0 my $factory_type = $object->get_my_factory_type;
92 0 0       0 defined $factory_type ? $factory_type eq $object_type : 0;
93             }
94              
95             sub gen_class_hash_accessor (@) {
96 2     2 1 6 for my $prefix (@_) {
97 2         12 my $method = sprintf 'get_%s_class_name_for' => lc $prefix;
98 2         7 my $every_hash_name = sprintf '%s_CLASS_NAME_HASH', $prefix;
99 2         4 my $hash; # will be cached here
100 2     2   12 no strict 'refs';
  2         3  
  2         1226  
101 2 50       10 $::PTAGS && $::PTAGS->add_tag($method, __FILE__, __LINE__ + 1);
102             *$method = sub {
103 3 50 33 3   18 local $DB::sub = local *__ANON__ = sprintf "%s::%s", __PACKAGE__,
104             $method
105             if defined &DB::DB && !$Devel::DProf::VERSION;
106 3         10 my ($self, $key) = @_;
107 3   66     23 $hash ||= $self->every_hash($every_hash_name);
108 3 50       1620 $hash->{$key} || $hash->{_AUTO};
109 2         17 };
110              
111             # so FOO_CLASS_NAME() will return the whole every_hash
112 2         10 $method = sprintf '%s_CLASS_NAME' => lc $prefix;
113 2 50       8 $::PTAGS && $::PTAGS->add_tag($method, __FILE__, __LINE__ + 1);
114             *$method = sub {
115 0 0 0 0   0 local $DB::sub = local *__ANON__ = sprintf "%s::%s", __PACKAGE__,
116             $method
117             if defined &DB::DB && !$Devel::DProf::VERSION;
118 0         0 my $self = shift;
119 0   0     0 $hash ||= $self->every_hash($every_hash_name);
120 0 0       0 wantarray ? %$hash : $hash;
121 2         14 };
122 2         9 $method = sprintf 'release_%s_class_name_hash' => lc $prefix;
123 2 50       8 $::PTAGS && $::PTAGS->add_tag($method, __FILE__, __LINE__ + 1);
124             *$method = sub {
125 0 0 0 0   0 local $DB::sub = local *__ANON__ = sprintf "%s::%s", __PACKAGE__,
126             $method
127             if defined &DB::DB && !$Devel::DProf::VERSION;
128 0         0 undef $hash;
129 2         16 };
130             }
131             }
132             gen_class_hash_accessor('STORAGE');
133              
134             sub load_cached_class_for_type {
135 0     0 1 0 my ($self, $object_type_const) = @_;
136              
137             # Cache for efficiency reasons; the environment is the core of the whole
138             # framework.
139 0         0 our %cache;
140 0         0 my $class = $self->get_class_name_for($object_type_const);
141 0 0 0     0 unless (defined($class) && length($class)) {
142 0         0 throw Error::Hierarchy::Internal::CustomMessage(custom_message =>
143             "Can't find class for object type [$object_type_const]",);
144             }
145 0         0 load_class $class, $self->test_mode;
146 0         0 $class;
147             }
148              
149             sub storage_for_type {
150 0     0 1 0 my ($self, $object_type) = @_;
151 0         0 my $storage_type = $self->get_storage_type_for($object_type);
152 0         0 $self->$storage_type;
153             }
154              
155             # When running class tests in non-final distributions, which storage should we
156             # use? Ideally, every distribution (but especially the non-final ones like
157             # Registry-Core and Registry-Enum) should have a mock storage against which to
158             # test. Until then, the following mechanism can be used:
159             #
160             # Every storage notes whether it is abstract or an implementation. Class tests
161             # that need a storage will skip() the tests if the storage is abstract.
162             # Problem: we need to ask all the object types' storages used in a test code
163             # block, as different objects types could use different storages. For example:
164             # skip(...) unless
165             # $self->delegate->all_storages_are_implemented(qw/person command .../);
166             sub all_storages_are_implemented {
167 0     0 1 0 my ($self, @object_types) = @_;
168 0         0 for my $object_type (@object_types) {
169 0 0       0 return 0 if $self->storage_for_type($object_type)->is_abstract;
170             }
171 0         0 1;
172             }
173              
174             # Have a special method for making delegate objects, because delegates will be
175             # cached (i.e., pseudo-singletons) and don't need storages and extra args and
176             # such.
177             sub make_delegate {
178 0     0 1 0 my ($self, $object_type_const, @args) = @_;
179 0         0 our %cache;
180 0   0     0 $cache{delegate}{$object_type_const} ||=
181             $self->make_obj($object_type_const, @args);
182             }
183              
184             # ----------------------------------------------------------------------
185             # storage-related code
186 2     2   12 use constant STORAGE_TYPE_HASH => (_AUTO => 'core_storage',);
  2         5  
  2         1241  
187              
188             sub get_storage_type_for {
189 2     2 1 8 my ($self, $key) = @_;
190 2         5 our %cache;
191 2 50       13 return $cache{get_storage_type_for}{$key}
192             if exists $cache{get_storage_type_for}{$key};
193 2         22 my $storage_type_for = $self->every_hash('STORAGE_TYPE_HASH');
194 2   33     1934 $cache{get_storage_type_for}{$key} = $storage_type_for->{$key}
195             || $storage_type_for->{_AUTO};
196             }
197              
198             sub make_storage_object {
199 2     2 1 634 my $self = shift;
200 2         4 my $storage_name = shift;
201 1         8 my %args =
202             @_ == 1
203             ? defined $_[0]
204             ? ref $_[0] eq 'HASH'
205 2 50       18 ? %{ $_[0] }
    100          
    50          
206             : @_
207             : ()
208             : @_;
209 2 50       11 if (my $class = $self->get_storage_class_name_for($storage_name)) {
210 2         11 load_class $class, $self->test_mode;
211 2         63892 return $class->new(%args);
212             }
213             throw Error::Hierarchy::Internal::CustomMessage(
214 0         0 custom_message => "Invalid storage name [$storage_name]",);
215             }
216              
217             sub core_storage {
218 5     5 1 13 my $self = shift;
219 5   66     30 $self->storage_cache->{core_storage} ||=
220             $self->make_storage_object($self->core_storage_name,
221             $self->core_storage_args);
222             }
223              
224             sub memory_storage {
225 0     0 1 0 my $self = shift;
226 0   0     0 $self->storage_cache->{memory_storage} ||=
227             $self->make_storage_object($self->memory_storage_name);
228             }
229              
230             # Forward some special methods onto all cached storages. Some storages could
231             # be a bit special - we don't want to rollback or disconnect from them when
232             # calling the multiplexing rollback() and disconnect() methods below, so we
233             # ignore them when multiplexing. For example, mutex storages (see
234             # Data-Conveyor for the concept).
235             sub rollback {
236 0     0 1 0 my $self = shift;
237 0         0 while (my ($storage_type, $storage) = each %{ $self->storage_cache }) {
  0         0  
238 0 0       0 next if $self->multiplex_transaction_omit($storage_type);
239 0         0 $storage->rollback;
240             }
241             }
242              
243             sub commit {
244 0     0 1 0 my $self = shift;
245 0         0 while (my ($storage_type, $storage) = each %{ $self->storage_cache }) {
  0         0  
246 0 0       0 next if $self->multiplex_transaction_omit($storage_type);
247 0         0 $storage->commit;
248             }
249             }
250              
251             sub disconnect {
252 2     2 1 6 my $self = shift;
253 2         5 while (my ($storage_type, $storage) = each %{ $self->storage_cache }) {
  4         16  
254 2 50       42 next if $self->multiplex_transaction_omit($storage_type);
255 2         40 $storage->disconnect;
256              
257             # remove it from the cache so we'll reconnect next time
258 2         63 $self->storage_cache_delete($storage_type);
259 2         25 require Class::Scaffold::Storable;
260 2         27 %Class::Scaffold::Storable::cache = ();
261             }
262 2         23 our %cache;
263 2         303 $cache{get_storage_type_for} = {};
264             }
265              
266             # Check configuration values for consistency. Empty, but it exists so
267             # subclasses can call SUPER::check()
268 0     0 1   sub check { }
269             1;
270              
271              
272             __END__