File Coverage

lib/Su/Model.pm
Criterion Covered Total %
statement 120 128 93.7
branch 48 60 80.0
condition 5 9 55.5
subroutine 16 16 100.0
pod 4 4 100.0
total 193 217 88.9


line stmt bran cond sub pod time code
1             package Su::Model;
2              
3 15     15   34616 use strict;
  15         30  
  15         580  
4 15     15   90 use warnings;
  15         28  
  15         485  
5 15     15   74 use Exporter;
  15         29  
  15         683  
6 15     15   87 use File::Path;
  15         30  
  15         892  
7 15     15   1696 use Data::Dumper;
  15         13186  
  15         759  
8 15     15   89 use Test::More;
  15         26  
  15         143  
9 15     15   12741 use Carp;
  15         27  
  15         984  
10 15     15   24550 use Storable qw(dclone);
  15         71994  
  15         1179  
11              
12 15     15   534 use Su::Template;
  15         1327  
  15         125  
13 15     15   89 use Su::Log;
  15         25  
  15         26400  
14              
15             our @ISA = qw(Exporter);
16              
17             our @EXPORT = qw(generate_model load_model );
18              
19             our $MODEL_DIR = "Models";
20              
21             our $MODEL_BASE_DIR = "./";
22              
23             our $MODEL_CACHE_HREF = {};
24              
25             =pod
26              
27             =head1 NAME
28              
29             Su::Model - A module to treat user data.
30              
31             =head1 SYNOPSYS
32              
33             Su::Model::load_model('Pkg::SomeModel', {share => 1} )->{field_A} = $value;
34              
35             my $value = Su::Model::load_model('Pkg::SomeModel')->{field_A};
36              
37             =head1 DESCRIPTION
38              
39             Su::Model holds the data used in your application.
40             For convenience, Su provides method to generate Model class.
41              
42             use Su::Model;
43             generate_model('NewModel');
44              
45             =head1 ATTRIBUTES
46              
47             =cut
48              
49             our $_attr = {};
50              
51             =head1 FUNCTIONS
52              
53             =over
54              
55             =cut
56              
57             sub import {
58 19     19   1438 my $self = shift;
59 19         52 my %tmp_h = @_;
60 19         40 my $imports_aref = $tmp_h{import};
61 19         40 delete $tmp_h{import};
62 19         32 my $base = $tmp_h{base};
63 19         2991 my $dir = $tmp_h{dir};
64              
65             # print "base:" . Dumper($base) . "\n";
66             # print "dir:" . Dumper($dir) . "\n";
67              
68 19 50       79 $MODEL_BASE_DIR = $base if $base;
69 19 50       57 $MODEL_DIR = $dir if $dir;
70              
71 19 50 33     536 if ( $base || $dir ) {
72 0         0 $self->export_to_level( 1, $self, @{$imports_aref} );
  0         0  
73             } else {
74              
75             # If '' or '' is not passed, then all of the parameters are required method names.
76 19         17544 $self->export_to_level( 1, $self, @_ );
77             }
78              
79             } ## end sub import
80              
81             =item attr()
82              
83             Save the passed data in application scope.
84              
85             Su::Model->attr( 'key1', 'value1' );
86             my $value = Su::Model->attr('key1');
87              
88             Su::Model->attr->{key4} = 'value4';
89             my $value = Su::Model->attr->{key4};
90              
91             =cut
92              
93             sub attr {
94 10 50 33 10 1 2581 my $self = shift if ( ref $_[0] eq __PACKAGE__ or $_[0] eq __PACKAGE__ );
95 10         16 my $key = shift;
96 10         12 my $value = shift;
97 10 100       32 if ( !defined $key ) {
    100          
98              
99             # If no argment is passed, return hash ref itself.
100 2         9 return $_attr;
101             } elsif ( defined $value ) {
102 4         17 $_attr->{$key} = $value;
103             } else {
104 4         26 return $_attr->{$key};
105             }
106             } ## end sub attr
107              
108             =item new()
109              
110             A Constructor.
111              
112             =cut
113              
114             sub new {
115 42     42 1 251079 my $self = shift;
116 42         142 my %h = @_;
117 42         231 my $log = Su::Log->new;
118 42         106 $h{logger} = $log;
119 42         100 $h{models} = {};
120 42         178 return bless \%h, $self;
121             } ## end sub new
122              
123             =item generate_model()
124              
125             generate_model('SomeModel', qw(field1 string field2 number field3 date));
126             $mdl->generate_model('Nest/Mdl2');
127             $mdl->generate_model('Nest::Mdl3');
128             $mdl->generate_model('Nest::Mdl4',"field1",{"key1"=>"value1","key2"=>"value2"},"field2","value3");
129              
130             generate_model(NAME, &rest @(FIELD, VALUE));
131              
132             Generate the model class using the passed model name.
133             If the optional parameters are passed, then generate the model class
134             using the passed parameter as the value of the model field of the
135             Model.
136             VALUE can be specified as scalar or hash reference.
137              
138             The model field of the generated Model is like the following.
139              
140             my $model=
141             {
142             field1 => "value1",
143             field2 => {somekey => "hashvalue"},
144             };
145              
146              
147             You can generate Model class from command line using the following command.
148              
149             perl -I../lib -MSu::Model -e '{generate_model("ModelClass",field1,"value1",field2,"value2")}'
150             perl -I../lib -MSu::Model -e '{generate_model("Pkg::ModelClass",field1,"value1",field2,"value2")}'
151              
152             If you want to specify the directory to generate the Model class, then
153             pass the C parameter like the following sample.
154              
155             perl -MSu::Model=base,lib -e '{generate_model("ModelClass",field1,value1,field2,value2)}'
156              
157             You can specify the package name using the C parameter.
158              
159             perl -MSu::Model=dir,PkgName -e '{generate_model("ModelClass",field1,value1,field2,value2)}'
160              
161             Note that if the model name is specified with qualified package name,
162             then this C parameter not effect.
163              
164             If generation is success, this subroutine return the generated file
165             name, else should die or return undef.
166              
167             =cut
168              
169             sub generate_model {
170 11 100   11 1 7487 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
171 11 100       97 my $logger = $self->{logger} ? $self->{logger} : Su::Log->new;
172              
173             # NOTE: No need this safe guard.
174             # $self = {} unless $self;
175 11 100       51 my $MODEL_BASE_DIR = $self->{base} ? $self->{base} : $MODEL_BASE_DIR;
176 11 100       40 my $MODEL_DIR = $self->{dir} ? $self->{dir} : $MODEL_DIR;
177              
178             #diag('$MODEL_BASE_DIR:' . $MODEL_BASE_DIR);
179             #diag('$MODEL_DIR:' . $MODEL_DIR);
180 11         69 $logger->trace( '$MODEL_BASE_DIR:' . $MODEL_BASE_DIR );
181 11         76 $logger->trace( '$MODEL_DIR:' . $MODEL_DIR );
182 11         52 my $comp_id = shift;
183 11         30 my @rest = @_;
184              
185             # Make directory path.
186 11         97 my @arr = split( '/|::', $comp_id );
187 11         22 my $comp_base_name = '';
188 11 100       51 if ( scalar @arr > 1 ) {
189 6         30 $comp_base_name = join( '/', @arr[ 0 .. scalar @arr - 2 ] );
190             }
191              
192 11         19 my $dir;
193 11 100       67 if ( $comp_id =~ /::|\// ) {
194 6         18 $dir = $MODEL_BASE_DIR . "/" . $comp_base_name;
195             } else {
196 5         22 $dir = $MODEL_BASE_DIR . "/" . $MODEL_DIR . "/" . $comp_base_name;
197             }
198              
199             # Prepare directory for generate file.
200 11 100       865 mkpath $dir unless ( -d $dir );
201              
202             # '$!' can't judge error correctly.
203             # $! and die "$!:" . $dir;
204 11 50       155 if ( !-d $dir ) {
205 0         0 die "Can't make dir:" . $!;
206             }
207              
208 11         23 my $comp_id_filepath = $comp_id;
209 11         44 $comp_id_filepath =~ s!::!/!g;
210              
211             # Generate file.
212 11         24 my $fpath;
213 11 100       61 if ( $comp_id =~ /::|\// ) {
214 6         20 $fpath = $MODEL_BASE_DIR . "/" . $comp_id_filepath . ".pm";
215             } else {
216 5         22 $fpath =
217             $MODEL_BASE_DIR . "/" . $MODEL_DIR . "/" . $comp_id_filepath . ".pm";
218             }
219              
220 11 50       309394 open( my $file, '>', $fpath ) or carp "Can't open file:$fpath:" . $!;
221              
222 11         39 $comp_id =~ s/\//::/g;
223              
224 11 100       47 if ( $comp_id !~ /::/ ) {
225 5         13 my $model_dir_for_package = $MODEL_DIR;
226 5         19 $model_dir_for_package =~ s!/!::!g;
227              
228             #Note: Automatically add the default package Models.
229 5         56 $comp_id = $model_dir_for_package . '::' . $comp_id;
230             } ## end if ( $comp_id !~ /::/ )
231              
232 11         54 my $contents = _gen_contents( $comp_id, @_ );
233              
234 11         190 my $ret = print $file $contents;
235              
236 11 50       43 if ( $ret == 1 ) {
237 11         2884 print "generated:$fpath\n";
238 11         1316 return $fpath;
239             } else {
240 0         0 print "output fail:$fpath\n";
241 0         0 return undef;
242             }
243              
244             } ## end sub generate_model
245              
246             =item load_model()
247              
248             Loat the Model object from the passed model name and return it's model field.
249             Note that this mothod do not return the instance of the loaded model object itself.
250              
251             Functional style usage is like the following.
252              
253             my $model_href = Su::Model::load_model('SomeModel');
254              
255             OO Style usage is like the following.
256              
257             my $mdl = Su::Model->new;
258             $model_href = $mdl->load_model('Pkg/Mdl2');
259             $model_href = $mdl->load_model('Pkg::Mdl3');
260              
261             If you want to set some data to the model and access the data from the
262             model, then the sample code becomes as follwings:
263              
264             Su::Model::load_model('Pkg::SomeModel')->{value} = $value;
265              
266             my $value = Su::Model::load_model('Pkg::SomeModel')->{value};
267              
268             If you want to suppress dying because of module require error, then pass
269             the second parameter like the following.
270              
271             my $model = $mdl->load_model( 'Pkg::SomeModel', {suppress_error => 1} );
272              
273             When the second parameter is passed and load error occured, then this
274             method return undef.
275              
276             If you want to share and reuse model data, then pass the share parameter as
277             the second parameter.
278              
279             my $model = $mdl->load_model( 'Pkg::SomeModel', {share => 1} );
280              
281             =cut
282              
283             sub load_model {
284 54 100   54 1 3432 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
285              
286 54         887 my $model_id = shift;
287 54         83 my $opt_href = shift;
288 54 100       206 my $MODEL_CACHE_HREF = $self ? $self->{models} : $MODEL_CACHE_HREF;
289              
290             # Return the cacned data if cache exists.
291 54 100 100     287 return $MODEL_CACHE_HREF->{$model_id}
292             if ( $MODEL_CACHE_HREF->{$model_id} ) && $opt_href->{share};
293              
294             # NOTE: No need this safe guard.
295             # $self = {} unless $self;
296 52 100       162 my $MODEL_BASE_DIR = $self->{base} ? $self->{base} : $MODEL_BASE_DIR;
297 52 100       162 my $MODEL_DIR = $self->{dir} ? $self->{dir} : $MODEL_DIR;
298              
299 52         79 my $model_path = $model_id;
300              
301             # Convert package separator to file path separator.
302 52         223 $model_path =~ s!::!/!g;
303 52         87 $model_path .= ".pm";
304              
305             # Trim the head of dot slash(./) of the file path.
306 52         89 $model_path =~ s!^\./(.+)!$1!;
307              
308 52         81 eval { require($model_path); };
  52         12889  
309 52 100       2686 if ($@) {
310 5 100       17 if ( $opt_href->{suppress_error} ) {
311 4         16 return undef;
312             } else {
313 1         6 die $@;
314             }
315             } ## end if ($@)
316              
317             # Recover separator to use as model package separator.
318 47         97 $model_id =~ s!/!::!g;
319 47         86 my $model_href;
320 47 50       57 if ( exists &{ ( $model_id . "::new" ) } ) {
  47         188  
321 0         0 my $model_inst = $model_id->new;
322 0         0 $model_href = $model_inst->{model};
323             } else {
324 47 50       463 $model_href = $model_id->can('model') ? $model_id->model : undef;
325             }
326              
327 47 50       389 die "Model has no model field:" . $model_path unless $model_href;
328              
329             # Cache the model data.
330 47         124 $self->{models}->{$model_id} = $model_href;
331              
332 47 50       119 if ( $opt_href->{share} ) {
333 0         0 return $model_href;
334             } else {
335              
336             # To prevent destructive effect to this model data, we need to replicate the instance.
337 47         3217 return dclone($model_href);
338             }
339             } ## end sub load_model
340              
341             =begin comment
342              
343             Return the contents of the new Model.
344              
345             =end comment
346              
347             =cut
348              
349             sub _gen_contents {
350 11 50   11   39 shift if ( ref $_[0] eq __PACKAGE__ );
351              
352 11         29 my $comp_id = shift;
353 11         35 my %h = @_;
354              
355 11         191 my $ft = Su::Template->new;
356 11         54 my $ret = $ft->expand( <<'__TMPL__', $comp_id, \%h );
357             % my $comp_id = shift;
358             % my $href = shift;
359             package <%=$comp_id%>;
360             use strict;
361             use warnings;
362              
363             my $model=
364             {
365             % while(my ($k,$v) = each(%{$href})){
366             % if(ref $v eq 'HASH'){
367             <%=$k%> =>
368             {
369             % while(my ($kk,$vv) = each(%{$v})){
370             <%=$kk%> => "<%=$vv%>",
371             % }
372             },
373             % }else{
374             <%=$k%> => "<%=$v%>",
375             % }
376             % }
377             };
378              
379             sub model{
380             if($_[0] eq __PACKAGE__){
381             shift;
382             }
383             my $arg = shift;
384             if($arg){
385             $model = $arg;
386             }else{
387             return $model;
388             }
389              
390             }
391              
392             1;
393              
394             __TMPL__
395              
396 11         56 return $ret;
397              
398             } ## end sub _gen_contents
399              
400             =pod
401              
402             =back
403              
404             =cut
405              
406             #sub tmpl_test{
407             #
408             # my $ret = tmpl(<<'__HERE__');
409             #% my $tmp = "tmpval";
410             #one
411             #hoge<% foreach my $v ("a","b","c"){%>fuga
412             #looping
413             #<%= $v%>
414             #<%} %>
415             #<%= $tmp%>
416             #two
417             #three
418             #__HERE__
419             #
420             # return $ret;
421             #
422             #}
423              
424             1;