engine2 1.0.4 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile +0 -0
- data/Rakefile +4 -4
- data/app/{engine2actions.coffee → actions.coffee} +341 -215
- data/app/app.coffee +0 -0
- data/app/app.css +17 -0
- data/app/engine2.coffee +158 -208
- data/app/modal.coffee +138 -0
- data/bower.json +4 -2
- data/conf/message.yaml +5 -0
- data/conf/message_pl.yaml +7 -2
- data/config.coffee +24 -12
- data/engine2.gemspec +8 -8
- data/lib/engine2.rb +12 -10
- data/lib/engine2/action.rb +1338 -133
- data/lib/engine2/action/array.rb +189 -0
- data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
- data/lib/engine2/action/delete.rb +64 -0
- data/lib/engine2/action/form.rb +16 -0
- data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
- data/lib/engine2/action/link.rb +117 -0
- data/lib/engine2/action/list.rb +333 -0
- data/lib/engine2/action/save.rb +28 -0
- data/lib/engine2/action/view.rb +8 -0
- data/lib/engine2/action_node.rb +221 -0
- data/lib/engine2/core.rb +175 -87
- data/lib/engine2/handler.rb +14 -13
- data/lib/engine2/model.rb +85 -43
- data/lib/engine2/models/Files.rb +4 -1
- data/lib/engine2/models/UserInfo.rb +6 -3
- data/lib/engine2/post_bootstrap.rb +4 -3
- data/lib/engine2/pre_bootstrap.rb +10 -6
- data/lib/engine2/scheme.rb +107 -65
- data/lib/engine2/templates.rb +41 -6
- data/lib/engine2/type_info.rb +51 -23
- data/lib/engine2/version.rb +2 -1
- data/package.json +22 -16
- data/public/favicon.ico +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/views/fields/blob.slim +1 -1
- data/views/fields/bs_select.slim +2 -2
- data/views/fields/bsselect_picker.slim +4 -4
- data/views/fields/bsselect_picker_opt.slim +5 -5
- data/views/fields/checkbox.slim +4 -4
- data/views/fields/checkbox_button.slim +6 -0
- data/views/fields/checkbox_buttons.slim +3 -3
- data/views/fields/checkbox_buttons_opt.slim +3 -3
- data/views/fields/currency.slim +2 -2
- data/views/fields/date.slim +4 -4
- data/views/fields/date_range.slim +9 -9
- data/views/fields/date_time.slim +9 -9
- data/views/fields/datetime.slim +8 -8
- data/views/fields/decimal.slim +1 -1
- data/views/fields/decimal_date.slim +3 -3
- data/views/fields/decimal_time.slim +3 -3
- data/views/fields/email.slim +3 -3
- data/views/fields/file_store.slim +11 -11
- data/views/fields/input_text.slim +4 -4
- data/views/fields/integer.slim +1 -1
- data/views/fields/list_bsmselect.slim +20 -0
- data/views/fields/list_bsselect.slim +5 -5
- data/views/fields/list_bsselect_opt.slim +6 -6
- data/views/fields/list_buttons.slim +1 -1
- data/views/fields/list_buttons_opt.slim +2 -2
- data/views/fields/list_mbuttons.slim +9 -0
- data/views/fields/list_mbuttons_opt.slim +11 -0
- data/views/fields/list_mselect.slim +12 -0
- data/views/fields/list_select.slim +4 -4
- data/views/fields/list_select_opt.slim +5 -5
- data/views/fields/password.slim +4 -4
- data/views/fields/radio_checkbox.slim +3 -3
- data/views/fields/scaffold.slim +2 -2
- data/views/fields/scaffold_picker.slim +5 -5
- data/views/fields/select_picker.slim +3 -3
- data/views/fields/select_picker_opt.slim +4 -4
- data/views/fields/text_area.slim +4 -3
- data/views/fields/time.slim +5 -4
- data/views/fields/typeahead_picker.slim +12 -9
- data/views/index.slim +3 -3
- data/views/infra/index.slim +0 -0
- data/views/infra/inspect.slim +41 -10
- data/views/modals/close_m.slim +0 -0
- data/views/modals/confirm_m.slim +0 -0
- data/views/modals/empty_m.slim +0 -0
- data/views/modals/menu_m.slim +1 -1
- data/views/modals/yes_no_m.slim +0 -0
- data/views/panels/menu_m.slim +1 -1
- data/views/scaffold/confirm.slim +0 -0
- data/views/scaffold/fields.slim +6 -4
- data/views/scaffold/form.slim +1 -1
- data/views/scaffold/form_collapse.slim +4 -3
- data/views/scaffold/form_tabs.slim +3 -2
- data/views/scaffold/list.slim +0 -0
- data/views/scaffold/message.slim +0 -0
- data/views/scaffold/search.slim +4 -4
- data/views/scaffold/search_collapse.slim +8 -7
- data/views/scaffold/search_tabs.slim +6 -5
- data/views/scaffold/view.slim +2 -2
- data/views/scaffold/view_collapse.slim +5 -4
- data/views/scaffold/view_tabs.slim +4 -3
- data/views/search_fields/bsmselect_picker.slim +4 -4
- data/views/search_fields/bsselect_picker.slim +4 -4
- data/views/search_fields/checkbox.slim +3 -3
- data/views/search_fields/checkbox2.slim +5 -5
- data/views/search_fields/checkbox_buttons.slim +3 -3
- data/views/search_fields/date.slim +20 -0
- data/views/search_fields/date_range.slim +8 -8
- data/views/search_fields/decimal_date_range.slim +5 -5
- data/views/search_fields/input_text.slim +2 -2
- data/views/search_fields/integer.slim +1 -1
- data/views/search_fields/integer_range.slim +2 -2
- data/views/search_fields/list_bsmselect.slim +4 -4
- data/views/search_fields/list_bsselect.slim +4 -4
- data/views/search_fields/list_buttons.slim +2 -2
- data/views/search_fields/list_mbuttons.slim +12 -0
- data/views/search_fields/list_select.slim +3 -3
- data/views/search_fields/scaffold_picker.slim +2 -2
- data/views/search_fields/select_picker.slim +3 -3
- data/views/search_fields/typeahead_picker.slim +8 -7
- metadata +53 -48
- data/lib/engine2/meta.rb +0 -1216
- data/lib/engine2/meta/array_meta.rb +0 -82
- data/lib/engine2/meta/delete_meta.rb +0 -60
- data/lib/engine2/meta/form_meta.rb +0 -15
- data/lib/engine2/meta/link_meta.rb +0 -134
- data/lib/engine2/meta/list_meta.rb +0 -281
- data/lib/engine2/meta/save_meta.rb +0 -50
- data/lib/engine2/meta/view_meta.rb +0 -7
- data/public/__sinatra__/404.png +0 -0
- data/public/__sinatra__/500.png +0 -0
data/app/modal.coffee
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
angular.module('Engine2')
|
|
3
|
+
.provider '$e2Modal', ->
|
|
4
|
+
$get: ($rootScope, $modal, $timeout, $window, $injector) ->
|
|
5
|
+
class MManager
|
|
6
|
+
@Z_INDEX: 1050
|
|
7
|
+
@index: 0
|
|
8
|
+
constructor: () ->
|
|
9
|
+
backdrop_z_index: (num, index) -> angular.element(document.querySelectorAll('.modal-backdrop')).eq(num).css('z-index', index)
|
|
10
|
+
modal_num: (num) -> angular.element(document.querySelectorAll('.modal')).eq(num)
|
|
11
|
+
backdrop: (bdr) -> bdr ? 'static'
|
|
12
|
+
show_before: ->
|
|
13
|
+
@z_index = MManager.Z_INDEX + MManager.index * 2
|
|
14
|
+
@modal.css('z-index', @z_index + 1)
|
|
15
|
+
hide_before: ->
|
|
16
|
+
@z_index = MManager.Z_INDEX + ((MManager.index - 1) * 2)
|
|
17
|
+
show: ->
|
|
18
|
+
hide: -> angular.element($window.document.body).addClass('modal-open modal-with-am-fade') if MManager.index > 0
|
|
19
|
+
|
|
20
|
+
class DefaultMManager extends MManager
|
|
21
|
+
show_before: ->
|
|
22
|
+
super()
|
|
23
|
+
@backdrop.css('z-index', @z_index) # @backdrop_z_index(-MManager.index - 1, z_index)
|
|
24
|
+
|
|
25
|
+
class FirstMManager extends MManager
|
|
26
|
+
constructor: () ->
|
|
27
|
+
super()
|
|
28
|
+
@threshold = 2
|
|
29
|
+
backdrop: (bdr) -> if MManager.index > @threshold then false else super(bdr)
|
|
30
|
+
show_before: ->
|
|
31
|
+
super()
|
|
32
|
+
if MManager.index > @threshold
|
|
33
|
+
@modal_num((MManager.index - 1) - @threshold).css('display', 'none')
|
|
34
|
+
@backdrop_z_index(0, @z_index)
|
|
35
|
+
else
|
|
36
|
+
@backdrop.css('z-index', @z_index) # @backdrop_z_index(-MManager.index - 1, z_index)
|
|
37
|
+
|
|
38
|
+
hide_before: ->
|
|
39
|
+
super()
|
|
40
|
+
if MManager.index > @threshold
|
|
41
|
+
@backdrop_z_index(0, @z_index)
|
|
42
|
+
@modal_num((MManager.index - 1) - @threshold).css('display', 'block')
|
|
43
|
+
|
|
44
|
+
class SingleBackdropMManager extends MManager
|
|
45
|
+
backdrop: (bdr) -> if MManager.index > 0 then false else super(bdr)
|
|
46
|
+
show_before: ->
|
|
47
|
+
super()
|
|
48
|
+
@backdrop_z_index(0, @z_index)
|
|
49
|
+
hide_before: ->
|
|
50
|
+
super()
|
|
51
|
+
@backdrop_z_index(0, @z_index)
|
|
52
|
+
|
|
53
|
+
is_modal: -> MManager.index > 0
|
|
54
|
+
|
|
55
|
+
show: (action) ->
|
|
56
|
+
scope = if action.scope then action.scope().$new(true) else $rootScope.$new()
|
|
57
|
+
scope.action = action
|
|
58
|
+
manager = new SingleBackdropMManager()
|
|
59
|
+
|
|
60
|
+
scope.$on 'modal.show.before', (e, m) ->
|
|
61
|
+
e.stopPropagation()
|
|
62
|
+
manager.modal = m.$element
|
|
63
|
+
manager.backdrop = m.$backdrop
|
|
64
|
+
throw "Modal has element" if action.element?()
|
|
65
|
+
action.element = -> m.$element
|
|
66
|
+
action.panel_show?()
|
|
67
|
+
manager.show_before()
|
|
68
|
+
MManager.index++
|
|
69
|
+
|
|
70
|
+
scope.$on 'modal.show', (e, m) ->
|
|
71
|
+
e.stopPropagation()
|
|
72
|
+
manager.show()
|
|
73
|
+
action.panel_shown?()
|
|
74
|
+
|
|
75
|
+
scope.$on 'modal.hide.before', (e) ->
|
|
76
|
+
e.stopPropagation()
|
|
77
|
+
MManager.index--
|
|
78
|
+
manager.hide_before()
|
|
79
|
+
action.panel_hide?()
|
|
80
|
+
|
|
81
|
+
scope.$on 'modal.hide', (e) ->
|
|
82
|
+
e.stopPropagation()
|
|
83
|
+
manager.hide()
|
|
84
|
+
action.panel_hidden?()
|
|
85
|
+
scope.$destroy()
|
|
86
|
+
|
|
87
|
+
$injector.get('E2').fetch_panel(action.meta.panel, true).then (template) ->
|
|
88
|
+
modal = $modal
|
|
89
|
+
scope: scope
|
|
90
|
+
show: false
|
|
91
|
+
template: template
|
|
92
|
+
backdrop: manager.backdrop(action.meta.panel.backdrop)
|
|
93
|
+
animation: action.meta.panel.animation ? 'am-fade'
|
|
94
|
+
|
|
95
|
+
action.modal_hide = -> modal.$scope.$hide()
|
|
96
|
+
modal.$promise.then ->
|
|
97
|
+
modal.show()
|
|
98
|
+
modal
|
|
99
|
+
|
|
100
|
+
show_modal: (title, msg, options = {html: false, alert_class: 'alert-danger', modal_class: 'modal-large'}) ->
|
|
101
|
+
body = if options.html then msg else "<div class='alert alert-#{options.alert_class}'>#{msg}</div>"
|
|
102
|
+
clazz = if options.html then "modal-huge" else options.modal_class
|
|
103
|
+
@show meta: panel: (panel_template: "close_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
info: (title, msg, options = {alert_class: 'info', modal_class: 'modal-large'}) -> @show_modal(title, msg, options)
|
|
107
|
+
warning: (title, msg, options = {alert_class: 'warning', modal_class: 'modal-large'}) -> @show_modal(title, msg, options)
|
|
108
|
+
error: (title, msg, options = {alert_class: 'danger', modal_class: 'modal-huge'}) -> @show_modal(title, msg, options)
|
|
109
|
+
|
|
110
|
+
confirm: (title, msg, action) ->
|
|
111
|
+
body = "<div class='alert alert-warning'>#{msg}</div>"
|
|
112
|
+
clazz = "modal-large"
|
|
113
|
+
@show
|
|
114
|
+
confirm: action,
|
|
115
|
+
meta: panel: (panel_template: "confirm_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
|
|
116
|
+
|
|
117
|
+
.directive 'e2Modal', ($e2Modal) ->
|
|
118
|
+
restrict: 'E'
|
|
119
|
+
# replace: true
|
|
120
|
+
# transclude: true
|
|
121
|
+
scope: true
|
|
122
|
+
compile: (celem, cattr) ->
|
|
123
|
+
obody = celem[0].children[0]
|
|
124
|
+
celem.empty() if obody
|
|
125
|
+
(scope, elem, attrs) ->
|
|
126
|
+
scope.$on attrs.name, (ev, args) ->
|
|
127
|
+
return if ev.defaultPrevented
|
|
128
|
+
ev.preventDefault()
|
|
129
|
+
|
|
130
|
+
panel = panel_template: attrs.panelTemplate, title: attrs.title, class: attrs.clazz, footer: true
|
|
131
|
+
if obody then panel.template_string = obody.outerHTML else panel.template = attrs.template
|
|
132
|
+
action = meta: (panel: panel), scope: -> scope
|
|
133
|
+
_.assign(action, args)
|
|
134
|
+
|
|
135
|
+
modal = $e2Modal.show(action)
|
|
136
|
+
hide_off = scope.$on "#{attrs.name}_close", ->
|
|
137
|
+
hide_off()
|
|
138
|
+
modal.then (m) -> m.$scope.$hide()
|
data/bower.json
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
"name": "engine2",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"angular-strap": "^2.3.
|
|
6
|
-
"angular": "^1.5.8"
|
|
5
|
+
"angular-strap": "^2.3.12",
|
|
6
|
+
"angular": "^1.5.8",
|
|
7
|
+
"angular-websocket": "^2.0.0",
|
|
8
|
+
"angular-toastr": "^2.1.1"
|
|
7
9
|
},
|
|
8
10
|
"overrides": {
|
|
9
11
|
"angular": {
|
data/conf/message.yaml
CHANGED
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
:error: Error
|
|
9
9
|
:no_entry: Entry doesnt exist
|
|
10
10
|
:decode_selected: selected
|
|
11
|
+
:list_select_selected: Selected
|
|
11
12
|
|
|
13
|
+
:E2Files: Files
|
|
14
|
+
:files: File
|
|
12
15
|
:name: Name
|
|
13
16
|
:mime: Mime
|
|
14
17
|
:owner: Owner
|
|
15
18
|
:model: Model
|
|
16
19
|
:field: Field
|
|
20
|
+
:uploaded: Uploaded
|
|
17
21
|
:updated: Updated
|
|
18
22
|
|
|
19
23
|
:ok: Ok
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
:save: Save
|
|
52
56
|
:approve: Approve
|
|
53
57
|
:cancel: Cancel
|
|
58
|
+
:close: Close
|
|
54
59
|
|
|
55
60
|
:inspect_modal: Inspect
|
|
56
61
|
|
data/conf/message_pl.yaml
CHANGED
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
:error: Błąd
|
|
9
9
|
:no_entry: Wpis nie istnieje
|
|
10
10
|
:decode_selected: wybranych
|
|
11
|
+
:list_select_selected: Wybranych
|
|
11
12
|
|
|
13
|
+
:E2Files: Pliki
|
|
14
|
+
:files: Plik
|
|
12
15
|
:name: Nazwa
|
|
13
16
|
:mime: Mime
|
|
14
17
|
:owner: Właściciel
|
|
15
18
|
:model: Model
|
|
16
19
|
:field: Pole
|
|
20
|
+
:uploaded: Wysłany
|
|
17
21
|
:updated: Aktualizowany
|
|
18
22
|
|
|
19
23
|
:ok: Ok
|
|
@@ -30,12 +34,12 @@
|
|
|
30
34
|
:select_toggle: Zaznacz
|
|
31
35
|
:confirm_delete: Usuń
|
|
32
36
|
:confirm_delete_title: <span class='glyphicon glyphicon-trash'></span> Potwierdzenie
|
|
33
|
-
:confirm_bulk_delete: Usuń
|
|
37
|
+
:confirm_bulk_delete: Usuń zaznaczone
|
|
34
38
|
:delete_restricted: Blokujące relacje
|
|
35
39
|
:confirm_bulk_delete_title: <span class='glyphicon glyphicon-trash'></span> Potwierdzenie
|
|
36
40
|
:confirm_unlink: Odłącz
|
|
37
41
|
:confirm_unlink_title: <span class='glyphicon glyphicon-minus'></span> Potwierdzenie
|
|
38
|
-
:confirm_bulk_unlink: Odłącz
|
|
42
|
+
:confirm_bulk_unlink: Odłącz zaznaczone
|
|
39
43
|
:confirm_bulk_unlink_title: <span class='glyphicon glyphicon-minus'></span> Potwierdzenie
|
|
40
44
|
:debug_info: Debug info
|
|
41
45
|
:show_meta: Metainfo
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
:save: Zapisz
|
|
52
56
|
:approve: Zatwierdź
|
|
53
57
|
:cancel: Anuluj
|
|
58
|
+
:close: Zamknij
|
|
54
59
|
|
|
55
60
|
:inspect_modal: Inspekcja
|
|
56
61
|
|
data/config.coffee
CHANGED
|
@@ -9,11 +9,13 @@ exports.config =
|
|
|
9
9
|
"bootstrap-additions": ["dist/bootstrap-additions.css"]
|
|
10
10
|
"angular-motion": ["dist/angular-motion.css"]
|
|
11
11
|
"angular-ui-tree": ["dist/angular-ui-tree.css"]
|
|
12
|
-
"
|
|
12
|
+
"fork-awesome": ["css/fork-awesome.css"]
|
|
13
|
+
# "ui-select": ["dist/select.css"]
|
|
13
14
|
|
|
14
15
|
modules:
|
|
15
16
|
definition: 'commonjs'
|
|
16
17
|
wrapper: false
|
|
18
|
+
nameCleaner: (path) -> path
|
|
17
19
|
|
|
18
20
|
paths:
|
|
19
21
|
public: 'public'
|
|
@@ -22,18 +24,23 @@ exports.config =
|
|
|
22
24
|
files:
|
|
23
25
|
javascripts:
|
|
24
26
|
joinTo:
|
|
25
|
-
'engine2vendor.js': /^node_modules|bower_components/
|
|
26
|
-
'engine2.js': /^app/
|
|
27
|
-
|
|
28
|
-
stylesheets:
|
|
29
|
-
joinTo:
|
|
30
|
-
'engine2vendor.css': /^node_modules/
|
|
31
|
-
'engine2app.css': /^app/
|
|
27
|
+
'assets/engine2vendor.js': /^node_modules|bower_components/
|
|
28
|
+
'assets/engine2.js': /^app/
|
|
32
29
|
order:
|
|
33
30
|
before: [
|
|
34
|
-
/
|
|
31
|
+
"app/engine2.coffee"
|
|
35
32
|
]
|
|
36
33
|
|
|
34
|
+
stylesheets:
|
|
35
|
+
joinTo:
|
|
36
|
+
'assets/engine2vendor.css': /^(?:node_modules||bower_components)\/(?!(bootstrap\/))/
|
|
37
|
+
'assets/bootstrap.css': /^node_modules\/(bootstrap\/)/
|
|
38
|
+
'assets/engine2.css': /^app/
|
|
39
|
+
# order:
|
|
40
|
+
# before: [
|
|
41
|
+
# /bootstrap\.css$/
|
|
42
|
+
# ]
|
|
43
|
+
|
|
37
44
|
plugins:
|
|
38
45
|
on: ["ng-annotate-brunch"]
|
|
39
46
|
|
|
@@ -45,17 +52,22 @@ exports.config =
|
|
|
45
52
|
|
|
46
53
|
replacement:
|
|
47
54
|
replacements: [
|
|
48
|
-
files: [/vendor
|
|
55
|
+
files: [/vendor\.js$/]
|
|
49
56
|
match: (
|
|
50
57
|
fix = "$modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});"
|
|
51
58
|
find: fix.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1")
|
|
52
59
|
replace: "#{fix}$modal.$backdrop = backdropElement;"
|
|
53
|
-
|
|
60
|
+
)
|
|
61
|
+
# files: [/\.css$/]
|
|
62
|
+
# match: (
|
|
63
|
+
# find: "../fonts"
|
|
64
|
+
# replace: "fonts"
|
|
65
|
+
# )
|
|
54
66
|
]
|
|
55
67
|
|
|
56
68
|
copycat:
|
|
57
69
|
fonts: [
|
|
58
|
-
"node_modules/
|
|
70
|
+
"node_modules/fork-awesome/fonts"
|
|
59
71
|
"node_modules/bootstrap/fonts"
|
|
60
72
|
]
|
|
61
73
|
verbose: true
|
data/engine2.gemspec
CHANGED
|
@@ -13,19 +13,19 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.homepage = "http://none.for.now"
|
|
14
14
|
spec.license = 'MIT'
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) rescue []
|
|
17
17
|
spec.require_paths = ["lib"]
|
|
18
18
|
|
|
19
|
-
spec.add_dependency "sequel", '~>
|
|
20
|
-
spec.add_dependency "rack-contrib", '~> 1.4'
|
|
19
|
+
spec.add_dependency "sequel", '~> 5'
|
|
21
20
|
if defined? JRUBY_VERSION
|
|
22
|
-
spec.add_dependency 'jdbc-sqlite3', '~> 3.
|
|
21
|
+
spec.add_dependency 'jdbc-sqlite3', '~> 3.0'
|
|
23
22
|
else
|
|
24
23
|
spec.add_dependency 'sqlite3', '~> 1.3'
|
|
25
24
|
end
|
|
26
|
-
spec.add_dependency "sinatra", '~>
|
|
27
|
-
spec.add_dependency 'slim', '~>
|
|
25
|
+
spec.add_dependency "sinatra", '~> 2.0'
|
|
26
|
+
spec.add_dependency 'slim', '~> 4.0'
|
|
27
|
+
spec.add_dependency 'faye-websocket', '~> 0.10'
|
|
28
28
|
|
|
29
|
-
spec.add_development_dependency "bundler", "~>
|
|
30
|
-
spec.add_development_dependency "rake", "~>
|
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.00"
|
|
30
|
+
spec.add_development_dependency "rake", "~> 13"
|
|
31
31
|
end
|
data/lib/engine2.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
# frozen-string-literal: true
|
|
2
3
|
|
|
3
4
|
require 'yaml'
|
|
4
5
|
require 'logger'
|
|
@@ -7,6 +8,7 @@ require 'sinatra'
|
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'slim'
|
|
9
10
|
require 'engine2/version'
|
|
11
|
+
require 'faye/websocket'
|
|
10
12
|
|
|
11
13
|
%w[
|
|
12
14
|
core.rb
|
|
@@ -14,19 +16,19 @@ require 'engine2/version'
|
|
|
14
16
|
type_info.rb
|
|
15
17
|
model.rb
|
|
16
18
|
templates.rb
|
|
17
|
-
meta.rb
|
|
18
19
|
action.rb
|
|
20
|
+
action_node.rb
|
|
19
21
|
scheme.rb
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
action/array.rb
|
|
24
|
+
action/list.rb
|
|
25
|
+
action/view.rb
|
|
26
|
+
action/form.rb
|
|
27
|
+
action/save.rb
|
|
28
|
+
action/delete.rb
|
|
29
|
+
action/decode.rb
|
|
30
|
+
action/link.rb
|
|
31
|
+
action/infra.rb
|
|
30
32
|
].each do |f|
|
|
31
33
|
load "engine2/#{f}"
|
|
32
34
|
end
|
data/lib/engine2/action.rb
CHANGED
|
@@ -1,213 +1,1418 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Engine2
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ACCESS_FORBIDDEN ||= ->h{false}
|
|
7
|
-
attr_reader :parent, :name, :number, :actions, :recheck_access
|
|
8
|
-
attr_reader :meta_proc
|
|
5
|
+
class Action
|
|
6
|
+
attr_reader :node, :meta, :assets, :static, :invokable
|
|
9
7
|
|
|
10
8
|
class << self
|
|
11
|
-
|
|
9
|
+
def action_type at = nil
|
|
10
|
+
at ? @action_type = at : @action_type
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def http_method hm = nil
|
|
14
|
+
hm ? @http_method = hm : @http_method
|
|
15
|
+
end
|
|
12
16
|
|
|
13
|
-
def
|
|
14
|
-
|
|
17
|
+
def inherited cls
|
|
18
|
+
cls.http_method http_method
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def inherit &blk
|
|
22
|
+
cls = Class.new self do
|
|
23
|
+
action_type superclass.action_type
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
cls.instance_eval &blk if block_given?
|
|
27
|
+
cls
|
|
15
28
|
end
|
|
16
29
|
end
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@
|
|
22
|
-
@
|
|
23
|
-
@
|
|
24
|
-
@
|
|
31
|
+
http_method :get
|
|
32
|
+
|
|
33
|
+
def initialize node, assets, static = self
|
|
34
|
+
@meta = {}
|
|
35
|
+
@node = node
|
|
36
|
+
@assets = assets
|
|
37
|
+
@static = static
|
|
25
38
|
end
|
|
26
39
|
|
|
27
|
-
def
|
|
28
|
-
@
|
|
29
|
-
@meta
|
|
40
|
+
def http_method
|
|
41
|
+
@http_method # || (raise E2Error.new("No http method for action #{self.class}"))
|
|
30
42
|
end
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
def action_type
|
|
45
|
+
@action_type || (raise E2Error.new("No action_type for action #{self.class}"))
|
|
46
|
+
end
|
|
33
47
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
@access_block = blk
|
|
48
|
+
def check_static_action
|
|
49
|
+
raise E2Error.new("Static action required") if dynamic?
|
|
37
50
|
end
|
|
38
51
|
|
|
39
|
-
def
|
|
40
|
-
|
|
52
|
+
def check_anonymous_action_class name
|
|
53
|
+
raise E2Error.new("Defining method '#{name}'' for named class '#{self.class}', consider using #inherit") if self.class.name
|
|
41
54
|
end
|
|
42
55
|
|
|
43
|
-
def
|
|
44
|
-
|
|
56
|
+
def define_method name, &blk
|
|
57
|
+
check_anonymous_action_class name
|
|
58
|
+
self.class.class_eval{define_method name, &blk}
|
|
45
59
|
end
|
|
46
60
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
def define_invoke &blk
|
|
62
|
+
check_static_action
|
|
63
|
+
define_method :invoke, &blk
|
|
64
|
+
# self.class.class_eval{define_method :invoke, &blk}
|
|
51
65
|
end
|
|
52
66
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
action
|
|
67
|
+
def invoke! handler
|
|
68
|
+
if rmp = @request_action_proc
|
|
69
|
+
action = self.class.new(node, assets, self)
|
|
70
|
+
result = action.instance_exec(handler, *action.request_action_proc_params(handler), &rmp)
|
|
71
|
+
action.post_process
|
|
72
|
+
response = @requestable ? (result.is_a?(Hash) ? result : {}) : action.invoke(handler)
|
|
73
|
+
response[:meta] = action.meta
|
|
74
|
+
response
|
|
75
|
+
else
|
|
76
|
+
invoke(handler)
|
|
60
77
|
end
|
|
61
|
-
action.instance_eval(&blk) if blk
|
|
62
|
-
action.*.action_defined
|
|
63
|
-
action
|
|
64
78
|
end
|
|
65
79
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
def repeat time
|
|
81
|
+
@meta[:repeat] = time
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def arguments args
|
|
85
|
+
(@meta[:arguments] ||= {}).merge! args
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def execute command
|
|
89
|
+
(@meta[:execute] ||= []) << command
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def dynamic?
|
|
93
|
+
self != @static
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# def [] *keys
|
|
97
|
+
# @meta.path(*keys)
|
|
98
|
+
# end
|
|
99
|
+
|
|
100
|
+
# def []= *keys, value
|
|
101
|
+
# @meta.path!(*keys, value)
|
|
102
|
+
# end
|
|
103
|
+
|
|
104
|
+
def lookup *keys
|
|
105
|
+
if dynamic? # we are the request action
|
|
106
|
+
value = @meta.path(*keys)
|
|
107
|
+
value.nil? ? @static.meta.path(*keys) : value
|
|
108
|
+
# value || @static.value.path(keys)
|
|
109
|
+
else
|
|
110
|
+
@meta.path(*keys)
|
|
69
111
|
end
|
|
70
112
|
end
|
|
71
113
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
def merge *keys
|
|
115
|
+
if keys.length == 1
|
|
116
|
+
key = keys.first
|
|
117
|
+
dynamic? ? @static.meta[key].merge(@meta[key] || {}) : @meta[key]
|
|
118
|
+
else
|
|
119
|
+
dynamic? ? @static.meta.path(*keys).merge(@meta.path(*keys)) : @meta.path(*keys)
|
|
75
120
|
end
|
|
76
121
|
end
|
|
77
122
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
def freeze_action
|
|
124
|
+
hash = @meta
|
|
125
|
+
hash.freeze
|
|
126
|
+
# hash.each_pair{|k, v| freeze(v) if v.is_a? Hash}
|
|
127
|
+
freeze
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def request_action_proc_params handler
|
|
131
|
+
[]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def request &blk
|
|
135
|
+
raise E2Error.new("No block given for request action") unless blk
|
|
136
|
+
raise E2Error.new("No request block in request action allowed") if dynamic?
|
|
137
|
+
@request_action_proc = @request_action_proc ? @request_action_proc.chain_args(&blk) : blk
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def pre_run
|
|
142
|
+
@action_type = self.class.action_type
|
|
143
|
+
@http_method = self.class.http_method
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def node_defined
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def post_run
|
|
150
|
+
if respond_to? :invoke
|
|
151
|
+
@invokable = true
|
|
152
|
+
else
|
|
153
|
+
if @request_action_proc
|
|
154
|
+
@invokable = true
|
|
155
|
+
@requestable = true
|
|
82
156
|
else
|
|
83
|
-
|
|
157
|
+
@meta[:invokable] = false
|
|
84
158
|
end
|
|
85
159
|
end
|
|
160
|
+
@meta[:dynamic_meta] = true if @request_action_proc
|
|
161
|
+
post_process
|
|
86
162
|
end
|
|
87
163
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
|
|
164
|
+
def post_process
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def split_keys id
|
|
168
|
+
Sequel::split_keys(id)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def join_keys id
|
|
172
|
+
Sequel::join_keys(id)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
module ActionWebSocketSupport
|
|
177
|
+
WS_METHODS ||= Faye::WebSocket::API::TYPES.keys.map(&:to_sym)
|
|
178
|
+
WS_METHODS.each do |method|
|
|
179
|
+
define_method :"ws_#{method}" do |&blk|
|
|
180
|
+
@ws_methods[method] = blk
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def pre_run
|
|
185
|
+
super
|
|
186
|
+
@ws_methods = {}
|
|
187
|
+
@meta[:websocket] = {options: {}}
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def ws_options opts
|
|
191
|
+
@meta[:websocket][:options].merge! opts
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def ws_execute execute
|
|
195
|
+
(@meta[:websocket][:execute] ||= {}).merge! execute
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def post_run
|
|
199
|
+
super
|
|
200
|
+
@invokable = true
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def invoke! handler
|
|
204
|
+
if Faye::WebSocket.websocket?(handler.env)
|
|
205
|
+
ws = Faye::WebSocket.new(handler.env)
|
|
206
|
+
@ws_methods.each do |method, blk|
|
|
207
|
+
ws.on(method) do |evt|
|
|
208
|
+
begin
|
|
209
|
+
data = method == :message ? JSON.parse(evt.data, symbolize_names: true) : evt
|
|
210
|
+
action = self.class.new(node, assets, self)
|
|
211
|
+
result = action.instance_exec(data, ws, evt, &blk)
|
|
212
|
+
result = {} unless result.is_a?(Hash)
|
|
213
|
+
result[:meta] = action.meta
|
|
214
|
+
ws.send! result unless action.meta.empty?
|
|
215
|
+
rescue Exception => e
|
|
216
|
+
ws.send! error: {exception: e, method: method}
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
ws.rack_response
|
|
221
|
+
else
|
|
222
|
+
super
|
|
91
223
|
end
|
|
92
224
|
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class WebSocketAction < Action
|
|
228
|
+
include ActionWebSocketSupport
|
|
229
|
+
end
|
|
93
230
|
|
|
94
|
-
|
|
95
|
-
|
|
231
|
+
class InlineAction < Action
|
|
232
|
+
action_type :inline
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
class RootAction < Action
|
|
236
|
+
def initialize *args
|
|
237
|
+
super
|
|
238
|
+
@meta.merge! environment: Handler::environment, application: Engine2::SETTINGS[:name], uuid: SecureRandom.uuid,
|
|
239
|
+
key_separator: Engine2::SETTINGS[:key_separator], ws_methods: ActionWebSocketSupport::WS_METHODS
|
|
96
240
|
end
|
|
241
|
+
end
|
|
97
242
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
243
|
+
module ActionAPISupport
|
|
244
|
+
def fields field
|
|
245
|
+
(@meta[:fields] ||= {})[field.to_sym] ||= {}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def config
|
|
249
|
+
@meta[:config] ||= {}
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def fields! *fields, options
|
|
253
|
+
raise E2Error.new("No fields given to info") if fields.empty?
|
|
254
|
+
fields.each do |field|
|
|
255
|
+
fields(field).merge! options # rmerge ?
|
|
111
256
|
end
|
|
257
|
+
end
|
|
112
258
|
|
|
113
|
-
|
|
114
|
-
|
|
259
|
+
def loc! hash
|
|
260
|
+
(@meta[:loc] ||= {}).merge! hash
|
|
115
261
|
end
|
|
116
262
|
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
h
|
|
263
|
+
def decorate list
|
|
264
|
+
list.each do |f|
|
|
265
|
+
fields(f)[:loc] ||= LOCS[f.to_sym]
|
|
121
266
|
end
|
|
122
267
|
end
|
|
123
268
|
|
|
124
|
-
def
|
|
125
|
-
|
|
269
|
+
def render field, options
|
|
270
|
+
fields! field, render: options
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def hide_fields *flds
|
|
274
|
+
fields! *flds, hidden: true
|
|
126
275
|
end
|
|
127
276
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
277
|
+
def show_fields *flds
|
|
278
|
+
fields! *flds, hidden: false
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def field_filter *flds, filter
|
|
282
|
+
fields! *flds, filter: filter
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
module ActionMenuSupport
|
|
287
|
+
def menu menu_name, &blk
|
|
288
|
+
@menus ||= {}
|
|
289
|
+
@menus[menu_name] ||= ActionMenuBuilder.new(:root)
|
|
290
|
+
@menus[menu_name].instance_eval(&blk) if blk
|
|
291
|
+
@menus[menu_name]
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def menu? menu_name
|
|
295
|
+
@menus && @menus[menu_name]
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def post_process
|
|
299
|
+
super
|
|
300
|
+
if @menus && !@menus.empty?
|
|
301
|
+
@meta[:menus] = {}
|
|
302
|
+
@menus.each_pair do |name, menu|
|
|
303
|
+
@meta[:menus][name] = {entries: menu.to_a, properties: menu.properties}
|
|
304
|
+
end
|
|
132
305
|
end
|
|
133
306
|
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
module ActionModelSupport
|
|
310
|
+
def pre_run
|
|
311
|
+
if !(mdl = @assets[:model])
|
|
312
|
+
act = node
|
|
313
|
+
begin
|
|
314
|
+
act = act.parent
|
|
315
|
+
raise E2Error.new("Model not found in tree for node: #{node.name}") unless act
|
|
316
|
+
mdl = act.*.assets[:model]
|
|
317
|
+
end until mdl
|
|
134
318
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
319
|
+
if asc = @assets[:assoc]
|
|
320
|
+
@assets[:model] = asc.associated_class
|
|
321
|
+
# raise E2Error.new("Association '#{asc}' for model '#{asc[:class_name]}' not found") unless @assets[:model]
|
|
322
|
+
else
|
|
323
|
+
@assets[:model] = mdl
|
|
324
|
+
asc = act.*.assets[:assoc]
|
|
325
|
+
@assets[:assoc] = asc if asc
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# @meta[:model!] = assets[:model]
|
|
330
|
+
# @meta[:assoc!] = assets[:assoc] ? assets[:assoc][:name] : nil
|
|
331
|
+
# @meta[:action_class!] = self.class
|
|
332
|
+
super
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def hide_pk
|
|
336
|
+
hide_fields *assets[:model].primary_keys
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def show_pk
|
|
340
|
+
show_fields *assets[:model].primary_keys
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# def parent_model_name
|
|
344
|
+
# model = @assets[:model]
|
|
345
|
+
# prnt = node.parent
|
|
346
|
+
|
|
347
|
+
# while prnt && prnt.*.assets[:model] == model
|
|
348
|
+
# prnt = prnt.parent
|
|
349
|
+
# end
|
|
350
|
+
# m = prnt.*.assets[:model]
|
|
351
|
+
# m ? m.name : nil
|
|
352
|
+
# end
|
|
353
|
+
|
|
354
|
+
def node_defined
|
|
355
|
+
super
|
|
356
|
+
# p_model_name = parent_model_name
|
|
357
|
+
model = @assets[:model]
|
|
358
|
+
|
|
359
|
+
at = action_type
|
|
360
|
+
case at
|
|
361
|
+
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
|
|
362
|
+
model.many_to_one_associations.each do |assoc_name, assoc|
|
|
363
|
+
unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
|
|
364
|
+
dc = model.type_info[assoc[:keys].first][:decode]
|
|
365
|
+
node.run_scheme :decode, model, assoc_name, dc[:search]
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
case at
|
|
371
|
+
when :modify, :create
|
|
372
|
+
model.many_to_one_associations.each do |assoc_name, assoc|
|
|
373
|
+
unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
|
|
374
|
+
dc = model.type_info[assoc[:keys].first][:decode]
|
|
375
|
+
node.run_scheme :decode, model, assoc_name, dc[:form]
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
case at
|
|
381
|
+
when :list #, :star_to_many_list, :many_to_one_list # list dropdowns
|
|
382
|
+
model.one_to_many_associations.merge(model.many_to_many_associations).each do |assoc_name, assoc|
|
|
383
|
+
unless assoc[:propagate] == false
|
|
384
|
+
node.run_scheme :star_to_many, :"#{assoc_name}!", assoc
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
case at
|
|
390
|
+
when :modify, :create
|
|
391
|
+
model.type_info.each do |field, info|
|
|
392
|
+
case info[:type]
|
|
393
|
+
when :blob_store
|
|
394
|
+
node.run_scheme :blob_store, model, field
|
|
395
|
+
when :foreign_blob_store
|
|
396
|
+
node.run_scheme :foreign_blob_store, model, field
|
|
397
|
+
when :file_store
|
|
398
|
+
node.run_scheme :file_store, model, field
|
|
399
|
+
when :star_to_many_field
|
|
400
|
+
assoc = model.association_reflections[info[:assoc_name]] # info[:name] ?
|
|
401
|
+
raise E2Error.new("Association '#{info[:assoc_name]}' not found for model '#{model}'") unless assoc
|
|
402
|
+
node.run_scheme :star_to_many_field, assoc, field
|
|
142
403
|
end
|
|
143
404
|
end
|
|
144
405
|
end
|
|
145
|
-
result
|
|
146
406
|
end
|
|
147
407
|
|
|
148
|
-
def
|
|
149
|
-
|
|
408
|
+
def unsupported_association assoc
|
|
409
|
+
raise E2Error.new("Unsupported association: #{assoc}")
|
|
150
410
|
end
|
|
411
|
+
end
|
|
151
412
|
|
|
152
|
-
|
|
153
|
-
|
|
413
|
+
module ActionQuerySupport
|
|
414
|
+
def query q, &blk
|
|
415
|
+
@query = blk ? q.naked.with_row_proc(blk) : q.naked
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def post_run
|
|
419
|
+
query select(*assets[:model].columns) unless @query
|
|
420
|
+
super
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def get_query # move to query ?
|
|
424
|
+
if dynamic?
|
|
425
|
+
@query || @static.get_query
|
|
426
|
+
else
|
|
427
|
+
@query
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def find_record handler, id
|
|
432
|
+
get_query.load assets[:model].primary_keys_hash_qualified(split_keys(id))
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def select *args, use_pk: true, &blk
|
|
436
|
+
ds = assets[:model].select(*args, &blk)
|
|
437
|
+
ds = ds.ensure_primary_key if use_pk
|
|
438
|
+
ds.setup_query(@meta[:field_list] = [])
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
module ActionTabSupport
|
|
443
|
+
def select_tabs tabs, *args, &blk
|
|
444
|
+
field_tabs tabs
|
|
445
|
+
select *tabs.map{|name, fields|fields}.flatten, *args, &blk
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def field_tabs hash
|
|
449
|
+
@meta[:tab_list] = hash.keys
|
|
450
|
+
@meta[:tabs] = hash.reduce({}){|h, (k, v)| h[k] = {name: k, loc: LOCS[k], field_list: v}; h}
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def tab *tabs, options
|
|
454
|
+
raise E2Error.new("No tabs given to info") if tabs.empty?
|
|
455
|
+
tabs.each do |tab|
|
|
456
|
+
@meta[:tabs][tab].merge! options # rmerge ?
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
module ActionAngularSupport
|
|
462
|
+
def ng_execute expr
|
|
463
|
+
(@meta[:execute] ||= String.new) << expr + ";"
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def ng_record! name, value
|
|
467
|
+
value = case value
|
|
468
|
+
when String
|
|
469
|
+
"'#{value}'"
|
|
470
|
+
when nil
|
|
471
|
+
'null'
|
|
472
|
+
else
|
|
473
|
+
value
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
"action.record['#{name}'] = #{value}"
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def ng_record name
|
|
480
|
+
"action.record['#{name}']"
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def ng_info! name, *selector, expression
|
|
484
|
+
# expression = "'#{expression}'" if expression.is_a? String
|
|
485
|
+
"action.meta.fields['#{name}'].#{selector.join('.')} = #{expression}"
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def ng_call name, *args
|
|
489
|
+
# TODO
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
module ActionPanelSupport
|
|
494
|
+
def pre_run
|
|
495
|
+
modal_action true
|
|
496
|
+
super
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def post_run
|
|
500
|
+
super
|
|
501
|
+
if @meta[:panel]
|
|
502
|
+
panel_panel_template 'menu_m' if panel[:panel_template].nil?
|
|
503
|
+
# modal_action false if panel[:panel_template] == false
|
|
504
|
+
panel_class '' unless panel[:class]
|
|
505
|
+
panel_footer true if panel[:footer] != false && menu?(:panel_menu)
|
|
506
|
+
panel_header true if panel[:header] != false
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def panel
|
|
511
|
+
@meta[:panel] ||= {}
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def modal_action modal = true
|
|
515
|
+
panel[:modal_action] = modal
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def panel_template tmpl
|
|
519
|
+
panel[:template] = tmpl
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def panel_panel_template tmpl
|
|
523
|
+
panel[:panel_template] = tmpl
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def panel_class cls
|
|
527
|
+
panel[:class] = cls
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def panel_title tle
|
|
531
|
+
panel[:title] = tle
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def panel_header hdr
|
|
535
|
+
panel[:header] = hdr
|
|
536
|
+
end
|
|
154
537
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
538
|
+
def panel_footer ftr
|
|
539
|
+
panel[:footer] = ftr
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
module ActionDraggableSupport
|
|
544
|
+
def draggable
|
|
545
|
+
@meta[:draggable] ||= {}
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def post_run
|
|
549
|
+
super
|
|
550
|
+
draggable[:position_field] ||= 'position' if @meta[:draggable]
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
class MenuAction < Action
|
|
555
|
+
include ActionMenuSupport
|
|
556
|
+
action_type :menu
|
|
557
|
+
|
|
558
|
+
def invoke handler
|
|
559
|
+
{}
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
class ConfirmAction < Action
|
|
564
|
+
include ActionPanelSupport, ActionMenuSupport
|
|
565
|
+
action_type :confirm
|
|
566
|
+
|
|
567
|
+
def message msg
|
|
568
|
+
@meta[:message] = msg
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def pre_run
|
|
572
|
+
super
|
|
573
|
+
panel_template 'scaffold/message'
|
|
574
|
+
panel_title LOCS[:confirmation]
|
|
575
|
+
panel_class 'modal-default'
|
|
576
|
+
|
|
577
|
+
menu :panel_menu do
|
|
578
|
+
option :approve, icon: "ok", loc: LOCS[:ok], disabled: "action.action_pending()"
|
|
579
|
+
option :cancel, icon: "remove"
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def invoke handler
|
|
584
|
+
params = handler.request.params
|
|
585
|
+
# params.merge({arguments: params.keys})
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
module ActionOnChangeSupport
|
|
590
|
+
def on_change field, trigger_on_start = false, &blk
|
|
591
|
+
node_name = :"#{field}_on_change"
|
|
592
|
+
nd = node.define_node node_name, (blk.arity <= 2 ? OnChangeGetAction : OnChangePostAction)
|
|
593
|
+
nd.*{request &blk}
|
|
594
|
+
|
|
595
|
+
fields! field, remote_onchange: {action: node_name, record: blk.arity > 2, trigger_on_start: trigger_on_start}
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
class OnChangeAction < Action
|
|
599
|
+
include ActionAPISupport, ActionAngularSupport
|
|
600
|
+
|
|
601
|
+
def request_action_proc_params handler
|
|
602
|
+
if handler.request.post?
|
|
603
|
+
json = handler.post_to_json
|
|
604
|
+
[json[:value], json[:record]]
|
|
163
605
|
else
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
606
|
+
params = handler.request.params
|
|
607
|
+
[params["value"], params["record"]]
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# def invoke handler
|
|
612
|
+
# {}
|
|
613
|
+
# end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
class OnChangeGetAction < OnChangeAction
|
|
617
|
+
action_type :on_change
|
|
618
|
+
|
|
619
|
+
def request_action_proc_params handler
|
|
620
|
+
params = handler.request.params
|
|
621
|
+
[params["value"], params["record"]]
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
class OnChangePostAction < OnChangeAction
|
|
626
|
+
http_method :post
|
|
627
|
+
action_type :on_change
|
|
628
|
+
|
|
629
|
+
def request_action_proc_params handler
|
|
630
|
+
json = handler.post_to_json
|
|
631
|
+
[json[:value], json[:record]]
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
module ActionListSupport
|
|
637
|
+
include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport, ActionOnChangeSupport, ActionDraggableSupport
|
|
638
|
+
attr_reader :filters, :orders, :default_order_field
|
|
639
|
+
|
|
640
|
+
def pre_run
|
|
641
|
+
super
|
|
642
|
+
config.merge!(per_page: 10, use_count: false, selectable: true) # search_active: false,
|
|
643
|
+
|
|
644
|
+
panel_template 'scaffold/list'
|
|
645
|
+
panel_title "#{assets[:model].model_icon.icon} #{LOCS[assets[:model].model_route]}"
|
|
646
|
+
loc! LOCS[:list_locs]
|
|
647
|
+
menu :menu do
|
|
648
|
+
properties break: 2, group_class: "btn-group-sm"
|
|
649
|
+
option :search_toggle, icon: "search", show: "action.meta.search_field_list", active: "action.ui_state.search_active", button_loc: false
|
|
650
|
+
# divider
|
|
651
|
+
option :refresh, icon: "refresh", button_loc: false
|
|
652
|
+
option :default_order, icon: "signal", button_loc: false
|
|
653
|
+
divider
|
|
654
|
+
option :debug_info, icon: "list-alt" do
|
|
655
|
+
option :show_meta, icon: "eye-open"
|
|
656
|
+
end if Handler::development?
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
menu :item_menu do
|
|
660
|
+
properties break: 1, group_class: "btn-group-sm"
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
@meta[:state] = [:query, :ui_state]
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def field_tabs hash
|
|
667
|
+
super
|
|
668
|
+
search_template 'scaffold/search_tabs'
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def select_toggle_menu
|
|
672
|
+
m = menu :menu
|
|
673
|
+
unless m.option_index(:select_toggle, false)
|
|
674
|
+
m.option_after :default_order, :select_toggle, icon: "check", enabled: "action.meta.config.selectable", active: "action.selection", button_loc: false
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def post_run
|
|
679
|
+
super
|
|
680
|
+
|
|
681
|
+
unless panel[:class]
|
|
682
|
+
panel_class case @meta[:field_list].size
|
|
683
|
+
when 1..3; ''
|
|
684
|
+
when 4..6; 'modal-large'
|
|
685
|
+
else; 'modal-huge'
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
@meta[:primary_fields] = assets[:model].primary_keys
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
# def find_renderer type_info
|
|
693
|
+
# renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
|
|
694
|
+
# raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
|
|
695
|
+
# renderer.(self, type_info)
|
|
696
|
+
# end
|
|
697
|
+
|
|
698
|
+
def post_process
|
|
699
|
+
model = assets[:model]
|
|
700
|
+
if fields = @meta[:search_field_list]
|
|
701
|
+
fields = fields - static.meta[:search_field_list] if dynamic?
|
|
702
|
+
|
|
703
|
+
decorate(fields)
|
|
704
|
+
fields.each do |name|
|
|
705
|
+
type_info = model.find_type_info(name)
|
|
706
|
+
|
|
707
|
+
# render = fields[name][:render]
|
|
708
|
+
# if not render
|
|
709
|
+
# fields[name][:render] = find_renderer(type_info)
|
|
710
|
+
# else
|
|
711
|
+
# fields[name][:render].merge!(find_renderer(type_info)){|key, v1, v2|v1}
|
|
712
|
+
# end
|
|
713
|
+
|
|
714
|
+
fields(name)[:render] ||= begin # set before :field_list
|
|
715
|
+
renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
|
|
716
|
+
raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
|
|
717
|
+
renderer.(self, type_info)
|
|
181
718
|
end
|
|
719
|
+
|
|
720
|
+
proc = SearchRendererPostProcessors[type_info[:type]] || ListRendererPostProcessors[type_info[:type]] # ?
|
|
721
|
+
proc.(self, name, type_info) if proc
|
|
182
722
|
end
|
|
723
|
+
end
|
|
183
724
|
|
|
184
|
-
|
|
185
|
-
|
|
725
|
+
if fields = @meta[:field_list]
|
|
726
|
+
fields = fields - static.meta[:field_list] if dynamic?
|
|
727
|
+
|
|
728
|
+
decorate(fields)
|
|
729
|
+
fields.each do |name|
|
|
730
|
+
type_info = model.find_type_info(name)
|
|
731
|
+
proc = ListRendererPostProcessors[type_info[:type]]
|
|
732
|
+
proc.(self, name, type_info) if proc
|
|
733
|
+
end
|
|
186
734
|
end
|
|
187
735
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
736
|
+
super
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def search_template template
|
|
740
|
+
panel[:search_template] = template
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
def sortable *flds
|
|
744
|
+
flds = @meta[:field_list] if flds.empty?
|
|
745
|
+
fields! *flds, sort: true
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def default_order order
|
|
749
|
+
@default_order_field = order
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def search_live *flds
|
|
753
|
+
if flds.empty?
|
|
754
|
+
flds = @meta[:search_field_list]
|
|
755
|
+
@meta[:disable_search_button] = true
|
|
756
|
+
end
|
|
757
|
+
fields! *flds, search_live: true
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
def searchable *flds
|
|
761
|
+
@meta.delete(:tab_list)
|
|
762
|
+
@meta.delete(:tabs)
|
|
763
|
+
search_template 'scaffold/search'
|
|
764
|
+
@meta[:search_field_list] = *flds
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def searchable_tabs tabs
|
|
768
|
+
searchable *tabs.map{|name, fields|fields}.flatten
|
|
769
|
+
field_tabs tabs
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def template
|
|
773
|
+
SearchTemplates
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def filter name, &blk
|
|
777
|
+
(@filters ||= {})[name] = blk
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def filter_case_insensitive name
|
|
781
|
+
model = assets[:model]
|
|
782
|
+
raise E2Error.new("Field '#{name}' needs to be a string") unless model.find_type_info(name)[:otype] == :string
|
|
783
|
+
filter name do |handler, query, hash|
|
|
784
|
+
value = hash[name]
|
|
785
|
+
value ? query.where(model.table_name.q(name).ilike("%#{value}%")) : query
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
def order name, &blk
|
|
790
|
+
(@orders ||= {})[name] = blk
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
module ActionApproveSupport
|
|
795
|
+
include ActionModelSupport
|
|
796
|
+
attr_reader :validations
|
|
797
|
+
|
|
798
|
+
def self.included action
|
|
799
|
+
action.http_method :post if action.is_a? Class
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
def validate_fields *fields
|
|
803
|
+
if fields.empty?
|
|
804
|
+
@validate_fields
|
|
805
|
+
else
|
|
806
|
+
@validate_fields = assets[:model].type_info.keys & (fields + assets[:model].primary_keys).uniq
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
def before_approve handler, record
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
def after_approve handler, record
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
def validate_and_approve handler, record, parent_id
|
|
817
|
+
static.before_approve(handler, record)
|
|
818
|
+
record.valid?
|
|
819
|
+
validate_record(handler, record, parent_id)
|
|
820
|
+
if record.errors.empty?
|
|
821
|
+
static.after_approve(handler, record)
|
|
191
822
|
true
|
|
823
|
+
else
|
|
824
|
+
false
|
|
192
825
|
end
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
def allocate_record handler, json_rec
|
|
829
|
+
model = assets[:model]
|
|
830
|
+
handler.permit json_rec.is_a?(Hash)
|
|
831
|
+
val_fields = (dynamic? ? static.validate_fields : @validate_fields) || model.type_info.keys
|
|
832
|
+
left_fields = (json_rec.keys - val_fields)
|
|
833
|
+
|
|
834
|
+
puts "Left: #{left_fields.inspect}" unless left_fields.empty?
|
|
835
|
+
handler.permit left_fields.empty?
|
|
836
|
+
|
|
837
|
+
record = model.call(json_rec)
|
|
838
|
+
record.validate_fields = val_fields
|
|
839
|
+
record
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
def record handler, record
|
|
843
|
+
{errors: nil}
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def invoke handler
|
|
847
|
+
json = handler.post_to_json
|
|
848
|
+
record = allocate_record(handler, json[:record])
|
|
849
|
+
validate_and_approve(handler, record, json[:parent_id]) ? static.record(handler, record) : {record!: record.to_hash, errors!: record.errors}
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
def validate name, &blk
|
|
853
|
+
(@validations ||= {})[name] = blk
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def validate_record handler, record, parent_id
|
|
857
|
+
@validations.each do |name, val|
|
|
858
|
+
unless record.errors[name]
|
|
859
|
+
result = val.(handler, record, parent_id)
|
|
860
|
+
record.errors.add(name, result) if result
|
|
861
|
+
end
|
|
862
|
+
end if @validations
|
|
863
|
+
end
|
|
193
864
|
|
|
194
|
-
|
|
865
|
+
def pre_run
|
|
866
|
+
super
|
|
867
|
+
execute "action.errors || [action.parent().invoke(), action.panel_close()]"
|
|
195
868
|
end
|
|
196
869
|
|
|
197
|
-
def
|
|
198
|
-
|
|
870
|
+
def post_run
|
|
871
|
+
super
|
|
872
|
+
validate_fields *node.parent.*.meta[:field_list] unless validate_fields
|
|
199
873
|
end
|
|
200
874
|
end
|
|
201
875
|
|
|
876
|
+
module ActionSaveSupport
|
|
877
|
+
include ActionApproveSupport
|
|
202
878
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
879
|
+
def self.included action
|
|
880
|
+
action.http_method :post
|
|
881
|
+
class << action
|
|
882
|
+
attr_accessor :validate_only
|
|
883
|
+
end
|
|
207
884
|
end
|
|
208
885
|
|
|
209
|
-
def
|
|
210
|
-
|
|
886
|
+
def validate_and_approve handler, record, parent_id, validate_only = self.class.validate_only
|
|
887
|
+
if validate_only
|
|
888
|
+
super(handler, record, parent_id)
|
|
889
|
+
else
|
|
890
|
+
record.skip_save_refresh = true
|
|
891
|
+
record.raise_on_save_failure = false
|
|
892
|
+
model = assets[:model]
|
|
893
|
+
assoc = assets[:assoc]
|
|
894
|
+
new_assoc = record.new? && assoc && assoc[:type]
|
|
895
|
+
|
|
896
|
+
save = lambda do |c|
|
|
897
|
+
if super(handler, record, parent_id)
|
|
898
|
+
if new_assoc == :one_to_many
|
|
899
|
+
handler.permit parent_id
|
|
900
|
+
assoc[:keys].zip(split_keys(parent_id)).each{|k, v|record[k] = v}
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
result = record.save(transaction: false, validate: false)
|
|
904
|
+
if result && new_assoc == :many_to_many
|
|
905
|
+
handler.permit parent_id
|
|
906
|
+
model.db[assoc[:join_table]].insert(assoc[:left_keys] + assoc[:right_keys], split_keys(parent_id) + record.primary_key_values)
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
model.association_reflections.each do |name, assoc|
|
|
910
|
+
hash = record[name]
|
|
911
|
+
if hash.is_a?(Hash)
|
|
912
|
+
validate_and_approve_association(handler, record, name, :create, hash)
|
|
913
|
+
validate_and_approve_association(handler, record, name, :modify, hash)
|
|
914
|
+
nd = node.parent[:"#{name}!"]
|
|
915
|
+
raise Sequel::Rollback unless record.errors.empty?
|
|
916
|
+
nd.confirm_delete.delete.*.invoke_delete_db(handler, hash[:delete].to_a, model.table_name) unless hash[:delete].to_a.empty?
|
|
917
|
+
nd.link.*.invoke_link_db(handler, record.primary_key_values, hash[:link].to_a) unless hash[:link].to_a.empty?
|
|
918
|
+
nd.confirm_unlink.unlink.*.invoke_unlink_db(handler, record.primary_key_values, hash[:unlink].to_a) unless hash[:unlink].to_a.empty?
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
after_save(handler, record)
|
|
922
|
+
result
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
(model.validation_in_transaction || new_assoc == :many_to_many) ? model.db.transaction(&save) : save.(nil)
|
|
926
|
+
end
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
def validate_and_approve_association handler, record, assoc_name, node_name, hash
|
|
930
|
+
records = hash[node_name].to_a
|
|
931
|
+
unless records.empty?
|
|
932
|
+
action = node.parent[:"#{assoc_name}!"][node_name].approve.*
|
|
933
|
+
parent_id = join_keys(record.primary_key_values)
|
|
934
|
+
records.each do |arec|
|
|
935
|
+
rec = action.allocate_record(handler, arec)
|
|
936
|
+
action.validate_and_approve(handler, rec, parent_id, false)
|
|
937
|
+
rec.errors.each do |k, v|
|
|
938
|
+
(record.errors[assoc_name] ||= []).concat(v)
|
|
939
|
+
end unless rec.errors.empty?
|
|
940
|
+
end
|
|
941
|
+
end
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
def after_save handler, record
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
module ActionInsertSupport
|
|
949
|
+
def allocate_record handler, json_rec
|
|
950
|
+
record = super(handler, json_rec)
|
|
951
|
+
record.instance_variable_set(:"@new", true)
|
|
952
|
+
model = assets[:model]
|
|
953
|
+
model.primary_keys.each{|k|record.values.delete k} unless model.natural_key
|
|
954
|
+
handler.permit !record.has_primary_key? unless model.natural_key
|
|
955
|
+
record
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
module ActionUpdateSupport
|
|
960
|
+
def allocate_record handler, json_rec
|
|
961
|
+
record = super(handler, json_rec)
|
|
962
|
+
model = assets[:model]
|
|
963
|
+
handler.permit record.has_primary_key? unless model.natural_key or self.class.validate_only
|
|
964
|
+
record
|
|
211
965
|
end
|
|
212
966
|
end
|
|
213
|
-
|
|
967
|
+
|
|
968
|
+
module ActionFormSupport
|
|
969
|
+
include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport, ActionAngularSupport, ActionOnChangeSupport
|
|
970
|
+
|
|
971
|
+
def field_template template
|
|
972
|
+
panel[:field_template] = template
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
def pre_run
|
|
976
|
+
super
|
|
977
|
+
panel_template 'scaffold/form'
|
|
978
|
+
field_template 'scaffold/fields'
|
|
979
|
+
panel_class 'modal-large'
|
|
980
|
+
top = node.parent.parent == nil
|
|
981
|
+
menu :panel_menu do
|
|
982
|
+
option :approve, icon: "ok", disabled: "action.action_pending()" # text: true,
|
|
983
|
+
option :cancel, icon: "remove" unless top # text: true,
|
|
984
|
+
end
|
|
985
|
+
# modal_action false
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
def field_tabs hash
|
|
989
|
+
super
|
|
990
|
+
panel_template 'scaffold/form_tabs'
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
def record handler, record
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
def post_process
|
|
997
|
+
if fields = @meta[:field_list]
|
|
998
|
+
model = assets[:model]
|
|
999
|
+
fields = fields - static.meta[:field_list] if dynamic?
|
|
1000
|
+
|
|
1001
|
+
decorate(fields)
|
|
1002
|
+
|
|
1003
|
+
fields.each do |name|
|
|
1004
|
+
type_info = model.find_type_info(name)
|
|
1005
|
+
|
|
1006
|
+
fields(name)[:render] ||= begin
|
|
1007
|
+
renderer = DefaultFormRenderers[type_info[:type]] # .merge(default: true)
|
|
1008
|
+
raise E2Error.new("No form renderer found for field '#{type_info[:name]}' of type '#{type_info[:type]}'") unless renderer
|
|
1009
|
+
renderer.(self, type_info)
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
proc = FormRendererPostProcessors[type_info[:type]]
|
|
1013
|
+
proc.(self, name, type_info) if proc
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
assoc = assets[:assoc]
|
|
1017
|
+
if assoc && assoc[:type] == :one_to_many
|
|
1018
|
+
# fields.select{|f| assoc[:keys].include? f}.each do |key|
|
|
1019
|
+
# # hide_fields(key) if self[:fields, key, :hidden] == nil
|
|
1020
|
+
# fields! key, disabled: true
|
|
1021
|
+
# end
|
|
1022
|
+
assoc[:keys].each do |key|
|
|
1023
|
+
fields! key, disabled: true if fields.include? key
|
|
1024
|
+
end
|
|
1025
|
+
end
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
super
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
def post_run
|
|
1032
|
+
super
|
|
1033
|
+
@meta[:primary_fields] = assets[:model].primary_keys
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
def template
|
|
1037
|
+
Templates
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
def hr_after field, message = '-'
|
|
1041
|
+
fields! field, hr: message
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
def create?
|
|
1045
|
+
@action_type == :create
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def modify?
|
|
1049
|
+
@action_type == :modify
|
|
1050
|
+
end
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
module ActionCreateSupport
|
|
1054
|
+
include ActionFormSupport
|
|
1055
|
+
|
|
1056
|
+
def self.included action
|
|
1057
|
+
action.action_type :create
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
def pre_run
|
|
1061
|
+
super
|
|
1062
|
+
panel_title "#{LOCS[:create_title]} - #{LOCS[assets[:model].table_name]}"
|
|
1063
|
+
node.parent.*.menu(:menu).option_at 0, node.name, icon: "plus-sign", button_loc: false if node.parent.*.is_a?(ActionListSupport)
|
|
1064
|
+
|
|
1065
|
+
hide_pk unless assets[:model].natural_key
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
def record handler, record
|
|
1069
|
+
create_record(handler, record)
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
def create_record handler, record
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
def invoke handler
|
|
1076
|
+
record = {}
|
|
1077
|
+
# if assoc = assets[:assoc]
|
|
1078
|
+
# case assoc[:type]
|
|
1079
|
+
# when :one_to_many
|
|
1080
|
+
# parent = handler.params[:parent_id]
|
|
1081
|
+
# assoc[:keys].zip(split_keys(parent)).each{|key, val| record[key] = val} if parent
|
|
1082
|
+
# end
|
|
1083
|
+
# end
|
|
1084
|
+
static.record(handler, record)
|
|
1085
|
+
{record: record, new: true}
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
module ActionModifySupport
|
|
1090
|
+
include ActionFormSupport
|
|
1091
|
+
|
|
1092
|
+
def self.included action
|
|
1093
|
+
action.action_type :modify
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
def pre_run
|
|
1097
|
+
super
|
|
1098
|
+
panel_title "#{LOCS[:modify_title]} - #{LOCS[assets[:model].table_name]}"
|
|
1099
|
+
node.parent.*.menu(:item_menu).option node.name, icon: "pencil", button_loc: false
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
def record handler, record
|
|
1103
|
+
modify_record(handler, record)
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
def modify_record handler, record
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def invoke handler
|
|
1110
|
+
handler.permit id = handler.params[:id]
|
|
1111
|
+
record = find_record(handler, id)
|
|
1112
|
+
|
|
1113
|
+
if record
|
|
1114
|
+
static.record(handler, record)
|
|
1115
|
+
{record: record}
|
|
1116
|
+
else
|
|
1117
|
+
handler.halt_not_found LOCS[:no_entry]
|
|
1118
|
+
end
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
def post_run
|
|
1122
|
+
super
|
|
1123
|
+
assets[:model].primary_keys.each do |key| # pre_run ?
|
|
1124
|
+
fields! key, disabled: true
|
|
1125
|
+
end
|
|
1126
|
+
end
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
module ActionViewSupport
|
|
1130
|
+
include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport
|
|
1131
|
+
|
|
1132
|
+
def self.included action
|
|
1133
|
+
action.action_type :view
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def pre_run
|
|
1137
|
+
super
|
|
1138
|
+
panel_template 'scaffold/view'
|
|
1139
|
+
panel_title "#{LOCS[:view_title]} - #{LOCS[assets[:model].table_name]}"
|
|
1140
|
+
panel[:backdrop] = true
|
|
1141
|
+
|
|
1142
|
+
menu(:panel_menu).option :close, icon: "remove"
|
|
1143
|
+
node.parent.*.menu(:item_menu).option node.name, icon: "file", button_loc: false
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def field_tabs hash
|
|
1147
|
+
super
|
|
1148
|
+
panel_template 'scaffold/view_tabs'
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def record handler, record
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
def invoke handler
|
|
1155
|
+
handler.permit id = handler.params[:id]
|
|
1156
|
+
record = find_record(handler, id)
|
|
1157
|
+
if record
|
|
1158
|
+
static.record(handler, record)
|
|
1159
|
+
{record: record}
|
|
1160
|
+
else
|
|
1161
|
+
handler.halt_not_found LOCS[:no_entry]
|
|
1162
|
+
end
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
def post_process
|
|
1166
|
+
if fields = @meta[:field_list]
|
|
1167
|
+
model = assets[:model]
|
|
1168
|
+
fields = fields - static.meta[:field_list] if dynamic?
|
|
1169
|
+
|
|
1170
|
+
decorate(fields)
|
|
1171
|
+
fields.each do |name|
|
|
1172
|
+
type_info = model.find_type_info(name)
|
|
1173
|
+
proc = ListRendererPostProcessors[type_info[:type]]
|
|
1174
|
+
proc.(self, name, type_info) if proc
|
|
1175
|
+
end
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
super
|
|
1179
|
+
end
|
|
1180
|
+
end
|
|
1181
|
+
|
|
1182
|
+
module ActionDeleteSupport
|
|
1183
|
+
include ActionModelSupport
|
|
1184
|
+
|
|
1185
|
+
def self.included action
|
|
1186
|
+
action.http_method :delete
|
|
1187
|
+
action.action_type :delete
|
|
1188
|
+
end
|
|
1189
|
+
|
|
1190
|
+
def pre_run
|
|
1191
|
+
super
|
|
1192
|
+
execute "action.errors || [action.parent().invoke(), action.panel_close()]"
|
|
1193
|
+
node.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
|
|
1194
|
+
end
|
|
1195
|
+
end
|
|
1196
|
+
|
|
1197
|
+
module ActionBulkDeleteSupport
|
|
1198
|
+
include ActionModelSupport
|
|
1199
|
+
|
|
1200
|
+
def self.included action
|
|
1201
|
+
action.http_method :delete
|
|
1202
|
+
action.action_type :bulk_delete
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
def pre_run
|
|
1206
|
+
super
|
|
1207
|
+
execute "action.errors || [action.parent().invoke(), action.panel_close()]"
|
|
1208
|
+
node.parent.parent.*.select_toggle_menu
|
|
1209
|
+
node.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
|
|
1210
|
+
end
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
(FormRendererPostProcessors ||= {}).merge!(
|
|
1214
|
+
boolean: lambda{|action, field, info|
|
|
1215
|
+
action.fields(field)[:render].merge! true_value: info[:true_value], false_value: info[:false_value]
|
|
1216
|
+
action.fields(field)[:dont_strip] = info[:dont_strip] if info[:dont_strip]
|
|
1217
|
+
},
|
|
1218
|
+
date: lambda{|action, field, info|
|
|
1219
|
+
action.fields(field)[:render].merge! format: info[:format], model_format: info[:model_format]
|
|
1220
|
+
if date_to = info[:other_date]
|
|
1221
|
+
action.fields(field)[:render].merge! other_date: date_to #, format: info[:format], model_format: info[:model_format]
|
|
1222
|
+
action.hide_fields date_to
|
|
1223
|
+
elsif time = info[:other_time]
|
|
1224
|
+
action.fields(field)[:render].merge! other_time: time
|
|
1225
|
+
action.hide_fields time
|
|
1226
|
+
end
|
|
1227
|
+
},
|
|
1228
|
+
time: lambda{|action, field, info|
|
|
1229
|
+
render = action.fields(field)[:render]
|
|
1230
|
+
render[:type] ||= info[:otype] == :string ? :string : :number
|
|
1231
|
+
render.merge! format: info[:format], model_format: info[:model_format]
|
|
1232
|
+
},
|
|
1233
|
+
decimal_date: lambda{|action, field, info|
|
|
1234
|
+
FormRendererPostProcessors[:date].(action, field, info)
|
|
1235
|
+
action.fields! field, type: :decimal_date
|
|
1236
|
+
},
|
|
1237
|
+
decimal_time: lambda{|action, field, info|
|
|
1238
|
+
FormRendererPostProcessors[:time].(action, field, info)
|
|
1239
|
+
action.fields! field, type: :decimal_time
|
|
1240
|
+
},
|
|
1241
|
+
datetime: lambda{|action, field, info|
|
|
1242
|
+
action.fields(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]
|
|
1243
|
+
},
|
|
1244
|
+
currency: lambda{|action, field, info|
|
|
1245
|
+
action.fields(field)[:render].merge! symbol: info[:symbol]
|
|
1246
|
+
},
|
|
1247
|
+
# date_range: lambda{|action, field, info|
|
|
1248
|
+
# action.fields[field][:render].merge! other_date: info[:other_date], format: info[:format], model_format: info[:model_format]
|
|
1249
|
+
# action.hide_fields info[:other_date]
|
|
1250
|
+
# action.fields[field][:decimal_date] = true if info[:validations][:decimal_date]
|
|
1251
|
+
# },
|
|
1252
|
+
list_select: lambda{|action, field, info|
|
|
1253
|
+
render = action.fields(field)[:render]
|
|
1254
|
+
render.merge! values: info[:values]
|
|
1255
|
+
render.merge! max_length: info[:max_length], max_length_html: info[:max_length_html], separator: info[:separator] if info[:multiselect]
|
|
1256
|
+
},
|
|
1257
|
+
many_to_one: lambda{|action, field, info|
|
|
1258
|
+
field_info = action.fields(field)
|
|
1259
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
1260
|
+
field_info[:fields] = info[:keys]
|
|
1261
|
+
field_info[:type] = info[:otype]
|
|
1262
|
+
|
|
1263
|
+
(info[:keys] - [field]).each do |of|
|
|
1264
|
+
f_info = action.fields(of)
|
|
1265
|
+
f_info[:hidden] = true
|
|
1266
|
+
f_info[:type] = action.assets[:model].type_info[of].fetch(:otype)
|
|
1267
|
+
end
|
|
1268
|
+
},
|
|
1269
|
+
file_store: lambda{|action, field, info|
|
|
1270
|
+
action.fields(field)[:render].merge! multiple: info[:multiple]
|
|
1271
|
+
},
|
|
1272
|
+
star_to_many_field: lambda{|action, field, info|
|
|
1273
|
+
field_info = action.fields(field)
|
|
1274
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
1275
|
+
}
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
(ListRendererPostProcessors ||= {}).merge!(
|
|
1279
|
+
boolean: lambda{|action, field, info|
|
|
1280
|
+
action.fields! field, type: :boolean # move to action ?
|
|
1281
|
+
action.fields(field)[:render] ||= {}
|
|
1282
|
+
action.fields(field)[:render].merge! true_value: info[:true_value], false_value: info[:false_value]
|
|
1283
|
+
},
|
|
1284
|
+
list_select: lambda{|action, field, info|
|
|
1285
|
+
action.fields! field, type: :list_select
|
|
1286
|
+
render = (action.fields(field)[:render] ||= {})
|
|
1287
|
+
render.merge! values: info[:values]
|
|
1288
|
+
render.merge! multiselect: true if info[:multiselect]
|
|
1289
|
+
},
|
|
1290
|
+
datetime: lambda{|action, field, info|
|
|
1291
|
+
action.fields! field, type: :datetime
|
|
1292
|
+
},
|
|
1293
|
+
decimal_date: lambda{|action, field, info|
|
|
1294
|
+
action.fields! field, type: :decimal_date
|
|
1295
|
+
},
|
|
1296
|
+
decimal_time: lambda{|action, field, info|
|
|
1297
|
+
action.fields! field, type: :decimal_time
|
|
1298
|
+
},
|
|
1299
|
+
# date_range: lambda{|action, field, info|
|
|
1300
|
+
# action.fields[field][:type] = :decimal_date if info[:validations][:decimal_date] # ? :decimal_date : :date
|
|
1301
|
+
# }
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1304
|
+
(SearchRendererPostProcessors ||= {}).merge!(
|
|
1305
|
+
many_to_one: lambda{|action, field, info|
|
|
1306
|
+
model = action.assets[:model]
|
|
1307
|
+
if model.type_info[field]
|
|
1308
|
+
keys = info[:keys]
|
|
1309
|
+
else
|
|
1310
|
+
action.check_static_action
|
|
1311
|
+
model = model.many_to_one_associations[field.table].associated_class
|
|
1312
|
+
keys = info[:keys].map{|k| model.table_name.q(k)}
|
|
1313
|
+
end
|
|
1314
|
+
|
|
1315
|
+
field_info = action.fields(field)
|
|
1316
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
1317
|
+
field_info[:fields] = keys
|
|
1318
|
+
field_info[:type] = info[:otype]
|
|
1319
|
+
|
|
1320
|
+
(keys - [field]).each do |of|
|
|
1321
|
+
f_info = action.fields(of)
|
|
1322
|
+
raise E2Error.new("Missing searchable field: '#{of}' in model '#{action.assets[:model]}'") unless f_info
|
|
1323
|
+
f_info[:hidden_search] = true
|
|
1324
|
+
f_info[:type] = model.type_info[of].fetch(:otype)
|
|
1325
|
+
end
|
|
1326
|
+
},
|
|
1327
|
+
date: lambda{|action, field, info|
|
|
1328
|
+
action.fields(field)[:render] ||= {}
|
|
1329
|
+
action.fields(field)[:render].merge! format: info[:format], model_format: info[:model_format] # Model::DEFAULT_DATE_FORMAT
|
|
1330
|
+
},
|
|
1331
|
+
datetime: lambda{|action, field, info|
|
|
1332
|
+
action.fields(field)[:render] ||= {}
|
|
1333
|
+
action.fields(field)[:render].merge! format: info[:date_format], model_format: info[:date_model_format] # Model::DEFAULT_DATE_FORMAT
|
|
1334
|
+
},
|
|
1335
|
+
decimal_date: lambda{|action, field, info|
|
|
1336
|
+
SearchRendererPostProcessors[:date].(action, field, info)
|
|
1337
|
+
}
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
(DefaultFormRenderers ||= {}).merge!(
|
|
1341
|
+
date: lambda{|action, info|
|
|
1342
|
+
info[:other_date] ? Templates.date_range : (info[:other_time] ? Templates.date_time : Templates.date_picker)
|
|
1343
|
+
|
|
1344
|
+
},
|
|
1345
|
+
time: lambda{|action, info| Templates.time_picker},
|
|
1346
|
+
datetime: lambda{|action, info| Templates.datetime_picker},
|
|
1347
|
+
file_store: lambda{|action, info| Templates.file_store},
|
|
1348
|
+
blob: lambda{|action, info| Templates.blob}, # !!!
|
|
1349
|
+
blob_store: lambda{|action, info| Templates.blob},
|
|
1350
|
+
foreign_blob_store: lambda{|action, info| Templates.blob},
|
|
1351
|
+
string: lambda{|action, info| Templates.input_text(info[:length])},
|
|
1352
|
+
text: lambda{|action, info| Templates.text},
|
|
1353
|
+
integer: lambda{|action, info| Templates.integer},
|
|
1354
|
+
decimal: lambda{|action, info| Templates.decimal},
|
|
1355
|
+
decimal_date: lambda{|action, info| DefaultFormRenderers[:date].(action, info)},
|
|
1356
|
+
decimal_time: lambda{|action, info| Templates.time_picker},
|
|
1357
|
+
email: lambda{|action, info| Templates.email(info[:length])},
|
|
1358
|
+
password: lambda{|action, info| Templates.password(info[:length])},
|
|
1359
|
+
# date_range: lambda{|action, info| Templates.date_range},
|
|
1360
|
+
boolean: lambda{|action, info| Templates.checkbox_button},
|
|
1361
|
+
currency: lambda{|action, info| Templates.currency},
|
|
1362
|
+
list_select: lambda{|action, info|
|
|
1363
|
+
length = info[:values].length
|
|
1364
|
+
max_length = length > 0 ? info[:values].map(&:last).max_by(&:length).length : 0
|
|
1365
|
+
if info[:multiselect]
|
|
1366
|
+
Templates.list_bsmselect(max_length)
|
|
1367
|
+
elsif length <= 3
|
|
1368
|
+
Templates.list_buttons(optional: !info[:required])
|
|
1369
|
+
elsif length <= 15
|
|
1370
|
+
Templates.list_bsselect(max_length, optional: !info[:required])
|
|
1371
|
+
else
|
|
1372
|
+
Templates.list_select(max_length, optional: !info[:required])
|
|
1373
|
+
end
|
|
1374
|
+
},
|
|
1375
|
+
star_to_many_field: lambda{|action, info| Templates.scaffold},
|
|
1376
|
+
many_to_one: lambda{|action, info|
|
|
1377
|
+
tmpl_type = info[:decode][:form]
|
|
1378
|
+
case
|
|
1379
|
+
when tmpl_type[:scaffold]; Templates.scaffold_picker
|
|
1380
|
+
when tmpl_type[:list]; Templates.bsselect_picker
|
|
1381
|
+
when tmpl_type[:typeahead];Templates.typeahead_picker
|
|
1382
|
+
else
|
|
1383
|
+
raise E2Error.new("Unknown decode type #{tmpl_type}")
|
|
1384
|
+
end
|
|
1385
|
+
}, # required/opt
|
|
1386
|
+
)
|
|
1387
|
+
|
|
1388
|
+
(DefaultSearchRenderers ||= {}).merge!(
|
|
1389
|
+
date: lambda{|action, info| SearchTemplates.date_range},
|
|
1390
|
+
datetime: lambda{|action, info| SearchTemplates.date_range},
|
|
1391
|
+
decimal_date: lambda{|action, info| SearchTemplates.date_range},
|
|
1392
|
+
integer: lambda{|action, info| SearchTemplates.integer_range},
|
|
1393
|
+
string: lambda{|action, info| SearchTemplates.input_text},
|
|
1394
|
+
boolean: lambda{|action, info| SearchTemplates.checkbox_buttons},
|
|
1395
|
+
list_select: lambda{|action, info|
|
|
1396
|
+
length = info[:values].length
|
|
1397
|
+
if length <= 3
|
|
1398
|
+
SearchTemplates.list_buttons
|
|
1399
|
+
elsif length <= 15
|
|
1400
|
+
# max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
1401
|
+
SearchTemplates.list_bsselect(multiple: info[:multiple])
|
|
1402
|
+
else
|
|
1403
|
+
# max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
1404
|
+
SearchTemplates.list_select
|
|
1405
|
+
end
|
|
1406
|
+
},
|
|
1407
|
+
many_to_one: lambda{|action, info|
|
|
1408
|
+
tmpl_type = info[:decode][:search]
|
|
1409
|
+
case
|
|
1410
|
+
when tmpl_type[:scaffold]; SearchTemplates.scaffold_picker(multiple: tmpl_type[:multiple])
|
|
1411
|
+
when tmpl_type[:list]; SearchTemplates.bsselect_picker(multiple: tmpl_type[:multiple])
|
|
1412
|
+
when tmpl_type[:typeahead];SearchTemplates.typeahead_picker
|
|
1413
|
+
else
|
|
1414
|
+
raise E2Error.new("Unknown decode type #{tmpl_type}")
|
|
1415
|
+
end
|
|
1416
|
+
}
|
|
1417
|
+
)
|
|
1418
|
+
end
|