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