File Coverage

blib/lib/Data/Context/Instance.pm
Criterion Covered Total %
statement 93 98 94.9
branch 24 32 75.0
condition 16 27 59.2
subroutine 22 22 100.0
pod 4 4 100.0
total 159 183 86.8


line stmt bran cond sub pod time code
1             package Data::Context::Instance;
2              
3             # Created on: 2012-04-09 05:58:42
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 6     6   31 use Moose;
  6         10  
  6         52  
10 6     6   46942 use namespace::autoclean;
  6         16  
  6         59  
11 6     6   501 use warnings;
  6         11  
  6         224  
12 6     6   41 use version;
  6         12  
  6         46  
13 6     6   467 use Carp;
  6         11  
  6         537  
14 6     6   40 use Scalar::Util;
  6         11  
  6         276  
15 6     6   37 use List::Util;
  6         8  
  6         384  
16 6     6   36 use List::MoreUtils qw/pairwise/;
  6         24  
  6         95  
17 6     6   2872 use Data::Dumper qw/Dumper/;
  6         14  
  6         378  
18 6     6   31 use English qw/ -no_match_vars /;
  6         10  
  6         56  
19 6     6   7488 use Hash::Merge;
  6         16697  
  6         412  
20 6     6   3342 use Clone qw/clone/;
  6         16212  
  6         511  
21 6     6   2957 use Data::Context::Util qw/lol_path lol_iterate do_require/;
  6         15  
  6         468  
22 6     6   41 use Class::Inspector;
  6         7  
  6         113  
23 6     6   26 use Moose::Util::TypeConstraints qw/duck_type/;
  6         13  
  6         75  
24              
25             our $VERSION = version->new('0.1.8');
26              
27             has path => (
28             is => 'rw',
29             isa => 'Str',
30             required => 1,
31             );
32             has loader => (
33             is => 'rw',
34             isa => 'Data::Context::Loader',
35             required => 1,
36             );
37             has dc => (
38             is => 'rw',
39             isa => 'Data::Context',
40             required => 1,
41             weak_ref => 1,
42             );
43             has raw => (
44             is => 'rw',
45             isa => 'Any',
46             );
47             has actions => (
48             is => 'rw',
49             isa => 'HashRef[HashRef]',
50             default => sub {{}},
51             );
52             has merger => (
53             is => 'rw',
54             isa => duck_type( [qw/merge/] ),
55             builder => '_merger',
56             handles => [qw/merge/],
57             );
58              
59             sub init {
60 28     28 1 52 my ($self) = @_;
61              
62 28 100       95 return $self if !$self->changed;
63              
64 24         873 my $raw = $self->loader->load();
65              
66             # merge in any inherited data
67 24 100       78 if ( $raw->{PARENT} ) {
68 1         26 $self->raw({});
69 1         28 my $parent = $self->dc->get_instance( $raw->{PARENT} )->init;
70 1         29 $raw = $self->merge( $raw, $parent->raw );
71             }
72              
73             # save complete raw data
74 24         1002 $self->raw($raw);
75              
76             # get data actions
77 24         37 my $count = 0;
78 24     227   211 lol_iterate( $raw, sub { $self->process_data(\$count, @_) } );
  227         394  
79              
80 24         113 return $self;
81             }
82              
83             sub changed {
84 28     28 1 42 my ($self) = @_;
85              
86             # considered changed if not data has been read
87 28 100       1090 return 1 if !$self->raw;
88              
89             # considered changed if this file has changed
90 4 50       171 return 1 if $self->loader->changed;
91              
92 4 50       186 if ( $self->raw->{PARENT} ) {
93 0         0 my $parent = $self->dc->get_instance( $self->raw->{PARENT} );
94              
95             # considered changed if the parent instance has changed
96 0         0 return $parent->changed;
97             }
98              
99             # when all else fails the data is considered unchanged
100 4         19 return 0;
101             }
102              
103             sub get_data {
104 24     24 1 69 my ( $self, $vars ) = @_;
105 24         99 $self->init;
106              
107 24         874 my $data = clone $self->raw;
108 24         57 my @events;
109              
110             # process the data in order
111 24         1049 for my $path ( _sort_optional( $self->actions ) ) {
112 45         208 my ($value, $replacer) = lol_path( $data, $path );
113 45         1885 my $module = $self->actions->{$path}{module};
114 45         1523 my $method = $self->actions->{$path}{method};
115 45         323 my $new = $module->$method( $value, $vars, $path, $self );
116              
117 45 100 66     110603 if ( blessed($new) && $new->isa('AnyEvent::CondVar') ) {
118 24         109 push @events, [ $replacer, $new ];
119             }
120             else {
121 21         56 $replacer->($new);
122             }
123             }
124              
125 24         158 for my $event ( @events ) {
126 24         265 $event->[0]->($event->[1]->recv);
127             }
128              
129 24         262 return $data;
130             }
131              
132             sub process_data {
133 227     227 1 312 my ( $self, $count, $data, $path ) = @_;
134              
135 227 100 100     766 if ( !ref $data ) {
    100 66        
136 147 100 100     615 if ( defined $data && $data =~ /^\# (.*) \#$/xms ) {
137 9         21 my $data_path = $1;
138 9         287 do_require( $self->dc->action_class );
139 9         339 $self->actions->{$path} = {
140             module => $self->dc->action_class,
141             method => 'expand_vars',
142             found => $$count++,
143             path => $data_path,
144             };
145             }
146             }
147             elsif ( ref $data eq 'HASH' && ( $data->{MODULE} || $data->{METHOD} ) ) {
148 33   66     1083 $self->actions->{$path} = {
      66        
149             module => $data->{MODULE} || $self->dc->action_class,
150             method => $data->{METHOD} || $self->dc->action_method,
151             order => $data->{ORDER},
152             found => $$count++,
153             };
154 33         880 do_require( $self->actions->{$path}{module} );
155             }
156              
157 227         2379 return;
158             }
159              
160             sub _sort_optional {
161 25     25   136 my ($hash) = @_;
162              
163 53 100 66     312 my @sorted = sort {
164 25         151 return $hash->{$a}->{found} <=> $hash->{$b}->{found} if ! defined $hash->{$a}->{order} && ! defined $hash->{$b}->{order};
165 4 100       13 return $hash->{$b}->{order} >= 0 ? 1 : -1 if !defined $hash->{$a}->{order};
    100          
166 1 50       8 return $hash->{$a}->{order} >= 0 ? -1 : 1 if !defined $hash->{$b}->{order};
    50          
167 0 0 0     0 return -1 if $hash->{$a}->{order} >= 0 && $hash->{$b}->{order} < 0;
168 0 0 0     0 return 1 if $hash->{$a}->{order} < 0 && $hash->{$b}->{order} >= 0;
169 0         0 return $hash->{$a}->{order} <=> $hash->{$b}->{order};
170             } keys %$hash;
171              
172 25         99 return @sorted;
173             }
174              
175             sub _merger {
176 24     24   211 return Hash::Merge->new('LEFT_PRECEDENT');
177             }
178              
179             __PACKAGE__->meta->make_immutable;
180              
181             1;
182              
183             __END__
184              
185             =head1 NAME
186              
187             Data::Context::Instance - The in memory instance of a data context config file
188              
189             =head1 VERSION
190              
191             This documentation refers to Data::Context::Instance version 0.1.8.
192              
193             =head1 SYNOPSIS
194              
195             use Data::Context::Instance;
196              
197             # create a new object
198             my $dci = Data::Context::Instance->new(
199             path => 'dir/file',
200             file => Path::Class::file('path/to/dir/file.dc.js'),
201             type => 'js',
202             dc => $dc,
203             );
204              
205             # Initialise the object (done by get normally)
206             $dci->init;
207              
208             # get the data (with the context of $vars)
209             my $data = $dci->get_data($vars);
210              
211             =head1 DESCRIPTION
212              
213             =head1 SUBROUTINES/METHODS
214              
215             =head2 C<init()>
216              
217             Initialises the instance ie it reads the config file and merges in the parent if found
218              
219             =head2 C<changed ()>
220              
221             Returns true if any of the files that go into this instance have changed (or
222             if they haven't yet been processed) and returns false if this instance is still
223             valid.
224              
225             =head2 C<get_data ( $vars )>
226              
227             Returns the data from the config file processed with the context of $vars
228              
229             =head2 C<process_data( $count, $data, $path )>
230              
231             This does the magic of processing the data, and in the future handling of the
232             data event loop.
233              
234             =head1 DIAGNOSTICS
235              
236             =head1 CONFIGURATION AND ENVIRONMENT
237              
238             =head1 DEPENDENCIES
239              
240             =head1 INCOMPATIBILITIES
241              
242             =head1 BUGS AND LIMITATIONS
243              
244             There are no known bugs in this module.
245              
246             Please report problems to Ivan Wills (ivan.wills@gmail.com).
247              
248             Patches are welcome.
249              
250             =head1 AUTHOR
251              
252             Ivan Wills - (ivan.wills@gmail.com)
253              
254             =head1 LICENSE AND COPYRIGHT
255              
256             Copyright (c) 2012 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
257             All rights reserved.
258              
259             This module is free software; you can redistribute it and/or modify it under
260             the same terms as Perl itself. See L<perlartistic>. This program is
261             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
262             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
263             PARTICULAR PURPOSE.
264              
265             =cut