engine2 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +0 -0
  3. data/Rakefile +4 -4
  4. data/app/{engine2actions.coffee → actions.coffee} +341 -215
  5. data/app/app.coffee +0 -0
  6. data/app/app.css +17 -0
  7. data/app/engine2.coffee +158 -208
  8. data/app/modal.coffee +138 -0
  9. data/bower.json +4 -2
  10. data/conf/message.yaml +5 -0
  11. data/conf/message_pl.yaml +7 -2
  12. data/config.coffee +24 -12
  13. data/engine2.gemspec +8 -8
  14. data/lib/engine2.rb +12 -10
  15. data/lib/engine2/action.rb +1338 -133
  16. data/lib/engine2/action/array.rb +189 -0
  17. data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
  18. data/lib/engine2/action/delete.rb +64 -0
  19. data/lib/engine2/action/form.rb +16 -0
  20. data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
  21. data/lib/engine2/action/link.rb +117 -0
  22. data/lib/engine2/action/list.rb +333 -0
  23. data/lib/engine2/action/save.rb +28 -0
  24. data/lib/engine2/action/view.rb +8 -0
  25. data/lib/engine2/action_node.rb +221 -0
  26. data/lib/engine2/core.rb +175 -87
  27. data/lib/engine2/handler.rb +14 -13
  28. data/lib/engine2/model.rb +85 -43
  29. data/lib/engine2/models/Files.rb +4 -1
  30. data/lib/engine2/models/UserInfo.rb +6 -3
  31. data/lib/engine2/post_bootstrap.rb +4 -3
  32. data/lib/engine2/pre_bootstrap.rb +10 -6
  33. data/lib/engine2/scheme.rb +107 -65
  34. data/lib/engine2/templates.rb +41 -6
  35. data/lib/engine2/type_info.rb +51 -23
  36. data/lib/engine2/version.rb +2 -1
  37. data/package.json +22 -16
  38. data/public/favicon.ico +0 -0
  39. data/public/img/ajax-loader-dark.gif +0 -0
  40. data/public/img/ajax-loader.gif +0 -0
  41. data/views/fields/blob.slim +1 -1
  42. data/views/fields/bs_select.slim +2 -2
  43. data/views/fields/bsselect_picker.slim +4 -4
  44. data/views/fields/bsselect_picker_opt.slim +5 -5
  45. data/views/fields/checkbox.slim +4 -4
  46. data/views/fields/checkbox_button.slim +6 -0
  47. data/views/fields/checkbox_buttons.slim +3 -3
  48. data/views/fields/checkbox_buttons_opt.slim +3 -3
  49. data/views/fields/currency.slim +2 -2
  50. data/views/fields/date.slim +4 -4
  51. data/views/fields/date_range.slim +9 -9
  52. data/views/fields/date_time.slim +9 -9
  53. data/views/fields/datetime.slim +8 -8
  54. data/views/fields/decimal.slim +1 -1
  55. data/views/fields/decimal_date.slim +3 -3
  56. data/views/fields/decimal_time.slim +3 -3
  57. data/views/fields/email.slim +3 -3
  58. data/views/fields/file_store.slim +11 -11
  59. data/views/fields/input_text.slim +4 -4
  60. data/views/fields/integer.slim +1 -1
  61. data/views/fields/list_bsmselect.slim +20 -0
  62. data/views/fields/list_bsselect.slim +5 -5
  63. data/views/fields/list_bsselect_opt.slim +6 -6
  64. data/views/fields/list_buttons.slim +1 -1
  65. data/views/fields/list_buttons_opt.slim +2 -2
  66. data/views/fields/list_mbuttons.slim +9 -0
  67. data/views/fields/list_mbuttons_opt.slim +11 -0
  68. data/views/fields/list_mselect.slim +12 -0
  69. data/views/fields/list_select.slim +4 -4
  70. data/views/fields/list_select_opt.slim +5 -5
  71. data/views/fields/password.slim +4 -4
  72. data/views/fields/radio_checkbox.slim +3 -3
  73. data/views/fields/scaffold.slim +2 -2
  74. data/views/fields/scaffold_picker.slim +5 -5
  75. data/views/fields/select_picker.slim +3 -3
  76. data/views/fields/select_picker_opt.slim +4 -4
  77. data/views/fields/text_area.slim +4 -3
  78. data/views/fields/time.slim +5 -4
  79. data/views/fields/typeahead_picker.slim +12 -9
  80. data/views/index.slim +3 -3
  81. data/views/infra/index.slim +0 -0
  82. data/views/infra/inspect.slim +41 -10
  83. data/views/modals/close_m.slim +0 -0
  84. data/views/modals/confirm_m.slim +0 -0
  85. data/views/modals/empty_m.slim +0 -0
  86. data/views/modals/menu_m.slim +1 -1
  87. data/views/modals/yes_no_m.slim +0 -0
  88. data/views/panels/menu_m.slim +1 -1
  89. data/views/scaffold/confirm.slim +0 -0
  90. data/views/scaffold/fields.slim +6 -4
  91. data/views/scaffold/form.slim +1 -1
  92. data/views/scaffold/form_collapse.slim +4 -3
  93. data/views/scaffold/form_tabs.slim +3 -2
  94. data/views/scaffold/list.slim +0 -0
  95. data/views/scaffold/message.slim +0 -0
  96. data/views/scaffold/search.slim +4 -4
  97. data/views/scaffold/search_collapse.slim +8 -7
  98. data/views/scaffold/search_tabs.slim +6 -5
  99. data/views/scaffold/view.slim +2 -2
  100. data/views/scaffold/view_collapse.slim +5 -4
  101. data/views/scaffold/view_tabs.slim +4 -3
  102. data/views/search_fields/bsmselect_picker.slim +4 -4
  103. data/views/search_fields/bsselect_picker.slim +4 -4
  104. data/views/search_fields/checkbox.slim +3 -3
  105. data/views/search_fields/checkbox2.slim +5 -5
  106. data/views/search_fields/checkbox_buttons.slim +3 -3
  107. data/views/search_fields/date.slim +20 -0
  108. data/views/search_fields/date_range.slim +8 -8
  109. data/views/search_fields/decimal_date_range.slim +5 -5
  110. data/views/search_fields/input_text.slim +2 -2
  111. data/views/search_fields/integer.slim +1 -1
  112. data/views/search_fields/integer_range.slim +2 -2
  113. data/views/search_fields/list_bsmselect.slim +4 -4
  114. data/views/search_fields/list_bsselect.slim +4 -4
  115. data/views/search_fields/list_buttons.slim +2 -2
  116. data/views/search_fields/list_mbuttons.slim +12 -0
  117. data/views/search_fields/list_select.slim +3 -3
  118. data/views/search_fields/scaffold_picker.slim +2 -2
  119. data/views/search_fields/select_picker.slim +3 -3
  120. data/views/search_fields/typeahead_picker.slim +8 -7
  121. metadata +53 -48
  122. data/lib/engine2/meta.rb +0 -1216
  123. data/lib/engine2/meta/array_meta.rb +0 -82
  124. data/lib/engine2/meta/delete_meta.rb +0 -60
  125. data/lib/engine2/meta/form_meta.rb +0 -15
  126. data/lib/engine2/meta/link_meta.rb +0 -134
  127. data/lib/engine2/meta/list_meta.rb +0 -281
  128. data/lib/engine2/meta/save_meta.rb +0 -50
  129. data/lib/engine2/meta/view_meta.rb +0 -7
  130. data/public/__sinatra__/404.png +0 -0
  131. data/public/__sinatra__/500.png +0 -0
@@ -1,1216 +0,0 @@
1
- # coding: utf-8
2
- module Engine2
3
- class Meta
4
- attr_reader :action, :assets, :static, :invokable
5
-
6
- class << self
7
- def meta_type mt = nil
8
- mt ? @meta_type = mt : @meta_type
9
- end
10
-
11
- def http_method hm = nil
12
- hm ? @http_method = hm : @http_method
13
- end
14
-
15
- def inherited cls
16
- cls.http_method http_method
17
- end
18
- end
19
-
20
- http_method :get
21
-
22
- def initialize action, assets, static = self
23
- @meta = {}
24
- @action = action
25
- @assets = assets
26
- @static = static
27
- end
28
-
29
- def http_method
30
- @http_method # || (raise E2Error.new("No http method for meta #{self.class}"))
31
- end
32
-
33
- def meta_type
34
- @meta_type || (raise E2Error.new("No meta_type for meta #{self.class}"))
35
- end
36
-
37
- def check_static_meta
38
- raise E2Error.new("Static meta required") if dynamic?
39
- end
40
-
41
- def define_invoke &blk
42
- check_static_meta
43
- self.class.class_eval{define_method :invoke, &blk}
44
- end
45
-
46
- def invoke! handler
47
- if rmp = @request_meta_proc
48
- meta = self.class.new(action, assets, self)
49
- meta_result = meta.instance_exec(handler, *meta.request_meta_proc_params(handler), &rmp)
50
- meta.post_process
51
- response = @requestable ? (meta_result || {}) : meta.invoke(handler)
52
- response[:meta] = meta.get
53
- response
54
- else
55
- invoke(handler)
56
- end
57
- end
58
-
59
- def repeat time
60
- @meta[:repeat] = time
61
- end
62
-
63
- def arguments args
64
- @meta[:arguments] = args
65
- end
66
-
67
- def execute time
68
- @meta[:execute] = time
69
- end
70
-
71
- def get
72
- @meta
73
- end
74
-
75
- def dynamic?
76
- self != @static
77
- end
78
-
79
- # def [] *keys
80
- # @meta.path(*keys)
81
- # end
82
-
83
- # def []= *keys, value
84
- # @meta.path!(*keys, value)
85
- # end
86
-
87
- def lookup *keys
88
- if dynamic? # we are the request meta
89
- value = @meta.path(*keys)
90
- value.nil? ? @static.get.path(*keys) : value
91
- # value || @static.value.path(keys)
92
- else
93
- @meta.path(*keys)
94
- end
95
- end
96
-
97
- def merge *keys
98
- if keys.length == 1
99
- key = keys.first
100
- dynamic? ? @static.get[key].merge(@meta[key] || {}) : @meta[key]
101
- else
102
- dynamic? ? @static.get.path(*keys).merge(@meta.path(*keys)) : @meta.path(*keys)
103
- end
104
- end
105
-
106
- def freeze_meta
107
- hash = @meta
108
- hash.freeze
109
- # hash.each_pair{|k, v| freeze(v) if v.is_a? Hash}
110
- freeze
111
- end
112
-
113
- def request_meta_proc_params handler
114
- []
115
- end
116
-
117
- def request &blk
118
- raise E2Error.new("No block given for request meta") unless blk
119
- raise E2Error.new("Request meta already supplied") if @request_meta_proc
120
- raise E2Error.new("No request block in request meta allowed") if dynamic?
121
- @request_meta_proc = blk
122
- nil
123
- end
124
-
125
- def pre_run
126
- @meta_type = self.class.meta_type
127
- @http_method = self.class.http_method
128
- end
129
-
130
- def action_defined
131
- end
132
-
133
- def post_run
134
- if respond_to? :invoke
135
- @invokable = true
136
- else
137
- if @request_meta_proc
138
- @invokable = true
139
- @requestable = true
140
- else
141
- @meta[:invokable] = false
142
- end
143
- end
144
- post_process
145
- end
146
-
147
- def post_process
148
- end
149
-
150
- def split_keys id
151
- Sequel::split_keys(id)
152
- end
153
- end
154
-
155
- class InlineMeta < Meta
156
- meta_type :inline
157
- end
158
-
159
- class RootMeta < Meta
160
- def initialize *args
161
- super
162
- @meta.merge! environment: Handler::environment, application: Engine2::SETTINGS[:name], key_separator: Engine2::SETTINGS[:key_separator]
163
- end
164
- end
165
-
166
- module MetaAPISupport
167
- def reload_routes!
168
- @meta[:reload_routes] = true
169
- end
170
-
171
- def info
172
- @meta[:info] ||= {}
173
- end
174
-
175
- def config
176
- @meta[:config] ||= {}
177
- end
178
-
179
- def info! *fields, options
180
- raise E2Error.new("No fields given to info") if fields.empty?
181
- fields.each do |field|
182
- (info[field] ||= {}).merge! options # rmerge ?
183
- end
184
- end
185
-
186
- def loc! hash
187
- (@meta[:loc] ||= {}).merge! hash
188
- end
189
-
190
- def decorate list
191
- list.each do |f|
192
- m = (info[f] ||= {})
193
- m[:loc] ||= LOCS[f]
194
- end
195
- end
196
-
197
- def render field, options
198
- info! field, render: options
199
- end
200
-
201
- def hide_fields *flds
202
- info! *flds, hidden: true
203
- end
204
-
205
- def show_fields *flds
206
- info! *flds, hidden: false
207
- end
208
-
209
- def field_filter *flds, filter
210
- info! *flds, filter: filter
211
- end
212
- end
213
-
214
- module MetaMenuSupport
215
- def menu menu_name, &blk
216
- @menus ||= {}
217
- @menus[menu_name] ||= ActionMenuBuilder.new(:root)
218
- @menus[menu_name].instance_eval(&blk) if blk
219
- @menus[menu_name]
220
- end
221
-
222
- def menu? menu_name
223
- @menus && @menus[menu_name]
224
- end
225
-
226
- def post_process
227
- super
228
- if @menus && !@menus.empty?
229
- @meta[:menus] = {}
230
- @menus.each_pair do |name, menu|
231
- @meta[:menus][name] = {entries: menu.to_a, properties: menu.properties}
232
- end
233
- end
234
- end
235
- end
236
-
237
- module MetaModelSupport
238
- def pre_run
239
- if !(mdl = @assets[:model])
240
- act = action
241
- begin
242
- act = act.parent
243
- raise E2Error.new("Model not found in tree for action: #{action.name}") unless act
244
- mdl = act.*.assets[:model]
245
- end until mdl
246
-
247
- if asc = @assets[:assoc]
248
- @assets[:model] = asc.associated_class
249
- # raise E2Error.new("Association '#{asc}' for model '#{asc[:class_name]}' not found") unless @assets[:model]
250
- else
251
- @assets[:model] = mdl
252
- asc = act.*.assets[:assoc]
253
- @assets[:assoc] = asc if asc
254
- end
255
- end
256
-
257
- # @meta[:model!] = assets[:model]
258
- # @meta[:assoc!] = assets[:assoc] ? assets[:assoc][:name] : nil
259
- # @meta[:meta_class!] = self.class
260
- super
261
- end
262
-
263
- def hide_pk
264
- hide_fields *assets[:model].primary_keys
265
- end
266
-
267
- def show_pk
268
- show_fields *assets[:model].primary_keys
269
- end
270
-
271
- def get_type_info name
272
- model = assets[:model]
273
- info = model.type_info[name]
274
- unless info
275
- if name =~ /^(\w+)__(\w+?)$/ # (?:___\w+)?
276
- assoc = model.many_to_one_associations[$1.to_sym] || model.one_to_one_associations[$1.to_sym]
277
- raise E2Error.new("Association #{$1} not found for model #{model}") unless assoc
278
- m = assoc.associated_class
279
- info = m.type_info.fetch($2.to_sym)
280
- else
281
- raise E2Error.new("Type info not found for '#{name}' in model '#{model}'")
282
- end
283
- end
284
- info
285
- end
286
-
287
- # def parent_model_name
288
- # model = @assets[:model]
289
- # prnt = action.parent
290
-
291
- # while prnt && prnt.*.assets[:model] == model
292
- # prnt = prnt.parent
293
- # end
294
- # m = prnt.*.assets[:model]
295
- # m ? m.name : nil
296
- # end
297
-
298
- def action_defined
299
- super
300
- # p_model_name = parent_model_name
301
- model = @assets[:model]
302
-
303
- mt = meta_type
304
- case mt
305
- when :list, :star_to_many_list, :star_to_many_link_list, :star_to_many_field, :star_to_many_field_link_list # :many_to_one_list
306
- model.many_to_one_associations.each do |assoc_name, assoc|
307
- unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
308
- dc = model.type_info[assoc[:keys].first][:decode]
309
- action.run_scheme :decode, model, assoc_name, dc[:search]
310
- end
311
- end
312
- end
313
-
314
- case mt
315
- when :modify, :create
316
- model.many_to_one_associations.each do |assoc_name, assoc|
317
- unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
318
- dc = model.type_info[assoc[:keys].first][:decode]
319
- action.run_scheme :decode, model, assoc_name, dc[:form]
320
- end
321
- end
322
- end
323
-
324
- case mt
325
- when :list #, :star_to_many_list, :many_to_one_list # list dropdowns
326
- divider = false
327
- model.one_to_many_associations.merge(model.many_to_many_associations).each do |assoc_name, assoc|
328
- unless assoc[:propagate] == false
329
- menu(:item_menu).divider unless divider
330
- divider ||= true
331
- menu(:item_menu).option :"#{assoc_name}!", icon: "list" # , click: "action.show_assoc($index, \"#{assoc_name}!\")"
332
- action.run_scheme :star_to_many, :"#{assoc_name}!", assoc
333
- end
334
- end
335
- end
336
-
337
- case mt
338
- when :modify, :create
339
- model.type_info.each do |field, info|
340
- case info[:type]
341
- when :blob_store
342
- action.run_scheme :blob_store, model, field
343
- when :foreign_blob_store
344
- action.run_scheme :foreign_blob_store, model, field
345
- when :file_store
346
- action.run_scheme :file_store, model, field
347
- when :star_to_many_field
348
- assoc = model.association_reflections[info[:assoc_name]] # info[:name] ?
349
- raise E2Error.new("Association '#{info[:assoc_name]}' not found for model '#{model}'") unless assoc
350
- action.run_scheme :star_to_many_field, assoc, field
351
- end
352
- end
353
- end
354
- end
355
-
356
- def unsupported_association assoc
357
- raise E2Error.new("Unsupported association: #{assoc}")
358
- end
359
- end
360
-
361
- module MetaQuerySupport
362
- def query q, &blk
363
- @query = q.naked
364
- @query.row_proc = blk if blk
365
- end
366
-
367
- def post_run
368
- query select(*assets[:model].columns) unless @query
369
- super
370
- end
371
-
372
- def get_query # move to query ?
373
- if dynamic?
374
- @query || @static.get_query
375
- else
376
- @query
377
- end
378
- end
379
-
380
- def find_record handler, id
381
- get_query[assets[:model].primary_keys_hash_qualified(split_keys(id))]
382
- end
383
-
384
- def select *args, &blk
385
- assets[:model].select(*args, &blk).ensure_primary_key.setup! (@meta[:fields] = [])
386
- end
387
- end
388
-
389
- module MetaTabSupport
390
- def select_tabs tabs, *args, &blk
391
- field_tabs tabs
392
- select *tabs.map{|name, fields|fields}.flatten, *args, &blk
393
- end
394
-
395
- def field_tabs hash
396
- @meta[:tabs] = hash.map{|k, v| {name: k, loc: LOCS[k], fields: v} }
397
- end
398
-
399
- def lazy_tab tab_name
400
- tabs = @meta[:tabs]
401
- raise E2Error.new("No tabs defined") unless tabs
402
- tab = tabs.find{|t| t[:name] == tab_name}
403
- raise E2Error.new("No tab #{tab_name} defined") unless tab
404
- tab[:lazy] = true
405
- end
406
- end
407
-
408
- module MetaAngularSupport
409
- def ng_execute expr
410
- (@meta[:execute] ||= "") << expr + ";"
411
- end
412
-
413
- def ng_record! name, value
414
- value = case value
415
- when String
416
- "'#{value}'"
417
- when nil
418
- 'null'
419
- else
420
- value
421
- end
422
-
423
- "action.record['#{name}'] = #{value}"
424
- end
425
-
426
- def ng_record name
427
- "action.record['#{name}']"
428
- end
429
-
430
- def ng_info! name, *selector, expression
431
- # expression = "'#{expression}'" if expression.is_a? String
432
- "action.meta.info['#{name}'].#{selector.join('.')} = #{expression}"
433
- end
434
-
435
- def ng_call name, *args
436
- # TODO
437
- end
438
- end
439
-
440
- module MetaPanelSupport
441
- def pre_run
442
- modal_action true
443
- super
444
- end
445
-
446
- def post_run
447
- super
448
- if @meta[:panel]
449
- panel_panel_template 'menu_m' unless panel[:panel_template] == false
450
- # modal_action false if panel[:panel_template] == false
451
- panel_class '' unless panel[:class]
452
- panel_footer true if panel[:footer] != false && menu?(:panel_menu)
453
- end
454
- end
455
-
456
- def panel
457
- @meta[:panel] ||= {}
458
- end
459
-
460
- def modal_action modal = true
461
- panel[:modal_action] = modal
462
- end
463
-
464
- def panel_template tmpl
465
- panel[:template] = tmpl
466
- end
467
-
468
- def panel_panel_template tmpl
469
- panel[:panel_template] = tmpl
470
- end
471
-
472
- def panel_class cls
473
- panel[:class] = cls
474
- end
475
-
476
- def panel_title tle
477
- panel[:title] = tle
478
- end
479
-
480
- def panel_footer ftr
481
- panel[:footer] = ftr
482
- end
483
- end
484
-
485
- class MenuMeta < Meta
486
- include MetaMenuSupport
487
- meta_type :menu
488
-
489
- def invoke handler
490
- {}
491
- end
492
- end
493
-
494
- class ConfirmMeta < Meta
495
- include MetaPanelSupport, MetaMenuSupport
496
- meta_type :confirm
497
-
498
- def message msg
499
- @meta[:message] = msg
500
- end
501
-
502
- def pre_run
503
- super
504
- panel_template 'scaffold/message'
505
- panel_title LOCS[:confirmation]
506
- panel_class 'modal-default'
507
-
508
- menu :panel_menu do
509
- option :approve, icon: "ok", loc: LOCS[:ok], disabled: 'action.action_pending()'
510
- option :cancel, icon: "remove"
511
- end
512
- end
513
-
514
- def invoke handler
515
- params = handler.request.params
516
- # params.merge({arguments: params.keys})
517
- end
518
- end
519
-
520
- module MetaOnChangeSupport
521
- def on_change field, &blk
522
- action_name = :"#{field}_on_change"
523
- act = action.define_action action_name, (blk.arity > 2 ? OnChangeGetMeta : OnChangePostMeta)
524
- act.*{request &blk}
525
-
526
- info! field, remote_onchange: action_name
527
- info! field, remote_onchange_record: :true if blk.arity > 2
528
- end
529
-
530
- class OnChangeMeta < Meta
531
- include MetaAPISupport, MetaAngularSupport
532
-
533
- def request_meta_proc_params handler
534
- if handler.request.post?
535
- json = handler.post_to_json
536
- [json[:value], json[:record]]
537
- else
538
- params = handler.request.params
539
- [params["value"], params["record"]]
540
- end
541
- end
542
-
543
- def invoke handler
544
- {}
545
- end
546
- end
547
-
548
- class OnChangeGetMeta < OnChangeMeta
549
- meta_type :on_change
550
-
551
- def request_meta_proc_params handler
552
- params = handler.request.params
553
- [params["value"], params["record"]]
554
- end
555
- end
556
-
557
- class OnChangePostMeta < OnChangeMeta
558
- http_method :post
559
- meta_type :on_change
560
-
561
- def request_meta_proc_params handler
562
- json = handler.post_to_json
563
- [json[:value], json[:record]]
564
- end
565
- end
566
- end
567
-
568
- module MetaListSupport
569
- include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport, MetaOnChangeSupport
570
- attr_reader :filters, :orders
571
-
572
- def pre_run
573
- super
574
- config.merge!(per_page: 10, use_count: false, selectable: true) # search_active: false,
575
-
576
- panel_template 'scaffold/list'
577
- panel_panel_template 'panels/menu_m' unless action.parent.*.assets[:model]
578
- search_template 'scaffold/search'
579
- panel_title "#{:list.icon} #{LOCS[assets[:model].name.to_sym]}"
580
- loc! LOCS[:list_locs]
581
- menu :menu do
582
- properties break: 2, group_class: "btn-group-xs"
583
- option :search_toggle, icon: "search", show: "action.meta.search_fields", active: "action.ui_state.search_active", button_loc: false
584
- # divider
585
- option :refresh, icon: "refresh", button_loc: false
586
- option :default_order, icon: "signal", button_loc: false
587
- divider
588
- option :debug_info, icon: "list-alt" do
589
- option :show_meta, icon: "eye-open"
590
- end
591
- end
592
-
593
- menu :item_menu do
594
- properties break: 1, group_class: "btn-group-xs"
595
- end
596
-
597
- @meta[:state] = [:query, :ui_state]
598
- end
599
-
600
- def field_tabs hash
601
- super
602
- search_template 'scaffold/search_tabs'
603
- end
604
-
605
- def select_toggle_menu
606
- m = menu :menu
607
- unless m.option_index(:select_toggle, false)
608
- m.option_after :default_order, :select_toggle, icon: "check", enabled: "action.meta.config.selectable", active: "action.selection", button_loc: false
609
- end
610
- end
611
-
612
- def post_run
613
- unless panel[:class]
614
- panel_class case @meta[:fields].size
615
- when 1..3; ''
616
- when 4..6; 'modal-large'
617
- else; 'modal-huge'
618
- end
619
- end
620
-
621
- super
622
- @meta[:primary_fields] = assets[:model].primary_keys
623
- end
624
-
625
- # def find_renderer type_info
626
- # renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
627
- # raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
628
- # renderer.(self, type_info)
629
- # end
630
-
631
- def post_process
632
- if fields = @meta[:search_fields]
633
- fields = fields - static.get[:search_fields] if dynamic?
634
-
635
- decorate(fields)
636
- fields.each do |name|
637
- type_info = get_type_info(name)
638
-
639
- # render = info[name][:render]
640
- # if not render
641
- # info[name][:render] = find_renderer(type_info)
642
- # else
643
- # info[name][:render].merge!(find_renderer(type_info)){|key, v1, v2|v1}
644
- # end
645
-
646
- info[name][:render] ||= begin # set before :fields
647
- renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
648
- raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
649
- renderer.(self, type_info)
650
- end
651
-
652
- proc = SearchRendererPostProcessors[type_info[:type]] || ListRendererPostProcessors[type_info[:type]] # ?
653
- proc.(self, name, type_info) if proc
654
- end
655
- end
656
-
657
- if fields = @meta[:fields]
658
- fields = fields - static.get[:fields] if dynamic?
659
-
660
- decorate(fields)
661
- fields.each do |name|
662
- type_info = get_type_info(name)
663
- proc = ListRendererPostProcessors[type_info[:type]]
664
- proc.(self, name, type_info) if proc
665
- end
666
- end
667
-
668
- super
669
- end
670
-
671
- def search_template template
672
- panel[:search_template] = template
673
- end
674
-
675
- def sortable *flds
676
- flds = @meta[:fields] if flds.empty?
677
- info! *flds, sort: true
678
- end
679
-
680
- def search_live *flds
681
- flds = @meta[:search_fields] if flds.empty?
682
- info! *flds, search_live: true
683
- end
684
-
685
- def searchable *flds
686
- @meta.delete(:tabs)
687
- @meta[:search_fields] = *flds
688
- end
689
-
690
- def searchable_tabs tabs
691
- searchable *tabs.map{|name, fields|fields}.flatten
692
- field_tabs tabs
693
- end
694
-
695
- def template
696
- SearchTemplates
697
- end
698
-
699
- def filter name, &blk
700
- (@filters ||= {})[name] = blk
701
- end
702
-
703
- def filter_case_insensitive name
704
- raise E2Error.new("Field '#{name}' needs to be a string one") unless get_type_info(name)[:otype] == :string
705
- filter(name){|query, hash, handler| query.where(name.ilike("%#{hash[name]}%")) }
706
- end
707
-
708
- def order name, &blk
709
- (@orders ||= {})[name] = blk
710
- end
711
- end
712
-
713
- module MetaApproveSupport
714
- include MetaModelSupport
715
- attr_reader :validations
716
-
717
- def self.included meta
718
- meta.http_method :post
719
- end
720
-
721
- def validate_fields *fields
722
- if fields.empty?
723
- @validate_fields
724
- else
725
- @validate_fields = assets[:model].type_info.keys & (fields + assets[:model].primary_keys).uniq
726
- end
727
- end
728
-
729
- def before_approve handler, record
730
- end
731
-
732
- def after_approve handler, record
733
- end
734
-
735
- def validate_and_approve handler, record, json
736
- static.before_approve(handler, record)
737
- record.valid?
738
- validate_record(handler, record)
739
- if record.errors.empty?
740
- static.after_approve(handler, record)
741
- true
742
- else
743
- false
744
- end
745
- end
746
-
747
- def allocate_record handler, json
748
- model = assets[:model]
749
- json_rec = json[:record]
750
- handler.permit json_rec.is_a?(Hash)
751
- val_fields = (dynamic? ? static.validate_fields : @validate_fields) || model.type_info.keys
752
- handler.permit (json_rec.keys - val_fields).empty?
753
-
754
- record = model.call(json_rec)
755
- record.validate_fields = val_fields
756
- record
757
- end
758
-
759
- def record handler, record
760
- {errors: nil}
761
- end
762
-
763
- def invoke handler
764
- json = handler.post_to_json
765
- record = allocate_record(handler, json)
766
- validate_and_approve(handler, record, json) ? static.record(handler, record) : {record!: record.to_hash, errors!: record.errors}
767
- end
768
-
769
- def validate name, &blk
770
- (@validations ||= {})[name] = blk
771
- end
772
-
773
- def validate_record handler, record
774
- @validations.each do |name, val|
775
- unless record.errors[name]
776
- result = val.(record, handler)
777
- record.errors.add(name, result) if result
778
- end
779
- end if @validations
780
- end
781
-
782
- def post_run
783
- super
784
- validate_fields *action.parent.*.get[:fields] unless validate_fields
785
- end
786
- end
787
-
788
- module MetaFormSupport
789
- include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport, MetaAngularSupport, MetaOnChangeSupport
790
-
791
- def field_template template
792
- panel[:field_template] = template
793
- end
794
-
795
- def pre_run
796
- super
797
- panel_template 'scaffold/form'
798
- field_template 'scaffold/fields'
799
- panel_class 'modal-large'
800
-
801
- menu :panel_menu do
802
- option :approve, icon: "ok", disabled: 'action.action_pending()' # text: true,
803
- option :cancel, icon: "remove" # text: true,
804
- end
805
- # modal_action false
806
- end
807
-
808
- def field_tabs hash
809
- super
810
- panel_template 'scaffold/form_tabs'
811
- end
812
-
813
- def record handler, record
814
- end
815
-
816
- def post_process
817
- if fields = @meta[:fields]
818
- fields = fields - static.get[:fields] if dynamic?
819
-
820
- decorate(fields)
821
-
822
- fields.each do |name|
823
- # type_info = model.type_info.fetch(name)
824
- type_info = get_type_info(name)
825
-
826
- info[name][:render] ||= begin
827
- renderer = DefaultFormRenderers[type_info[:type]] # .merge(default: true)
828
- raise E2Error.new("No form renderer found for field '#{type_info[:name]}' of type '#{type_info[:type]}'") unless renderer
829
- renderer.(self, type_info)
830
- end
831
-
832
- proc = FormRendererPostProcessors[type_info[:type]]
833
- proc.(self, name, type_info) if proc
834
- end
835
-
836
- assoc = assets[:assoc]
837
- if assoc && assoc[:type] == :one_to_many
838
- # fields.select{|f| assoc[:keys].include? f}.each do |key|
839
- # # hide_fields(key) if self[:info, key, :hidden] == nil
840
- # info! key, disabled: true
841
- # end
842
- assoc[:keys].each do |key|
843
- info! key, disabled: true if fields.include? key
844
- end
845
- end
846
- end
847
-
848
- super
849
- end
850
-
851
- def post_run
852
- super
853
- @meta[:primary_fields] = assets[:model].primary_keys
854
- end
855
-
856
- def template
857
- Templates
858
- end
859
-
860
- def hr_after field, message = '-'
861
- info! field, hr: message
862
- end
863
- end
864
-
865
- module MetaCreateSupport
866
- include MetaFormSupport
867
-
868
- def self.included meta
869
- meta.meta_type :create
870
- end
871
-
872
- def pre_run
873
- super
874
- panel_title LOCS[:create_title]
875
- action.parent.*.menu(:menu).option_at 0, action.name, icon: "plus-sign", button_loc: false
876
-
877
- hide_pk unless assets[:model].natural_key
878
- end
879
-
880
- def record handler, record
881
- create_record(handler, record)
882
- end
883
-
884
- def create_record handler, record
885
- end
886
-
887
- def invoke handler
888
- record = {}
889
- if assoc = assets[:assoc]
890
- case assoc[:type]
891
- when :one_to_many
892
- handler.permit parent = handler.params[:parent_id]
893
- assoc[:keys].zip(split_keys(parent)).each{|key, val| record[key] = val}
894
- end
895
- end
896
- static.record(handler, record)
897
- {record: record, new: true}
898
- end
899
- end
900
-
901
- module MetaModifySupport
902
- include MetaFormSupport
903
-
904
- def self.included meta
905
- meta.meta_type :modify
906
- end
907
-
908
- def pre_run
909
- super
910
- panel_title LOCS[:modify_title]
911
- action.parent.*.menu(:item_menu).option action.name, icon: "pencil", button_loc: false
912
- end
913
-
914
- def record handler, record
915
- modify_record(handler, record)
916
- end
917
-
918
- def modify_record handler, record
919
- end
920
-
921
- def invoke handler
922
- handler.permit id = handler.params[:id]
923
- record = find_record(handler, id)
924
-
925
- if record
926
- static.record(handler, record)
927
- {record: record}
928
- else
929
- handler.halt_not_found LOCS[:no_entry]
930
- end
931
- end
932
-
933
- def post_run
934
- super
935
- assets[:model].primary_keys.each do |key| # pre_run ?
936
- info! key, disabled: true
937
- end
938
- end
939
- end
940
-
941
- module MetaViewSupport
942
- include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport
943
-
944
- def self.included meta
945
- meta.meta_type :view
946
- end
947
-
948
- def pre_run
949
- super
950
- panel_template 'scaffold/view'
951
- panel_title LOCS[:view_title]
952
- panel[:backdrop] = true
953
-
954
- menu(:panel_menu).option :cancel, icon: "remove"
955
- action.parent.*.menu(:item_menu).option action.name, icon: "file", button_loc: false
956
- end
957
-
958
- def field_tabs hash
959
- super
960
- panel_template 'scaffold/view_tabs'
961
- end
962
-
963
- def record handler, record
964
- end
965
-
966
- def invoke handler
967
- handler.permit id = handler.params[:id]
968
- record = find_record(handler, id)
969
- if record
970
- static.record(handler, record)
971
- {record: record}
972
- else
973
- handler.halt_not_found LOCS[:no_entry]
974
- end
975
- end
976
-
977
- def post_process
978
- if fields = @meta[:fields]
979
- fields = fields - static.get[:fields] if dynamic?
980
-
981
- decorate(fields)
982
- fields.each do |name|
983
- type_info = get_type_info(name)
984
- proc = ListRendererPostProcessors[type_info[:type]]
985
- proc.(self, name, type_info) if proc
986
- end
987
- end
988
-
989
- super
990
- end
991
- end
992
-
993
- module MetaDeleteSupport
994
- include MetaModelSupport
995
-
996
- def self.included meta
997
- meta.http_method :delete
998
- meta.meta_type :delete
999
- end
1000
-
1001
- def pre_run
1002
- super
1003
- action.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
1004
- end
1005
- end
1006
-
1007
- module MetaBulkDeleteSupport
1008
- include MetaModelSupport
1009
-
1010
- def self.included meta
1011
- meta.http_method :delete
1012
- meta.meta_type :bulk_delete
1013
- end
1014
-
1015
- def pre_run
1016
- super
1017
- action.parent.parent.*.select_toggle_menu
1018
- action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
1019
- end
1020
- end
1021
-
1022
- (FormRendererPostProcessors ||= {}).merge!(
1023
- boolean: lambda{|meta, field, info|
1024
- meta.info[field][:render].merge! true_value: info[:true_value], false_value: info[:false_value]
1025
- meta.info[field][:dont_strip] = info[:dont_strip] if info[:dont_strip]
1026
- },
1027
- date: lambda{|meta, field, info|
1028
- meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format]
1029
- if date_to = info[:other_date]
1030
- meta.info[field][:render].merge! other_date: date_to #, format: info[:format], model_format: info[:model_format]
1031
- meta.hide_fields date_to
1032
- elsif time = info[:other_time]
1033
- meta.info[field][:render].merge! other_time: time
1034
- meta.hide_fields time
1035
- end
1036
- },
1037
- time: lambda{|meta, field, info|
1038
- meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format]
1039
- },
1040
- decimal_date: lambda{|meta, field, info|
1041
- FormRendererPostProcessors[:date].(meta, field, info)
1042
- meta.info! field, type: :decimal_date
1043
- },
1044
- decimal_time: lambda{|meta, field, info|
1045
- FormRendererPostProcessors[:time].(meta, field, info)
1046
- meta.info! field, type: :decimal_time
1047
- },
1048
- datetime: lambda{|meta, field, info|
1049
- meta.info[field][:render].merge! date_format: info[:date_format], time_format: info[:time_format], date_model_format: info[:date_model_format], time_model_format: info[:time_model_format]
1050
- },
1051
- currency: lambda{|meta, field, info|
1052
- meta.info[field][:render].merge! symbol: info[:symbol]
1053
- },
1054
- # date_range: lambda{|meta, field, info|
1055
- # meta.info[field][:render].merge! other_date: info[:other_date], format: info[:format], model_format: info[:model_format]
1056
- # meta.hide_fields info[:other_date]
1057
- # meta.info[field][:decimal_date] = true if info[:validations][:decimal_date]
1058
- # },
1059
- list_select: lambda{|meta, field, info|
1060
- meta.info[field][:render].merge! list: info[:list]
1061
- },
1062
- many_to_one: lambda{|meta, field, info|
1063
- field_info = meta.info[field]
1064
- field_info[:assoc] = :"#{info[:assoc_name]}!"
1065
- field_info[:fields] = info[:keys]
1066
- field_info[:type] = info[:otype]
1067
-
1068
- (info[:keys] - [field]).each do |of|
1069
- f_info = meta.info.fetch(of)
1070
- f_info[:hidden] = true
1071
- f_info[:type] = meta.assets[:model].type_info[of].fetch(:otype)
1072
- end
1073
- },
1074
- file_store: lambda{|meta, field, info|
1075
- meta.info[field][:render].merge! multiple: info[:multiple]
1076
- },
1077
- star_to_many_field: lambda{|meta, field, info|
1078
- field_info = meta.info[field]
1079
- field_info[:assoc] = :"#{info[:assoc_name]}!"
1080
- }
1081
- )
1082
-
1083
- (ListRendererPostProcessors ||= {}).merge!(
1084
- boolean: lambda{|meta, field, info|
1085
- meta.info! field, type: :boolean # move to meta ?
1086
- meta.info[field][:render] ||= {}
1087
- meta.info[field][:render].merge! true_value: info[:true_value], false_value: info[:false_value]
1088
- },
1089
- list_select: lambda{|meta, field, info|
1090
- meta.info! field, type: :list_select
1091
- meta.info[field][:render] ||= {}
1092
- meta.info[field][:render].merge! list: info[:list]
1093
- },
1094
- datetime: lambda{|meta, field, info|
1095
- meta.info! field, type: :datetime
1096
- },
1097
- decimal_date: lambda{|meta, field, info|
1098
- meta.info! field, type: :decimal_date
1099
- },
1100
- decimal_time: lambda{|meta, field, info|
1101
- meta.info! field, type: :decimal_time
1102
- },
1103
- # date_range: lambda{|meta, field, info|
1104
- # meta.info[field][:type] = :decimal_date if info[:validations][:decimal_date] # ? :decimal_date : :date
1105
- # }
1106
- )
1107
-
1108
- (SearchRendererPostProcessors ||= {}).merge!(
1109
- many_to_one: lambda{|meta, field, info|
1110
- model = meta.assets[:model]
1111
- if model.type_info[field]
1112
- keys = info[:keys]
1113
- else
1114
- meta.check_static_meta
1115
- model = model.many_to_one_associations[field[/^\w+?(?=__)/].to_sym].associated_class
1116
- keys = info[:keys].map{|k| :"#{model.table_name}__#{k}"}
1117
- end
1118
-
1119
- field_info = meta.info[field]
1120
- field_info[:assoc] = :"#{info[:assoc_name]}!"
1121
- field_info[:fields] = keys
1122
- field_info[:type] = info[:otype]
1123
-
1124
- (keys - [field]).each do |of|
1125
- f_info = meta.info[of]
1126
- raise E2Error.new("Missing searchable field: '#{of}' in model '#{meta.assets[:model]}'") unless f_info
1127
- f_info[:hidden_search] = true
1128
- f_info[:type] = model.type_info[of].fetch(:otype)
1129
- end
1130
- },
1131
- date: lambda{|meta, field, info|
1132
- meta.info[field][:render] ||= {}
1133
- meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format] # Model::DEFAULT_DATE_FORMAT
1134
- },
1135
- decimal_date: lambda{|meta, field, info|
1136
- SearchRendererPostProcessors[:date].(meta, field, info)
1137
- }
1138
- )
1139
-
1140
- (DefaultFormRenderers ||= {}).merge!(
1141
- date: lambda{|meta, info|
1142
- info[:other_date] ? Templates.date_range : (info[:other_time] ? Templates.date_time : Templates.date_picker)
1143
-
1144
- },
1145
- time: lambda{|meta, info| Templates.time_picker},
1146
- datetime: lambda{|meta, info| Templates.datetime_picker},
1147
- file_store: lambda{|meta, info| Templates.file_store},
1148
- blob: lambda{|meta, info| Templates.blob}, # !!!
1149
- blob_store: lambda{|meta, info| Templates.blob},
1150
- foreign_blob_store: lambda{|meta, info| Templates.blob},
1151
- string: lambda{|meta, info| Templates.input_text(info[:length])},
1152
- text: lambda{|meta, info| Templates.text},
1153
- integer: lambda{|meta, info| Templates.integer},
1154
- decimal: lambda{|meta, info| Templates.decimal},
1155
- decimal_date: lambda{|meta, info| DefaultFormRenderers[:date].(meta, info)},
1156
- decimal_time: lambda{|meta, info| Templates.time_picker},
1157
- email: lambda{|meta, info| Templates.email(info[:length])},
1158
- password: lambda{|meta, info| Templates.password(info[:length])},
1159
- # date_range: lambda{|meta, info| Templates.date_range},
1160
- boolean: lambda{|meta, info| Templates.checkbox_buttons(optional: !info[:required])},
1161
- currency: lambda{|meta, info| Templates.currency},
1162
- list_select: lambda{|meta, info|
1163
- length = info[:list].length
1164
- if length <= 3
1165
- Templates.list_buttons(optional: !info[:required])
1166
- elsif length <= 15
1167
- max_length = info[:list].max_by{|a|a.last.length}.last.length
1168
- Templates.list_bsselect(max_length, optional: !info[:required])
1169
- else
1170
- max_length = info[:list].max_by{|a|a.last.length}.last.length
1171
- Templates.list_select(max_length, optional: !info[:required])
1172
- end
1173
- },
1174
- star_to_many_field: lambda{|meta, info| Templates.scaffold},
1175
- many_to_one: lambda{|meta, info|
1176
- tmpl_type = info[:decode][:form]
1177
- case
1178
- when tmpl_type[:scaffold]; Templates.scaffold_picker
1179
- when tmpl_type[:list]; Templates.bsselect_picker
1180
- when tmpl_type[:typeahead];Templates.typeahead_picker
1181
- else
1182
- raise E2Error.new("Unknown decode type #{tmpl_type}")
1183
- end
1184
- }, # required/opt
1185
- )
1186
-
1187
- (DefaultSearchRenderers ||= {}).merge!(
1188
- date: lambda{|meta, info| SearchTemplates.date_range},
1189
- decimal_date: lambda{|meta, info| SearchTemplates.date_range},
1190
- integer: lambda{|meta, info| SearchTemplates.integer_range},
1191
- string: lambda{|meta, info| SearchTemplates.input_text},
1192
- boolean: lambda{|meta, info| SearchTemplates.checkbox_buttons},
1193
- list_select: lambda{|meta, info|
1194
- length = info[:list].length
1195
- if length <= 3
1196
- SearchTemplates.list_buttons
1197
- elsif length <= 15
1198
- # max_length = info[:list].max_by{|a|a.last.length}.last.length
1199
- SearchTemplates.list_bsselect(multiple: info[:multiple])
1200
- else
1201
- # max_length = info[:list].max_by{|a|a.last.length}.last.length
1202
- SearchTemplates.list_select
1203
- end
1204
- },
1205
- many_to_one: lambda{|meta, info|
1206
- tmpl_type = info[:decode][:search]
1207
- case
1208
- when tmpl_type[:scaffold]; SearchTemplates.scaffold_picker(multiple: tmpl_type[:multiple])
1209
- when tmpl_type[:list]; SearchTemplates.bsselect_picker(multiple: tmpl_type[:multiple])
1210
- when tmpl_type[:typeahead];SearchTemplates.typeahead_picker
1211
- else
1212
- raise E2Error.new("Unknown decode type #{tmpl_type}")
1213
- end
1214
- }
1215
- )
1216
- end