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   25 use utf8;
  7         9  
  7         34  
2             package Mojolicious::Plugin::DevexpressHelpers::Helpers;
3             $Mojolicious::Plugin::DevexpressHelpers::Helpers::VERSION = '0.163571';
4             #ABSTRACT: Helpers for Devexpress controls are defined here
5 7     7   299 use Modern::Perl;
  7         8  
  7         23  
6 7     7   897 use Mojo::ByteStream;
  7         44640  
  7         239  
7 7     7   2789 use MojoX::AlmostJSON qw(encode_json);
  7         14604  
  7         383  
8 7     7   32 use constant DEBUG => 0;
  7         7  
  7         9606  
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 24 my $tag = shift;
71 20         79 my $bytes = Mojo::ByteStream->new($tag);
72 20 50       143 return $bytes->decode($OUT_DECODE) if defined $OUT_DECODE;
73 0         0 return $bytes;
74             }
75              
76             sub new{
77 12     12 1 20 my $class = shift;
78 12         65 my $self = bless {
79             next_id => 1,
80             bindings => '',
81             before_bindings => '',
82             after_bindings => '',
83             }, $class;
84 12         53 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 80 my $self = shift;
94 10         36 $self->{bindings} .= join "\n", @_;
95             }
96              
97             sub next_id{
98 3     3 1 30 my $self = shift;
99 3         30 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         11 $c->stash('dxHelper')->next_id;
106             }
107              
108             sub dxbind{
109 10     10 1 17 my ($c, $control, $id, $attrs, $extensions, $befores, $afters) = @_;
110             #should return html code to be associated to the control
111 10   100     37 $befores //=[];
112 10   100     32 $afters //=[];
113             #http://stackoverflow.com/questions/9930577/jquery-dot-in-id-selector
114 10         12 my $jquery_id = $id;
115 10         21 $jquery_id =~ s{\.}{\\\\.}g;
116 10   66     38 my $prepend = ref $attrs eq 'HASH' && delete $attrs->{prependTo};
117 10   66     46 my $append = ref $attrs eq 'HASH' && delete $attrs->{appendTo};
118 10         8 my $binding = '';
119 10 100 100     46 if($prepend || $append){
120 2         5 $binding = '$(\'
\').'.$control.'(';
121             }
122             else{
123 8         18 $binding = '$("#'.$jquery_id.'").'.$control.'(';
124             }
125 10         18 my @options;
126              
127 10 100       25 if (ref($attrs) eq 'HASH') {
128 9         10 $binding .= '{';
129 9 50       21 $binding .= "\n " if $INDENT_BINDING;
130 9         35 for my $k ( sort keys %$attrs){
131 19   50     39 my $v = $attrs->{$k} // '';
132 19 100       61 if(ref($v) eq 'SCALAR'){
    50          
133             #unref protected scalar
134 2         3 $v = $$v;
135             }
136             elsif ($v!~/^\s*(?:function\s*\()/) {
137 17         43 $v = encode_json $v;
138             }
139 19         527 push @options, "$k: $v";
140             }
141             }
142             else{
143 1         2 push @options, $attrs;
144             }
145 10 50       38 $binding .= join ",\n".($INDENT_BINDING?' ':''), @options;
146 10 100       28 $binding .= '}' if ref($attrs) eq 'HASH';
147 10         11 $binding .= ')';
148 10 100       21 $binding .= '.prependTo("'.$prepend.'")' if $prepend;
149 10 100       17 $binding .= '.appendTo("'.$append.'")' if $append;
150 10 50       25 $binding .= ';' . ($INDENT_BINDING?"\n":"");
151             #append some extensions (eg: dxdatagrid)
152 10 50       20 $binding .= join ";\n".($INDENT_BINDING?' ':''), @$extensions if defined $extensions;
    100          
153 10         28 $c->stash('dxHelper')->add_binding($binding);
154 10         17 my $html_code = "
";
155 10 100 100     38 if($prepend || $append){
156 2         2 $html_code = '';
157             }
158 10         33 out join('',@$befores, $html_code ,@$afters);
159             }
160              
161              
162             sub parse_attributs{
163 10     10 1 10 my $c = shift;
164 10         10 my @implicit_args = @{shift()};
  10         22  
165 10         12 my %attrs;
166             IMPLICIT_ARGUMENT:
167 10   100     62 while(@_ and @implicit_args){
168 27         28 my $ref = ref($_[0]);
169 27   50     51 my $implicit = shift @implicit_args || '';
170 27 50 33     113 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         34 $implicit =~ s/^[\\\*\%\@]//;
176 25         98 $attrs{ $implicit } = shift @_;
177             }
178 10 100       23 if(my $args = shift){
179 7 50       20 if(ref($args) eq 'HASH'){
180             NAMED_ARGUMENT:
181 7         28 while(my($k,$v)=each %$args){
182 8         47 $attrs{$k} = $v;
183             }
184             }
185             }
186 10         22 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 9187 my $c = shift;
207 3         14 my $attrs = parse_attributs( $c, [qw(id text onClick type)], @_ );
208 3   66     12 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
209 3         7 dxbind( $c, 'dxButton' => $id => $attrs);
210             }
211              
212              
213             sub dxdatagrid{
214 3     3 1 9087 my $c = shift;
215 3         11 my $attrs = parse_attributs( $c, [qw(id dataSource)], @_ );
216 3   66     10 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     17 if ($attrs->{dataSource} && ref($attrs->{dataSource}) eq '') {
220 1         4 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         5 $attrs->{dataSource} = \"{store:{type:'odata',url:'$dataSource'}}";
226             }
227 3 100       22 if (exists $attrs->{options}) {
228 1         2 $attrs = $attrs->{options};
229             }
230            
231 3         7 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 5337 my $c = shift;
246 1         3 my $attrs = parse_attributs( $c, [qw(id title contentTemplate)], @_ );
247 1   33     4 my $id = delete($attrs->{id}) // new_id( $c, $attrs );
248            
249 1         2 dxbind( $c, 'dxPopup' => $id => $attrs );
250             }
251              
252              
253              
254             sub mk_dxcontrol{
255 258     258 1 201 my $dxControl = shift;
256             my $generic = sub{
257 3     3   12245 my $c = shift;
258 3         12 my $attrs = parse_attributs( $c, [qw(id value label)], @_ );
259 3         7 my $id = delete($attrs->{id});
260 3 50       7 if (my $name = $id) {
261 3         5 $attrs->{name}=$name;
262             }
263            
264 3   33     7 $id //= new_id( $c, $attrs );
265            
266 3         4 my (@before, @after);
267 3 50       7 if(my $label = delete($attrs->{label})){
268 3         4 push @before, '
';
269 3         8 push @before, '
'.$label.'
';
270 3         3 push @before, '
';
271 3         4 push @after, '';
272 3         4 push @after, '';
273             }
274            
275 3         9 dxbind( $c, $dxControl => $id => $attrs, undef, \@before, \@after );
276 258         552 };
277            
278             {
279 7     7   33 no strict 'refs';
  7         6  
  7         4691  
  258         164  
280 258         153 *{__PACKAGE__.'::'.lc $dxControl} = $generic;
  258         1085  
281             }
282             }
283              
284              
285             sub dxbuild {
286 10     10 1 7040 my $c = shift;
287 10         19 my %opts = @_;
288 10 50       24 my $dxhelper = $c->stash('dxHelper') or return;
289 10 50       93 if($dxhelper->{bindings}){
290             out '';
      50        
299             }
300             }
301              
302              
303             sub require_asset{
304 3     3 1 7660 my $c = shift;
305 3 50       6 my $dxhelper = $c->stash('dxHelper') or return;
306            
307 3         29 push @{ $dxhelper->{required_assets} }, $_ for @_;
  3         8  
308            
309 3         4 return $c;
310             }
311              
312              
313             sub required_assets{
314 2     2 1 1304 my $c = shift;
315 2 50       7 my $dxhelper = $c->stash('dxHelper') or return;
316 2   50     24 my $required_assets = $dxhelper->{required_assets} // [];
317 2         21 my $results = Mojo::ByteStream->new();
318             ASSET:
319 2         18 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         794 $$results .= ${ $c->asset($asset) };
  3         28  
323             }
324 2         2097 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 7 my ( $self, $app, $args ) = @_;
348 6         9 my $tp = $args->{tag_prefix};
349            
350             #build generic dx-controls
351 6         23 mk_dxcontrol( "dx$_" ) for @generic_controls;
352            
353             SUB_NO_PREFIX:
354 6         11 for my $subname ( @without_prefix ){
355 36         358 my $lc_name = lc $subname;
356 36         106 my $sub = __PACKAGE__->can( $lc_name );
357 36 50       65 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         78 $app->helper( $lc_name => $sub );
362             }
363              
364             SUB_WITH_PREFIX:
365 6         52 for my $subname ( @with_prefix ){
366 300         2447 my $lc_name = lc $subname;
367 300         782 my $sub = __PACKAGE__->can( 'dx' . $lc_name );
368 300 50       438 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         157 say STDERR "## adding helper '$tp$lc_name'" if DEBUG;
373 300         484 $app->helper( $tp . $lc_name => $sub );
374 300         2268 say STDERR "## adding helper '$tp$subname'" if DEBUG and $args->{tag_camelcase};
375 300 50       690 $app->helper( $tp . $subname => $sub ) if $args->{tag_camelcase};
376             }
377            
378             }
379              
380             1;
381              
382             __END__