File Coverage

blib/lib/Data/Context/Instance.pm
Criterion Covered Total %
statement 75 98 76.5
branch 18 32 56.2
condition 12 27 44.4
subroutine 21 22 95.4
pod 4 4 100.0
total 130 183 71.0


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 2     2   9 use Moose;
  2         2  
  2         16  
10 2     2   12737 use namespace::autoclean;
  2         5  
  2         17  
11 2     2   158 use warnings;
  2         4  
  2         89  
12 2     2   12 use version;
  2         2  
  2         14  
13 2     2   141 use Carp;
  2         10  
  2         146  
14 2     2   10 use Scalar::Util;
  2         3  
  2         78  
15 2     2   8 use List::Util;
  2         2  
  2         101  
16 2     2   8 use List::MoreUtils qw/pairwise/;
  2         3  
  2         26  
17 2     2   773 use Data::Dumper qw/Dumper/;
  2         4  
  2         118  
18 2     2   10 use English qw/ -no_match_vars /;
  2         2  
  2         14  
19 2     2   2703 use Hash::Merge;
  2         5927  
  2         121  
20 2     2   1171 use Clone qw/clone/;
  2         4400  
  2         165  
21 2     2   794 use Data::Context::Util qw/lol_path lol_iterate do_require/;
  2         5  
  2         168  
22 2     2   14 use Class::Inspector;
  2         2  
  2         45  
23 2     2   8 use Moose::Util::TypeConstraints qw/duck_type/;
  2         5  
  2         27  
24              
25             our $VERSION = version->new('0.1.10');
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 4     4 1 8 my ($self) = @_;
61              
62 4 50       14 return $self if !$self->changed;
63              
64 4         155 my $raw = $self->loader->load();
65              
66             # merge in any inherited data
67 4 100       14 if ( $raw->{PARENT} ) {
68 1         29 $self->raw({});
69 1         26 my $parent = $self->dc->get_instance( $raw->{PARENT} )->init;
70 1         47 $raw = $self->merge( $raw, $parent->raw );
71             }
72              
73             # save complete raw data
74 4         291 $self->raw($raw);
75              
76             # get data actions
77 4         8 my $count = 0;
78 4     59   33 lol_iterate( $raw, sub { $self->process_data(\$count, @_) } );
  59         112  
79              
80 4         26 return $self;
81             }
82              
83             sub changed {
84 4     4 1 10 my ($self) = @_;
85              
86             # considered changed if not data has been read
87 4 50       137 return 1 if !$self->raw;
88              
89             # considered changed if this file has changed
90 0 0       0 return 1 if $self->loader->changed;
91              
92 0 0       0 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 0         0 return 0;
101             }
102              
103             sub get_data {
104 0     0 1 0 my ( $self, $vars ) = @_;
105 0         0 $self->init;
106              
107 0         0 my $data = clone $self->raw;
108 0         0 my @events;
109              
110             # process the data in order
111 0         0 for my $path ( _sort_optional( $self->actions ) ) {
112 0         0 my ($value, $replacer) = lol_path( $data, $path );
113 0         0 my $module = $self->actions->{$path}{module};
114 0         0 my $method = $self->actions->{$path}{method};
115 0         0 my $new = $module->$method( $value, $vars, $path, $self );
116              
117 0 0 0     0 if ( blessed($new) && $new->isa('AnyEvent::CondVar') ) {
118 0         0 push @events, [ $replacer, $new ];
119             }
120             else {
121 0         0 $replacer->($new);
122             }
123             }
124              
125 0         0 for my $event ( @events ) {
126 0         0 $event->[0]->($event->[1]->recv);
127             }
128              
129 0         0 return $data;
130             }
131              
132             sub process_data {
133 59     59 1 69 my ( $self, $count, $data, $path ) = @_;
134              
135 59 100 66     274 if ( !ref $data ) {
    100 66        
136 39 100 66     166 if ( defined $data && $data =~ /^\# (.*) \#$/xms ) {
137 3         7 my $data_path = $1;
138 3         83 do_require( $self->dc->action_class );
139 3         72 $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             $self->actions->{$path} = {
149             module => $data->{MODULE} || $self->dc->action_class,
150             method => $data->{METHOD} || $self->dc->action_method,
151             order => $data->{ORDER},
152 9   66     336 found => $$count++,
      66        
153             };
154 9         305 do_require( $self->actions->{$path}{module} );
155             }
156              
157 59         548 return;
158             }
159              
160             sub _sort_optional {
161 1     1   94 my ($hash) = @_;
162              
163             my @sorted = sort {
164 1 100 66     5 return $hash->{$a}->{found} <=> $hash->{$b}->{found} if ! defined $hash->{$a}->{order} && ! defined $hash->{$b}->{order};
  5         17  
165 4 50       10 return $hash->{$b}->{order} >= 0 ? 1 : -1 if !defined $hash->{$a}->{order};
    100          
166 3 100       10 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 1         4 return @sorted;
173             }
174              
175             sub _merger {
176 4     4   37 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.10.
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::Tiny::path('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