File Coverage

blib/lib/Data/Context.pm
Criterion Covered Total %
statement 62 62 100.0
branch 14 14 100.0
condition 6 9 66.6
subroutine 17 17 100.0
pod 2 2 100.0
total 101 104 97.1


line stmt bran cond sub pod time code
1             package Data::Context;
2              
3             # Created on: 2012-03-18 16:54:56
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 6     6   579904 use Moose;
  6         2857986  
  6         50  
10 6     6   45411 use namespace::autoclean;
  6         9648  
  6         31  
11 6     6   410 use warnings;
  6         22  
  6         182  
12 6     6   3871 use version;
  6         10496  
  6         37  
13 6     6   463 use Carp;
  6         8  
  6         453  
14 6     6   29 use Scalar::Util;
  6         10  
  6         218  
15 6     6   24 use List::Util;
  6         11  
  6         277  
16 6     6   2880 use Data::Dumper qw/Dumper/;
  6         28520  
  6         519  
17 6     6   3591 use English qw/ -no_match_vars /;
  6         11349  
  6         39  
18 6     6   3081 use Path::Class;
  6         1350857  
  6         501  
19              
20 6     6   3428 use Data::Context::Instance;
  6         21  
  6         447  
21 6     6   4248 use Data::Context::Finder::File;
  6         20  
  6         4851  
22              
23             our $VERSION = version->new('0.1.8');
24              
25             has fallback => (
26             is => 'rw',
27             isa => 'Bool',
28             default => 0,
29             );
30             has fallback_depth => (
31             is => 'rw',
32             isa => 'Int',
33             default => 0,
34             );
35             has actions => (
36             is => 'rw',
37             isa => 'HashRef[CodeRef]',
38             );
39             has action_class => (
40             is => 'rw',
41             isa => 'Str',
42             default => 'Data::Context::Actions',
43             );
44             has action_method => (
45             is => 'rw',
46             isa => 'Str',
47             default => 'get_data',
48             );
49             has log => (
50             is => 'rw',
51             isa => 'Object',
52             builder => '_log',
53             lazy_build => 1,
54             );
55             has debug => (
56             is => 'rw',
57             isa => 'Int',
58             builder => '_debug',
59             trigger => \&_debug_set,
60             lazy_build => 1,
61             );
62             has instance_class => (
63             is => 'rw',
64             isa => 'Str',
65             default => 'Data::Context::Instance',
66             );
67             has instance_cache => (
68             is => 'rw',
69             isa => 'HashRef[Data::Context::Instance]',
70             default => sub {{}},
71             init_arg => undef,
72             );
73             has finder => (
74             is => 'rw',
75             isa => ' Data::Context::Finder',
76             required => 1,
77             );
78              
79             around BUILDARGS => sub {
80             my ($orig, $class, @args) = @_;
81             my $args
82             = !@args ? {}
83             : @args == 1 ? $args[0]
84             : {@args};
85              
86             if ( !$args->{finder} ) {
87             $args->{finder} = Data::Context::Finder::File->new(
88             map { $_ => $args->{$_} }
89             grep { $_ eq 'path' || /^file_/xms }
90             keys %{ $args }
91             );
92             }
93              
94             return $class->$orig($args);
95             };
96              
97             sub get {
98 28     28 1 25311 my ( $self, $path, $vars ) = @_;
99              
100 28         109 my $dci = $self->get_instance($path);
101              
102 24         133 return $dci->get_data($vars);
103             }
104              
105             sub get_instance {
106 29     29 1 54 my ( $self, $path ) = @_;
107              
108             # TODO add some cache controls here or in ::Instance::init();
109 29 100       1260 return $self->instance_cache->{$path} if $self->instance_cache->{$path};
110              
111 25         166 my @path = split m{/+}xms, $path;
112 25 100 100     189 shift @path if !defined $path[0] || $path[0] eq '';
113 25 100       108 push @path, 'index' if $path =~ m{/$}xms;
114              
115 25         45 my $count = 1;
116 25         33 my $loader;
117              
118             # find the most appropriate file
119             PATH:
120 25         66 while ( @path ) {
121 41         1457 $loader = $self->finder->find(@path);
122              
123 41 100       35745 last if $loader;
124 20 100 33     817 last if !$self->fallback || ( $self->fallback_depth && $count++ >= $self->fallback_depth );
      66        
125              
126 16         51 pop @path;
127             }
128              
129 25 100       221 confess "Could not find a data context config file for '$path'\n" if ! $loader;
130              
131 21         853 my $instance_class = $self->instance_class;
132 21         836 return $self->instance_cache->{$path} = $instance_class->new(
133             path => $path,
134             loader => $loader,
135             dc => $self,
136             );
137             }
138              
139             sub _log {
140 1     1   2 my $self = shift;
141 1         747 require Data::Context::Log;
142 1         38 return Data::Context::Log->new( level => $self->debug );
143             }
144 1     1   23 sub _debug { return 3 }
145             sub _debug_set {
146 2     2   5 my ($self, $new_debug ) = @_;
147 2 100       69 if ( ref $self->log eq 'Data::Context::Log' ) {
148 1         28 $self->log->level( $new_debug );
149             }
150 2         58 return $new_debug;
151             }
152              
153             __PACKAGE__->meta->make_immutable;
154              
155             1;
156              
157             __END__
158              
159             =head1 NAME
160              
161             Data::Context - Configuration data with context
162              
163             =head1 VERSION
164              
165             This documentation refers to Data::Context version 0.1.8.
166              
167             =head1 SYNOPSIS
168              
169             use Data::Context;
170              
171             # create a new Data::Context variable
172             my $dc = Data::Context->new(
173             path => [qw{ /path/to/configs /alt/path }],
174             );
175              
176             # read a config
177             my $data = $dc->get(
178             'some/config',
179             {
180             context => 'values',
181             }
182             );
183              
184             =head1 DESCRIPTION
185              
186             /path/to/file.dc.js:
187             {
188             "PARENT" : "/path/to/default.dc.js:,
189             "replace_me" : "#replaced.from.input.variables.0#"
190             "structure" : {
191             "MODULE": "My::Module",
192             "METHOD": "do_stuff",
193             ....
194             },
195             ...
196             }
197              
198             Get object
199             Build -> parse file
200             -> if "PARENT" build parent
201             -> merge self and raw parent
202             -> construct instance
203             -> iterate to all values
204             -> if the value is a string of the form "#...#" make sub reference to add to call list
205             -> if the value is a HASHREF & "MODULE" or "METHOD" keys exist add to call list
206             -> cache result
207              
208             Use object
209             -> clone raw data
210             -> call each method call list
211             -> if return is a CODEREF assume it's an event handler
212             -> else replace data with returned value
213             -> if any event handlers are returned run event loop
214             -> return data
215              
216             MODULE HASHES
217             {
218             "MODULE" : "My::Module",
219             "METHOD" : "get_data",
220             "NEW" : "new",
221             ...
222             }
223             or
224             {
225             "METHOD" : "do_something",
226             "ORDER" : 1,
227             ....
228             }
229              
230             1st : calls My::Module->new->get_data (if NEW wasn't present would just call My::Module->get_data)
231             2nd : calls Data::Context::Actions->do_something
232              
233             the parameters passed in both cases are
234             $value = the hashref containing the method call
235             $dc = The whole data context raw data
236             $path = A path of how to get to this data
237             $vars = The variables that the get was called with
238              
239             Data::Context Configuration
240             path string or list of strings containing directory names to be searched config files
241             fallback bool if true if a config isn't found the parent config will be searched for etc
242             fallback_depth
243             If set to a non zero value the fall back will limited to this number of times
244             actions hashref of coderefs, allows simple adding of extra methods to Data::Context::Actions
245             action_class
246             Allows the using of an action class other than Data::Context::Actions. Although it is suggested that the alt class should inherit from Data::Context::Actions
247             file_suffixes HASHREF
248             json => '.dc.json' : JSON
249             js => '.dc.js' : JSON->relaxed
250             yaml => '.dc.yml' : YAML or YAML::XS
251             xml => '.dc.xml' : XML::Simple
252             log logging object, creates own object that just writes to STDERR if not specified
253             debug set the debugging level default is WARN (DEBUG, INFO, WARN, ERROR or FATAL)
254             cache ...
255              
256             =head1 SUBROUTINES/METHODS
257              
258             =head2 C<new (...)>
259              
260             Parameters to new:
261              
262             =over 4
263              
264             =item path
265              
266             The directory path (or a list of paths) where the configuration files can be found
267              
268             =item fallback
269              
270             A bool if set to true will allow the falling back along the path of the
271             config specified.
272              
273             eg config path = my/config/file
274              
275             if fallback is false the default search performed (for each directory in $dc->path) is
276              
277             my/config/file.dc.js
278             my/config/file.dc.json
279             my/config/file.dc.yml
280             my/config/file.dc.xml
281             my/config/_default.dc.js
282             my/config/_default.dc.json
283             my/config/_default.dc.yml
284             my/config/_default.dc.xml
285              
286             if fallback is true the search is (just for the .dc.js)
287              
288             my/config/file.dc.js
289             my/config/_default.dc.js
290             my/config.dc.js
291             my/_default.dc.js
292             my.dc.js
293             _default.dc.js
294              
295             =item fallback_depth
296              
297             f fallback is true this if set to a non zero +ve int will limit the number
298             of time a fallback will occur. eg from the above example
299              
300             fallback_depth = 0 or 2
301              
302             my/config/file.dc.js
303             my/config/_default.dc.js
304             my/config.dc.js
305             my/_default.dc.js
306             my.dc.js
307             _default.dc.js
308              
309             fallback_depth = 1
310              
311             my/config/file.dc.js
312             my/config/_default.dc.js
313             my/config.dc.js
314             my/_default.dc.js
315              
316             =item actions
317              
318             A hash ref of code refs to allow the simple adding of default actions. The
319             key can be used in templates METHOD parameter and the code will be called
320             when found.
321              
322             =item action_class
323              
324             If you want to use your own default class for actions (ie you don't want
325             to specify C<actions> and don't want to have to always specify MODULE).
326             Your class should inherit from L<Data::Context::Action> to be safe.
327              
328             =item action_method
329              
330             The default action_method is get_data, through this parameter you may choose
331             a different method name.
332              
333             =item file_suffixes
334              
335             This allows the setting of what file suffixes will be used for loading the
336             various config types. Default:
337              
338             {
339             js => '.dc.js',
340             json => '.dc.json',
341             yaml => '.dc.yml',
342             xml => '.dc.xml',
343             }
344              
345             =item file_suffix_order
346              
347             Specify the order to search for various file types. If you will only use
348             one config type you can specify just that type to speed up the searching.
349             Default: [ js, json, yaml, xml ]
350              
351             =item file_default
352              
353             Sets the name of the default config file name (_default by default). If you
354             unset this value, falling back to a default will be disabled
355              
356             =item log
357              
358             A log object should be compatible with a L<Catalyst::Log>, L<Log::Log4perl>,
359             etc logger object. The default value just writes to STDERR.
360              
361             =item debug
362              
363             When using the default logger for C<log>. This sets the level of logging.
364             1 = most information, 5 = almost none, default is 3 warnings and higher
365             messages
366              
367             =back
368              
369             =head2 C<get ($path, $vars)>
370              
371             Reads the config represented by C<$path> and apply the context variable
372             C<$vars> as dictated by the found config.
373              
374             =head2 C<get_instance ($path)>
375              
376             Creates (or retrieves from cache) an instance of the config C<$paht>.
377              
378             =head1 DIAGNOSTICS
379              
380             By default C<Data::Context> writes messages to STDERR (via it's simple log
381             object). More detailed messages can be had by upping the debug level (by
382             lowering the value of debug, 1 out puts all messages, 2 - info and above,
383             3 - warnings and above, 4 - errors and above, 5 - fatal errors)
384              
385             =head1 CONFIGURATION AND ENVIRONMENT
386              
387             =head1 DEPENDENCIES
388              
389             L<Moose>, L<Moose::Util::TypeConstraints>
390              
391             =head1 INCOMPATIBILITIES
392              
393             =head1 BUGS AND LIMITATIONS
394              
395             There are no known bugs in this module.
396              
397             Please report problems to Ivan Wills (ivan.wills@gmail.com).
398              
399             Patches are welcome.
400              
401             =head1 AUTHOR
402              
403             Ivan Wills - (ivan.wills@gmail.com)
404              
405             =head1 LICENSE AND COPYRIGHT
406              
407             Copyright (c) 2012 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
408             All rights reserved.
409              
410             This module is free software; you can redistribute it and/or modify it under
411             the same terms as Perl itself. See L<perlartistic>. This program is
412             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
413             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
414             PARTICULAR PURPOSE.
415              
416             =cut