File Coverage

blib/lib/Mojolicious/Plugin/DevexpressHelpers/Helpers.pm
Criterion Covered Total %
statement 151 180 83.8
branch 39 64 60.9
condition 40 76 52.6
subroutine 22 28 78.5
pod 21 21 100.0
total 273 369 73.9


line stmt bran cond sub pod time code
1 7     7   30 use utf8;
  7         8  
  7         41  
2             package Mojolicious::Plugin::DevexpressHelpers::Helpers;
3             $Mojolicious::Plugin::DevexpressHelpers::Helpers::VERSION = '0.163572';
4             #ABSTRACT: Helpers for Devexpress controls are defined here
5 7     7   319 use Modern::Perl;
  7         9  
  7         29  
6 7     7   971 use Mojo::ByteStream;
  7         45826  
  7         302  
7 7     7   3068 use MojoX::AlmostJSON qw(encode_json);
  7         16221  
  7         441  
8 7     7   40 use constant DEBUG => 0;
  7         7  
  7         11463  
9              
10             #Not sure why C function have to decode from utf8,
11             #but it make my day!
12             our $OUT_DECODE = 'UTF-8';
13             our $INDENT_BINDING = 0;
14              
15             my @generic_controls = qw(
16             Accordion
17             ActionSheet
18             Autocomplete
19             Box
20             CheckBox
21             Calendar
22             ColorBox
23             ContextMenu
24             DateBox
25             DeferRendering
26             FileUploader
27             Gallery
28             List
29             LoadIndicator
30             Lookup
31             Map
32             MultiView
33             NavBar
34             NumberBox
35             Panorama
36             Pivot
37             PivotGrid
38             PivotGridFieldChooser
39             Popover
40             ProgressBar
41             RadioGroup
42             RangeSlider
43             Resizable
44             ResponsiveBox
45             Scheduler
46             ScrollView
47             SelectBox
48             Slider
49             Switch
50             TabPanel
51             Tabs
52             TagBox
53             TextArea
54             TextBox
55             TitleView
56             Toast
57             Toolbar
58             TreeView
59             );
60              
61             #Helper method to export without prepending a prefix
62             my @without_prefix = qw( dxbuild required_assets require_asset indent_binding append_js prepend_js );
63              
64             #Helper method to export with prepending a prefix
65             my @with_prefix = (qw( Button DataGrid Form Popup Menu LoadPanel Lookup ),
66             @generic_controls);
67              
68              
69             sub out{
70 20     20 1 25 my $tag = shift;
71 20         106 my $bytes = Mojo::ByteStream->new($tag);
72 20 50       189 return $bytes->decode($OUT_DECODE) if defined $OUT_DECODE;
73 0         0 return $bytes;
74             }
75              
76             sub new{
77 12     12 1 23 my $class = shift;
78 12         76 my $self = bless {
79             next_id => 1,
80             bindings => '',
81             before_bindings => '',
82             after_bindings => '',
83             }, $class;
84 12         70 return $self;
85             }
86              
87             sub indent_binding{
88 0     0 1 0 my $self = shift;
89 0         0 $INDENT_BINDING = shift;
90             }
91              
92             sub add_binding{
93 10     10 1 106 my $self = shift;
94 10         52 $self->{bindings} .= join "\n", @_;
95             }
96              
97             sub next_id{
98 3     3 1 32 my $self = shift;
99 3         34 return "dxctl".($self->{next_id}++);
100             }
101              
102             sub new_id{
103 3     3 1 6 my ($c, $attrs) = @_;
104             #should compute a new uniq id
105 3         9 $c->stash('dxHelper')->next_id;
106             }
107              
108             sub dxbind{
109 10     10 1 20 my ($c, $control, $id, $attrs, $extensions, $befores, $afters) = @_;
110             #should return html code to be associated to the control
111 10   100     41 $befores //=[];
112 10   100     39 $afters //=[];
113             #http://stackoverflow.com/questions/9930577/jquery-dot-in-id-selector
114 10         17 my $jquery_id = $id;
115 10         25 $jquery_id =~ s{\.}{\\\\.}g;
116 10   66     48 my $prepend = ref $attrs eq 'HASH' && delete $attrs->{prependTo};
117 10   66     56 my $append = ref $attrs eq 'HASH' && delete $attrs->{appendTo};
118 10         14 my $binding = '';
119 10 100 100     56 if($prepend || $append){
120 2         5 $binding = '$(\'
\').'.$control.'(';
121             }
122             else{
123 8         25 $binding = '$("#'.$jquery_id.'").'.$control.'(';
124             }
125 10         14 my @options;
126              
127 10 100       37 if (ref($attrs) eq 'HASH') {
128 9         19 $binding .= '{';
129 9 50       24 $binding .= "\n " if $INDENT_BINDING;
130 9         47 for my $k ( sort keys %$attrs){
131 19   50     46 my $v = $attrs->{$k} // '';
132 19 100       72 if(ref($v) eq 'SCALAR'){
    50          
133             #unref protected scalar
134 2         3 $v = $$v;
135             }
136             elsif ($v!~/^\s*(?:function\s*\()/) {
137 17         58 $v = encode_json $v;
138             }
139 19         634 push @options, "$k: $v";
140             }
141             }
142             else{
143 1         2 push @options, $attrs;
144             }
145 10 50       47 $binding .= join ",\n".($INDENT_BINDING?' ':''), @options;
146 10 100       33 $binding .= '}' if ref($attrs) eq 'HASH';
147 10         18 $binding .= ')';
148 10 100       28 $binding .= '.prependTo("'.$prepend.'")' if $prepend;
149 10 100       24 $binding .= '.appendTo("'.$append.'")' if $append;
150 10 50       32 $binding .= ';' . ($INDENT_BINDING?"\n":"");
151             #append some extensions (eg: dxdatagrid)
152 10 50       29 $binding .= join ";\n".($INDENT_BINDING?' ':''), @$extensions if defined $extensions;
    100          
153 10         34 $c->stash('dxHelper')->add_binding($binding);
154 10         22 my $html_code = "
";
155 10 100 100     54 if($prepend || $append){
156 2         2 $html_code = '';
157             }
158 10         42 out join('',@$befores, $html_code ,@$afters);
159             }
160              
161              
162             sub parse_attributs{
163 10     10 1 15 my $c = shift;
164 10         19 my @implicit_args = @{shift()};
  10         26  
165 10         14 my %attrs;
166             IMPLICIT_ARGUMENT:
167 10   100     83 while(@_ and @implicit_args){
168 27         40 my $ref = ref($_[0]);
169 27   50     52 my $implicit = shift @implicit_args || '';
170 27 50 33     130 last unless $ref =~ /^(?:|SCALAR)$/
      66        
      33        
      33        
      33        
      33        
      33        
171             or (substr($implicit,0,1) eq '@' and $ref eq 'ARRAY')
172             or (substr($implicit,0,1) eq '%' and $ref eq 'HASH')
173             or (substr($implicit,0,1) eq '\\' and $ref eq 'REF')
174             or (substr($implicit,0,1) eq '*');
175 25         39 $implicit =~ s/^[\\\*\%\@]//;
176 25         107 $attrs{ $implicit } = shift @_;
177             }
178 10 100       34 if(my $args = shift){
179 7 50       25 if(ref($args) eq 'HASH'){
180             NAMED_ARGUMENT:
181 7         35 while(my($k,$v)=each %$args){
182 8         26 $attrs{$k} = $v;
183             }
184             }
185             }
186 10         26 return \%attrs;
187             }
188              
189             sub dxmenu {
190 0     0 1 0 my $c = shift;
191 0         0 my $attrs = parse_attributs( $c, [qw(id @items onItemClick)], @_ );
192 0   0     0 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
193 0         0 dxbind( $c, 'dxMenu' => $id => $attrs);
194             }
195              
196              
197             sub dxloadpanel {
198 0     0 1 0 my $c = shift;
199 0         0 my $attrs = parse_attributs( $c, [qw(id message)], @_ );
200 0   0     0 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
201 0         0 dxbind( $c, 'dxLoadPanel' => $id => $attrs);
202             }
203              
204              
205             sub dxbutton {
206 3     3 1 10225 my $c = shift;
207 3         19 my $attrs = parse_attributs( $c, [qw(id text onClick type)], @_ );
208 3   66     14 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
209 3         9 dxbind( $c, 'dxButton' => $id => $attrs);
210             }
211              
212              
213             sub dxdatagrid{
214 3     3 1 12000 my $c = shift;
215 3         15 my $attrs = parse_attributs( $c, [qw(id dataSource)], @_ );
216 3   66     13 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
217 3         3 my @extensions;
218             #dxbind( $c, 'dxDataGrid' => $id => $attrs, [ $dataSource ]);
219 3 100 100     18 if ($attrs->{dataSource} && ref($attrs->{dataSource}) eq '') {
220 1         2 my $dataSource = delete $attrs->{dataSource};
221             #push @extensions, '$.getJSON("' . $dataSource . '",function(data){$("#'.$id.'").dxDataGrid({ dataSource: data });});';
222             #$attrs->{dataSource} = \'[]'; #protect string to be "stringified" within dxbind
223              
224             #\"" is to protect string to be "stringified" within dxbind
225 1         4 $attrs->{dataSource} = \"{store:{type:'odata',url:'$dataSource'}}";
226             }
227 3 100       13 if (exists $attrs->{options}) {
228 1         2 $attrs = $attrs->{options};
229             }
230            
231 3         19 dxbind( $c, 'dxDataGrid' => $id => $attrs, \@extensions);
232             }
233              
234              
235             sub dxform{
236 0     0 1 0 my $c = shift;
237 0         0 my $attrs = parse_attributs( $c, [qw(id %formData @items)], @_ );
238 0   0     0 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
239            
240 0         0 dxbind( $c, 'dxForm' => $id => $attrs );
241             }
242              
243              
244             sub dxpopup{
245 1     1 1 5119 my $c = shift;
246 1         5 my $attrs = parse_attributs( $c, [qw(id title contentTemplate)], @_ );
247 1   33     7 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
248            
249 1         3 dxbind( $c, 'dxPopup' => $id => $attrs );
250             }
251              
252              
253              
254             sub mk_dxcontrol{
255 258     258 1 225 my $dxControl = shift;
256             my $generic = sub{
257 3     3   16041 my $c = shift;
258 3         18 my $attrs = parse_attributs( $c, [qw(id value label)], @_ );
259 3         10 my $id = delete($attrs->{id});
260 3 50       9 if (my $name = $id) {
261 3         8 $attrs->{name}=$name;
262             }
263            
264 3   33     8 $id //= new_id( $c, $attrs );
265            
266 3         4 my (@before, @after);
267 3 50       10 if(my $label = delete($attrs->{label})){
268 3         5 push @before, '
';
269 3         8 push @before, '
'.$label.'
';
270 3         7 push @before, '
';
271 3         5 push @after, '';
272 3         7 push @after, '';
273             }
274            
275 3         13 dxbind( $c, $dxControl => $id => $attrs, undef, \@before, \@after );
276 258         699 };
277            
278             {
279 7     7   39 no strict 'refs';
  7         12  
  7         5690  
  258         186  
280 258         175 *{__PACKAGE__.'::'.lc $dxControl} = $generic;
  258         1375  
281             }
282             }
283              
284              
285             sub dxbuild {
286 10     10 1 7934 my $c = shift;
287 10         23 my %opts = @_;
288 10 50       27 my $dxhelper = $c->stash('dxHelper') or return;
289 10 50       115 if($dxhelper->{bindings}){
290             out '';
      50        
299             }
300             }
301              
302              
303             sub require_asset{
304 3     3 1 8691 my $c = shift;
305 3 50       9 my $dxhelper = $c->stash('dxHelper') or return;
306            
307 3         32 push @{ $dxhelper->{required_assets} }, $_ for @_;
  3         11  
308            
309 3         5 return $c;
310             }
311              
312              
313             sub required_assets{
314 2     2 1 1333 my $c = shift;
315 2 50       7 my $dxhelper = $c->stash('dxHelper') or return;
316 2   50     26 my $required_assets = $dxhelper->{required_assets} // [];
317 2         20 my $results = Mojo::ByteStream->new();
318             ASSET:
319 2         17 for my $asset (@$required_assets){
320             #not sure about how to simulate " %= asset 'resource' " that we can use in template rendering,
321             #nor how to output multiple Mojo::ByteStream objets at a time (is returning required ?)
322 3         1034 $$results .= ${ $c->asset($asset) };
  3         22  
323             }
324 2         1947 return $results;
325             }
326              
327             sub prepend_js{
328 0     0 1 0 my ($c, @js) = @_;
329 0 0       0 my $dxhelper = $c->stash('dxHelper') or return;
330 0         0 for(@js){
331 0 0       0 $dxhelper->{before_bindings} .= "\n" if $INDENT_BINDING;
332 0         0 $dxhelper->{before_bindings} .= $_;
333             }
334             }
335              
336              
337             sub append_js{
338 0     0 1 0 my ($c, @js) = @_;
339 0 0       0 my $dxhelper = $c->stash('dxHelper') or return;
340 0         0 for(@js){
341 0 0       0 $dxhelper->{after_bindings} .= "\n" if $INDENT_BINDING;
342 0         0 $dxhelper->{after_bindings} .= $_;
343             }
344             }
345              
346             sub register {
347 6     6 1 10 my ( $self, $app, $args ) = @_;
348 6         10 my $tp = $args->{tag_prefix};
349            
350             #build generic dx-controls
351 6         27 mk_dxcontrol( "dx$_" ) for @generic_controls;
352            
353             SUB_NO_PREFIX:
354 6         13 for my $subname ( @without_prefix ){
355 36         497 my $lc_name = lc $subname;
356 36         146 my $sub = __PACKAGE__->can( $lc_name );
357 36 50       79 unless($sub){
358 0         0 $app->log->debug(__PACKAGE__." helper '$lc_name' does not exists!");
359 0         0 next SUB_NO_PREFIX;
360             }
361 36         110 $app->helper( $lc_name => $sub );
362             }
363              
364             SUB_WITH_PREFIX:
365 6         68 for my $subname ( @with_prefix ){
366 300         2991 my $lc_name = lc $subname;
367 300         1002 my $sub = __PACKAGE__->can( 'dx' . $lc_name );
368 300 50       470 unless($sub){
369 0         0 $app->log->debug(__PACKAGE__." helper 'dx$lc_name' does not exists!");
370 0         0 next SUB_WITH_PREFIX;
371             }
372 300         190 say STDERR "## adding helper '$tp$lc_name'" if DEBUG;
373 300         586 $app->helper( $tp . $lc_name => $sub );
374 300         2740 say STDERR "## adding helper '$tp$subname'" if DEBUG and $args->{tag_camelcase};
375 300 50       814 $app->helper( $tp . $subname => $sub ) if $args->{tag_camelcase};
376             }
377            
378             }
379              
380             1;
381              
382             __END__