engine2 1.0.0
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 +7 -0
- data/Gemfile +2 -0
- data/Rakefile +138 -0
- data/conf/message.yaml +93 -0
- data/conf/message_pl.yaml +93 -0
- data/engine2.gemspec +34 -0
- data/lib/engine2.rb +34 -0
- data/lib/engine2/action.rb +217 -0
- data/lib/engine2/core.rb +572 -0
- data/lib/engine2/handler.rb +134 -0
- data/lib/engine2/meta.rb +969 -0
- data/lib/engine2/meta/decode_meta.rb +110 -0
- data/lib/engine2/meta/delete_meta.rb +73 -0
- data/lib/engine2/meta/form_meta.rb +144 -0
- data/lib/engine2/meta/infra_meta.rb +292 -0
- data/lib/engine2/meta/link_meta.rb +133 -0
- data/lib/engine2/meta/list_meta.rb +284 -0
- data/lib/engine2/meta/save_meta.rb +63 -0
- data/lib/engine2/meta/view_meta.rb +22 -0
- data/lib/engine2/model.rb +390 -0
- data/lib/engine2/models/Files.rb +38 -0
- data/lib/engine2/models/UserInfo.rb +24 -0
- data/lib/engine2/post_bootstrap.rb +83 -0
- data/lib/engine2/pre_bootstrap.rb +27 -0
- data/lib/engine2/scheme.rb +202 -0
- data/lib/engine2/templates.rb +229 -0
- data/lib/engine2/type_info.rb +342 -0
- data/lib/engine2/version.rb +9 -0
- data/public/assets/javascripts.js +13 -0
- data/public/assets/styles.css +4 -0
- data/public/css/angular-motion.css +1022 -0
- data/public/css/angular-ui-tree.min.css +1 -0
- data/public/css/app.css +196 -0
- data/public/css/bootstrap-additions.css +1560 -0
- data/public/css/bootstrap.min.css +11 -0
- data/public/css/font-awesome.min.css +4 -0
- data/public/favicon.ico +0 -0
- data/public/fonts/FontAwesome.otf +0 -0
- data/public/fonts/fontawesome-webfont.eot +0 -0
- data/public/fonts/fontawesome-webfont.svg +655 -0
- data/public/fonts/fontawesome-webfont.ttf +0 -0
- data/public/fonts/fontawesome-webfont.woff +0 -0
- data/public/fonts/fontawesome-webfont.woff2 +0 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +288 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/public/images/file.png +0 -0
- data/public/images/folder-closed.png +0 -0
- data/public/images/folder.png +0 -0
- data/public/images/node-closed-2.png +0 -0
- data/public/images/node-closed-light.png +0 -0
- data/public/images/node-closed.png +0 -0
- data/public/images/node-opened-2.png +0 -0
- data/public/images/node-opened-light.png +0 -0
- data/public/images/node-opened.png +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader-light.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/public/js/angular-animate.js +4115 -0
- data/public/js/angular-cookies.js +322 -0
- data/public/js/angular-local-storage.js +455 -0
- data/public/js/angular-route.js +1022 -0
- data/public/js/angular-sanitize.js +717 -0
- data/public/js/angular-strap.js +4339 -0
- data/public/js/angular-strap.tpl.js +43 -0
- data/public/js/angular-ui-tree.js +1569 -0
- data/public/js/angular.js +30714 -0
- data/public/js/i18n/angular-locale_pl.js +115 -0
- data/public/js/lodash.custom.min.js +97 -0
- data/public/js/ng-file-upload-shim.min.js +2 -0
- data/public/js/ng-file-upload.min.js +3 -0
- data/views/app.coffee +3 -0
- data/views/engine2.coffee +557 -0
- data/views/engine2actions.coffee +849 -0
- data/views/engine2templates.coffee +0 -0
- data/views/fields/blob.slim +22 -0
- data/views/fields/bs_select.slim +10 -0
- data/views/fields/bsselect_picker.slim +18 -0
- data/views/fields/bsselect_picker_opt.slim +22 -0
- data/views/fields/checkbox.slim +11 -0
- data/views/fields/checkbox_buttons.slim +6 -0
- data/views/fields/checkbox_buttons_opt.slim +8 -0
- data/views/fields/currency.slim +10 -0
- data/views/fields/date.slim +21 -0
- data/views/fields/date_range.slim +44 -0
- data/views/fields/date_time.slim +42 -0
- data/views/fields/datetime.slim +42 -0
- data/views/fields/decimal.slim +11 -0
- data/views/fields/decimal_date.slim +22 -0
- data/views/fields/decimal_time.slim +26 -0
- data/views/fields/email.slim +13 -0
- data/views/fields/file_store.slim +61 -0
- data/views/fields/input_text.slim +14 -0
- data/views/fields/integer.slim +11 -0
- data/views/fields/list_bsselect.slim +18 -0
- data/views/fields/list_bsselect_opt.slim +21 -0
- data/views/fields/list_buttons.slim +3 -0
- data/views/fields/list_buttons_opt.slim +5 -0
- data/views/fields/list_select.slim +11 -0
- data/views/fields/list_select_opt.slim +15 -0
- data/views/fields/password.slim +14 -0
- data/views/fields/radio_checkbox.slim +10 -0
- data/views/fields/scaffold.slim +2 -0
- data/views/fields/scaffold_picker.slim +20 -0
- data/views/fields/select_picker.slim +12 -0
- data/views/fields/select_picker_opt.slim +16 -0
- data/views/fields/text_area.slim +10 -0
- data/views/fields/time.slim +22 -0
- data/views/fields/typeahead_picker.slim +25 -0
- data/views/index.slim +44 -0
- data/views/infra/index.slim +5 -0
- data/views/infra/inspect.slim +81 -0
- data/views/modals/close_m.slim +15 -0
- data/views/modals/confirm_m.slim +19 -0
- data/views/modals/empty_m.slim +12 -0
- data/views/modals/menu_m.slim +13 -0
- data/views/modals/yes_no_m.slim +19 -0
- data/views/panels/menu_m.slim +9 -0
- data/views/scaffold/confirm.slim +3 -0
- data/views/scaffold/fields.slim +10 -0
- data/views/scaffold/form.slim +11 -0
- data/views/scaffold/list.slim +42 -0
- data/views/scaffold/message.slim +3 -0
- data/views/scaffold/search.slim +20 -0
- data/views/scaffold/view.slim +18 -0
- data/views/search_fields/bsmselect_picker.slim +25 -0
- data/views/search_fields/bsselect_picker.slim +24 -0
- data/views/search_fields/checkbox.slim +11 -0
- data/views/search_fields/checkbox2.slim +14 -0
- data/views/search_fields/checkbox_buttons.slim +10 -0
- data/views/search_fields/date_range.slim +46 -0
- data/views/search_fields/decimal_date_range.slim +47 -0
- data/views/search_fields/input_text.slim +18 -0
- data/views/search_fields/integer.slim +18 -0
- data/views/search_fields/integer_range.slim +27 -0
- data/views/search_fields/list_bsmselect.slim +24 -0
- data/views/search_fields/list_bsselect.slim +22 -0
- data/views/search_fields/list_buttons.slim +8 -0
- data/views/search_fields/list_select.slim +17 -0
- data/views/search_fields/scaffold_picker.slim +19 -0
- data/views/search_fields/select_picker.slim +17 -0
- data/views/search_fields/typeahead_picker.slim +25 -0
- metadata +327 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
module Engine2
|
|
4
|
+
class Handler < Sinatra::Base
|
|
5
|
+
reset!
|
|
6
|
+
API ||= "/api"
|
|
7
|
+
|
|
8
|
+
def halt_json code, cause, message
|
|
9
|
+
halt code, {'Content-Type' => 'application/json'}, {message: message, cause: cause}.to_json
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def halt_forbidden cause = '', message = LOCS[:access_forbidden]
|
|
13
|
+
halt_json 403, cause, message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def halt_unauthorized cause = '', message = LOCS[:access_unauthorized]
|
|
17
|
+
halt_json 401, cause, message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def halt_not_found cause = '', message = LOCS[:access_not_found]
|
|
21
|
+
halt_json 404, cause, message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def halt_method_not_allowed cause = '', message = LOCS[:access_method_not_allowed]
|
|
25
|
+
halt_json 405, cause, message
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def halt_server_error cause, message
|
|
29
|
+
halt_json 500, cause, message
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def permit access
|
|
33
|
+
halt_forbidden 'Permission denied' unless access
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initial?
|
|
37
|
+
params[:initial]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def logged_in?
|
|
41
|
+
!user.nil?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def user
|
|
45
|
+
session[:user]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def no_cache
|
|
49
|
+
# agent = request.user_agent
|
|
50
|
+
# if agent && (agent["MSIE"] || agent["Trident"])
|
|
51
|
+
# headers["Pragma"] = "no-cache"
|
|
52
|
+
# headers["Expires"] = "0"
|
|
53
|
+
# headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
|
54
|
+
# end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def post_to_json
|
|
58
|
+
JSON.parse(request.body.read, symbolize_names: true) # rescue halt_server_error
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def param_to_json name
|
|
62
|
+
permit param = params[name]
|
|
63
|
+
JSON.parse(param, symbolize_names: true) # rescue halt_server_error
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def serve_api_resource verb, path
|
|
67
|
+
path = path.split('/') # -1 ?
|
|
68
|
+
is_meta = path.pop if path.last == 'meta'
|
|
69
|
+
action = ROOT
|
|
70
|
+
path.each do |pat|
|
|
71
|
+
action = action[pat.to_sym]
|
|
72
|
+
halt_not_found unless action
|
|
73
|
+
halt_unauthorized unless action.check_access!(self)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
meta = action.*
|
|
77
|
+
response = if is_meta
|
|
78
|
+
params[:access] ? action.access_info(self) : {meta: meta.get, actions: action.actions_info(self)}
|
|
79
|
+
else
|
|
80
|
+
if meta.http_method == verb && meta.invokable
|
|
81
|
+
begin
|
|
82
|
+
meta.invoke!(self)
|
|
83
|
+
rescue => error
|
|
84
|
+
attachment nil, nil
|
|
85
|
+
# content_type :json
|
|
86
|
+
serve_api_error(error)
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
halt_method_not_allowed
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if response.is_a?(Hash)
|
|
94
|
+
content_type :json
|
|
95
|
+
response.to_json
|
|
96
|
+
else
|
|
97
|
+
response
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
[:get, :post, :delete].each do |verb|
|
|
102
|
+
send(verb, "#{API}/*"){|path| serve_api_resource(verb, path)}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def serve_api_error error
|
|
106
|
+
halt_server_error Rack::Utils.escape_html(error.inspect) + "<hr>" + error.backtrace.take(30).map{|b| Rack::Utils.escape_html(b)}.join("<br>"), LOCS[:error]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
get "/js/*.js" do |c|
|
|
110
|
+
coffee c.to_sym
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
get '/*' do |name|
|
|
114
|
+
headers 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'
|
|
115
|
+
if name.empty?
|
|
116
|
+
load 'engine2.rb' if settings.environment == :development
|
|
117
|
+
name = 'index'
|
|
118
|
+
end
|
|
119
|
+
slim name.to_sym
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
set :slim, pretty: true, sort_attrs: false
|
|
123
|
+
set :views, ["views", "#{PATH}/views"]
|
|
124
|
+
set :public_folder, "#{PATH}/public"
|
|
125
|
+
set :sessions, expire_after: 3600 # , :httponly => true, :secure => production?
|
|
126
|
+
|
|
127
|
+
helpers do
|
|
128
|
+
def find_template(views, name, engine, &block)
|
|
129
|
+
views.each{|v| super(v, name, engine, &block)}
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
end
|
|
134
|
+
end
|
data/lib/engine2/meta.rb
ADDED
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
module Engine2
|
|
3
|
+
class Meta
|
|
4
|
+
attr_reader :action, :assets, :invokable, :static
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def meta_type mt = nil
|
|
8
|
+
mt ? @meta_type = mt : @meta_type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def http_method hm = nil
|
|
12
|
+
hm ? @http_method = hm : @http_method
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def inherited cls
|
|
16
|
+
cls.http_method http_method
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
http_method :get
|
|
21
|
+
|
|
22
|
+
def initialize action, assets, static = self
|
|
23
|
+
@meta = {}
|
|
24
|
+
@action = action
|
|
25
|
+
@assets = assets
|
|
26
|
+
@static = static
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# def self.method_added name
|
|
30
|
+
# puts "ADDED #{name}"
|
|
31
|
+
# end
|
|
32
|
+
|
|
33
|
+
def http_method
|
|
34
|
+
@http_method # || (raise E2Error.new("No http method for meta #{self.class}"))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def meta_type
|
|
38
|
+
@meta_type || (raise E2Error.new("No meta_type for meta #{self.class}"))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def check_static_meta
|
|
42
|
+
raise E2Error.new("Static meta required") if dynamic?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def invoke! handler
|
|
46
|
+
if rmp = @request_meta_proc
|
|
47
|
+
meta = self.class.new(action, assets, self)
|
|
48
|
+
meta.instance_exec(handler, *meta.request_meta_proc_params(handler), &rmp)
|
|
49
|
+
meta.post_process
|
|
50
|
+
|
|
51
|
+
{response: meta.invoke(handler), meta: meta.get}
|
|
52
|
+
else
|
|
53
|
+
response = invoke(handler)
|
|
54
|
+
if response.is_a?(Hash)
|
|
55
|
+
{response: response}
|
|
56
|
+
else
|
|
57
|
+
response
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def response args
|
|
63
|
+
(@meta[:response] ||= {}).merge!(args)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def get
|
|
67
|
+
@meta
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def dynamic?
|
|
71
|
+
self != @static
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# def [] *keys
|
|
75
|
+
# @meta.path(*keys)
|
|
76
|
+
# end
|
|
77
|
+
|
|
78
|
+
# def []= *keys, value
|
|
79
|
+
# @meta.path!(*keys, value)
|
|
80
|
+
# end
|
|
81
|
+
|
|
82
|
+
def lookup *keys
|
|
83
|
+
if dynamic? # we are the request meta
|
|
84
|
+
value = @meta.path(*keys)
|
|
85
|
+
value.nil? ? @static.get.path(*keys) : value
|
|
86
|
+
# value || @static.value.path(keys)
|
|
87
|
+
else
|
|
88
|
+
@meta.path(*keys)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def merge *keys
|
|
93
|
+
if keys.length == 1
|
|
94
|
+
key = keys.first
|
|
95
|
+
dynamic? ? @static.get[key].merge(@meta[key] || {}) : @meta[key]
|
|
96
|
+
else
|
|
97
|
+
dynamic? ? @static.get.path(*keys).merge(@meta.path(*keys)) : @meta.path(*keys)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def freeze_meta
|
|
102
|
+
hash = @meta
|
|
103
|
+
hash.freeze
|
|
104
|
+
# hash.each_pair{|k, v| freeze(v) if v.is_a? Hash}
|
|
105
|
+
freeze
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def request_meta_proc_params handler
|
|
109
|
+
[]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def request &blk
|
|
113
|
+
raise E2Error.new("No block given for request meta") unless blk
|
|
114
|
+
raise E2Error.new("Request meta already supplied") if @request_meta_proc
|
|
115
|
+
raise E2Error.new("No request block in request meta allowed") if dynamic?
|
|
116
|
+
@request_meta_proc = blk
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def pre_run
|
|
121
|
+
@meta_type = self.class.meta_type
|
|
122
|
+
@http_method = self.class.http_method
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def action_defined
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def post_run
|
|
129
|
+
@invokable = respond_to?(:invoke)
|
|
130
|
+
post_process
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def post_process
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def split_keys id
|
|
137
|
+
Sequel::split_keys(id)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class DummyMeta < Meta
|
|
142
|
+
meta_type :dummy
|
|
143
|
+
|
|
144
|
+
# def invoke handler
|
|
145
|
+
# {}
|
|
146
|
+
# end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
module MetaAPISupport
|
|
150
|
+
def info
|
|
151
|
+
@meta[:info] ||= {}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def config
|
|
155
|
+
@meta[:config] ||= {}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def info! *fields, options
|
|
159
|
+
raise E2Error.new("No fields given to info") if fields.empty?
|
|
160
|
+
fields.each do |field|
|
|
161
|
+
if options
|
|
162
|
+
(info[field] ||= {}).merge! options # rmerge ?
|
|
163
|
+
else
|
|
164
|
+
info[field] = false
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def decorate list
|
|
170
|
+
list.each do |f|
|
|
171
|
+
m = (info[f] ||= {})
|
|
172
|
+
m[:loc] ||= LOCS[f]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def render field, options
|
|
177
|
+
info! field, render: options
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def hide_fields *flds
|
|
181
|
+
info! *flds, hidden: true
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def show_fields *flds
|
|
185
|
+
info! *flds, hidden: false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def field_filter *flds, filter
|
|
189
|
+
info! *flds, filter: filter
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
module MetaMenuSupport
|
|
194
|
+
def menu menu_name, &blk
|
|
195
|
+
@menus ||= {}
|
|
196
|
+
@menus[menu_name] ||= ActionMenuBuilder.new(:root)
|
|
197
|
+
@menus[menu_name].instance_eval(&blk) if blk
|
|
198
|
+
@menus[menu_name]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def post_process
|
|
202
|
+
super
|
|
203
|
+
if @menus && !@menus.empty?
|
|
204
|
+
@meta[:menus] = {}
|
|
205
|
+
@menus.each_pair do |name, menu|
|
|
206
|
+
@meta[:menus][name] = {entries: menu.to_a, properties: menu.properties}
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
module MetaModelSupport
|
|
213
|
+
def pre_run
|
|
214
|
+
if !(mdl = @assets[:model])
|
|
215
|
+
act = action
|
|
216
|
+
begin
|
|
217
|
+
act = act.parent
|
|
218
|
+
raise E2Error.new("Model not found in tree for action: #{action.name}") unless act
|
|
219
|
+
mdl = act.*.assets[:model]
|
|
220
|
+
end until mdl
|
|
221
|
+
|
|
222
|
+
if asc = @assets[:assoc]
|
|
223
|
+
@assets[:model] = Object.const_get(asc[:class_name])
|
|
224
|
+
# raise E2Error.new("Association '#{asc}' for model '#{asc[:class_name]}' not found") unless @assets[:model]
|
|
225
|
+
else
|
|
226
|
+
@assets[:model] = mdl
|
|
227
|
+
asc = act.*.assets[:assoc]
|
|
228
|
+
@assets[:assoc] = asc if asc
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @meta[:model!] = assets[:model]
|
|
233
|
+
# @meta[:assoc!] = assets[:assoc] ? assets[:assoc][:name] : nil
|
|
234
|
+
# @meta[:meta_class!] = self.class
|
|
235
|
+
super
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def hide_pk
|
|
239
|
+
hide_fields *assets[:model].primary_keys
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def show_pk
|
|
243
|
+
show_fields *assets[:model].primary_keys
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def get_type_info name
|
|
247
|
+
model = assets[:model]
|
|
248
|
+
info = model.type_info[name]
|
|
249
|
+
unless info
|
|
250
|
+
if name =~ /^(\w+)__(\w+?)$/ # (?:___\w+)?
|
|
251
|
+
assoc = model.many_to_one_associations[$1.to_sym] || model.one_to_one_associations[$1.to_sym]
|
|
252
|
+
raise E2Error.new("Association #{$1} not found for model #{model}") unless assoc
|
|
253
|
+
m = Object.const_get(assoc[:class_name])
|
|
254
|
+
info = m.type_info.fetch($2.to_sym)
|
|
255
|
+
else
|
|
256
|
+
raise E2Error.new("Type info not found for '#{name}' in model '#{model}'")
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
info
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# def parent_model_name
|
|
263
|
+
# model = @assets[:model]
|
|
264
|
+
# prnt = action.parent
|
|
265
|
+
|
|
266
|
+
# while prnt && prnt.*.assets[:model] == model
|
|
267
|
+
# prnt = prnt.parent
|
|
268
|
+
# end
|
|
269
|
+
# m = prnt.*.assets[:model]
|
|
270
|
+
# m ? m.name : nil
|
|
271
|
+
# end
|
|
272
|
+
|
|
273
|
+
def action_defined
|
|
274
|
+
super
|
|
275
|
+
# p_model_name = parent_model_name
|
|
276
|
+
model = @assets[:model]
|
|
277
|
+
|
|
278
|
+
mt = meta_type
|
|
279
|
+
case mt
|
|
280
|
+
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
|
|
281
|
+
model.many_to_one_associations.each do |assoc_name, assoc|
|
|
282
|
+
unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
|
|
283
|
+
dc = model.type_info[assoc[:keys].first][:decode]
|
|
284
|
+
action.run_scheme :decode, model, assoc_name, dc[:search]
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
case mt
|
|
290
|
+
when :modify, :create
|
|
291
|
+
model.many_to_one_associations.each do |assoc_name, assoc|
|
|
292
|
+
unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
|
|
293
|
+
dc = model.type_info[assoc[:keys].first][:decode]
|
|
294
|
+
action.run_scheme :decode, model, assoc_name, dc[:form]
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
case mt
|
|
300
|
+
when :list #, :star_to_many_list, :many_to_one_list # list dropdowns
|
|
301
|
+
divider = false
|
|
302
|
+
model.one_to_many_associations.merge(model.many_to_many_associations).each do |assoc_name, assoc|
|
|
303
|
+
unless assoc[:propagate] == false
|
|
304
|
+
menu(:item_menu).divider unless divider
|
|
305
|
+
divider ||= true
|
|
306
|
+
menu(:item_menu).option :"#{assoc_name}!", icon: "list" # , click: "action.show_assoc($index, \"#{assoc_name}!\")"
|
|
307
|
+
action.run_scheme :star_to_many, :"#{assoc_name}!", assoc
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
case mt
|
|
313
|
+
when :modify, :create
|
|
314
|
+
model.type_info.each do |field, info|
|
|
315
|
+
case info[:type]
|
|
316
|
+
when :blob_store
|
|
317
|
+
action.run_scheme :blob_store, model, field
|
|
318
|
+
when :foreign_blob_store
|
|
319
|
+
action.run_scheme :foreign_blob_store, model, field
|
|
320
|
+
when :file_store
|
|
321
|
+
action.run_scheme :file_store, model, field
|
|
322
|
+
when :star_to_many_field
|
|
323
|
+
assoc = model.association_reflections[info[:assoc_name]] # info[:name] ?
|
|
324
|
+
raise E2Error.new("Association '#{info[:assoc_name]}' not found for model '#{model}'") unless assoc
|
|
325
|
+
action.run_scheme :star_to_many_field, assoc, field
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def unsupported_association assoc
|
|
332
|
+
raise E2Error.new("Unsupported association: #{assoc}")
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
module MetaQuerySupport
|
|
337
|
+
def query q, &blk
|
|
338
|
+
@query = q.naked
|
|
339
|
+
@query.row_proc = blk if blk
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def post_run
|
|
343
|
+
query select(*assets[:model].columns) unless @query
|
|
344
|
+
super
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def get_query # move to query ?
|
|
348
|
+
if dynamic?
|
|
349
|
+
@query || @static.get_query
|
|
350
|
+
else
|
|
351
|
+
@query
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def select *args, &blk
|
|
356
|
+
assets[:model].select(*args, &blk).ensure_primary_key.setup! (@meta[:fields] = [])
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
module MetaTabSupport
|
|
361
|
+
def select_tabs tabs, *args, &blk
|
|
362
|
+
field_tabs tabs
|
|
363
|
+
select *tabs.map{|name, fields|fields}.flatten, *args, &blk
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def field_tabs hash
|
|
367
|
+
@meta[:tabs] = hash.map{|k, v| {name: k, loc: LOCS[k], fields: v} }
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def lazy_tab tab_name
|
|
371
|
+
tabs = @meta[:tabs]
|
|
372
|
+
raise E2Error.new("No tabs defined") unless tabs
|
|
373
|
+
tab = tabs.find{|t| t[:name] == tab_name}
|
|
374
|
+
raise E2Error.new("No tab #{tab_name} defined") unless tab
|
|
375
|
+
tab[:lazy] = true
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
module MetaAngularSupport
|
|
380
|
+
def ng_execute expr
|
|
381
|
+
(@meta[:execute] ||= "") << expr + ";"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def ng_record! name, value
|
|
385
|
+
value = case value
|
|
386
|
+
when String
|
|
387
|
+
"'#{value}'"
|
|
388
|
+
when nil
|
|
389
|
+
'null'
|
|
390
|
+
else
|
|
391
|
+
value
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
"action.record['#{name}'] = #{value}"
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def ng_record name
|
|
398
|
+
"action.record['#{name}']"
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def ng_info! name, *selector, expression
|
|
402
|
+
# expression = "'#{expression}'" if expression.is_a? String
|
|
403
|
+
"action.meta.info['#{name}'].#{selector.join('.')} = #{expression}"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def ng_call name, *args
|
|
407
|
+
# TODO
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
module MetaPanelSupport
|
|
412
|
+
def pre_run
|
|
413
|
+
modal_action true
|
|
414
|
+
super
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def post_run
|
|
418
|
+
super
|
|
419
|
+
if @meta[:panel]
|
|
420
|
+
panel_panel_template 'menu_m' unless panel[:panel_template] == false
|
|
421
|
+
# modal_action false if panel[:panel_template] == false
|
|
422
|
+
panel_class '' unless panel[:class]
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def glyphicon name
|
|
427
|
+
"<span class='glyphicon glyphicon-#{name}'></span>"
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def panel
|
|
431
|
+
@meta[:panel] ||= {}
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def modal_action modal = true
|
|
435
|
+
panel[:modal_action] = modal
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def panel_template tmpl
|
|
439
|
+
panel[:template] = tmpl
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def panel_panel_template tmpl
|
|
443
|
+
panel[:panel_template] = tmpl
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def panel_class cls
|
|
447
|
+
panel[:class] = cls
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def panel_title tle
|
|
451
|
+
panel[:title] = tle
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
class MenuMeta < Meta
|
|
456
|
+
include MetaMenuSupport
|
|
457
|
+
meta_type :menu
|
|
458
|
+
|
|
459
|
+
def invoke handler
|
|
460
|
+
{}
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
class ConfirmMeta < Meta
|
|
465
|
+
include MetaPanelSupport, MetaMenuSupport
|
|
466
|
+
meta_type :confirm
|
|
467
|
+
|
|
468
|
+
def pre_run
|
|
469
|
+
super
|
|
470
|
+
panel_template 'scaffold/message'
|
|
471
|
+
panel_title LOCS[:confirmation]
|
|
472
|
+
panel_class 'modal-default'
|
|
473
|
+
|
|
474
|
+
menu :panel_menu do
|
|
475
|
+
option :approve, icon: "ok", loc: LOCS[:ok], disabled: 'action.action_pending'
|
|
476
|
+
option :cancel, icon: "remove"
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def invoke handler
|
|
481
|
+
params = handler.request.params
|
|
482
|
+
# params.merge({arguments: params.keys})
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
module MetaOnChangeSupport
|
|
487
|
+
def on_change field, &blk
|
|
488
|
+
action_name = :"#{field}_on_change"
|
|
489
|
+
act = action.define_action action_name, (blk.arity > 2 ? OnChangeGetMeta : OnChangePostMeta)
|
|
490
|
+
act.*{request &blk}
|
|
491
|
+
|
|
492
|
+
info! field, remote_onchange: action_name
|
|
493
|
+
info! field, remote_onchange_record: :true if blk.arity > 2
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
class OnChangeMeta < Meta
|
|
497
|
+
include MetaAPISupport, MetaAngularSupport
|
|
498
|
+
|
|
499
|
+
def request_meta_proc_params handler
|
|
500
|
+
if handler.request.post?
|
|
501
|
+
json = handler.post_to_json
|
|
502
|
+
[json[:value], json[:record]]
|
|
503
|
+
else
|
|
504
|
+
params = handler.request.params
|
|
505
|
+
[params["value"], params["record"]]
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def invoke handler
|
|
510
|
+
{}
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
class OnChangeGetMeta < OnChangeMeta
|
|
515
|
+
meta_type :on_change
|
|
516
|
+
|
|
517
|
+
def request_meta_proc_params handler
|
|
518
|
+
params = handler.request.params
|
|
519
|
+
[params["value"], params["record"]]
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
class OnChangePostMeta < OnChangeMeta
|
|
524
|
+
http_method :post
|
|
525
|
+
meta_type :on_change
|
|
526
|
+
|
|
527
|
+
def request_meta_proc_params handler
|
|
528
|
+
json = handler.post_to_json
|
|
529
|
+
[json[:value], json[:record]]
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
module MetaListSupport
|
|
535
|
+
include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport, MetaOnChangeSupport
|
|
536
|
+
attr_reader :filters, :orders
|
|
537
|
+
|
|
538
|
+
def pre_run
|
|
539
|
+
super
|
|
540
|
+
config.merge!(per_page: 10, use_count: false, show_item_menu: true, selectable: true) # search_active: false,
|
|
541
|
+
|
|
542
|
+
# modal_action self.class != ListMeta
|
|
543
|
+
panel_template 'scaffold/list'
|
|
544
|
+
panel_panel_template 'panels/menu_m' unless action.parent.*.assets[:model]
|
|
545
|
+
search_template 'scaffold/search'
|
|
546
|
+
panel_title "#{glyphicon('list')} #{LOCS[assets[:model].name.to_sym]}"
|
|
547
|
+
menu(:panel_menu).option :cancel, icon: "remove"
|
|
548
|
+
menu :menu do
|
|
549
|
+
properties break: 2, group_class: "btn-group-xs"
|
|
550
|
+
option :search_toggle, icon: "search", show: "action.meta.search_fields", class: "action.ui_state.search_active && 'active'", button_loc: false
|
|
551
|
+
|
|
552
|
+
# divider
|
|
553
|
+
option :refresh, icon: "refresh", button_loc: false
|
|
554
|
+
option :default_order, icon: "signal", button_loc: false
|
|
555
|
+
option :select_toggle, icon: "check", enabled: "action.meta.config.selectable", button_loc: false
|
|
556
|
+
divider
|
|
557
|
+
option :debug_info, icon: "list-alt" do
|
|
558
|
+
option :show_meta, icon: "eye-open"
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
menu :item_menu do
|
|
563
|
+
properties break: 1, group_class: "btn-group-xs"
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
@meta[:state] = [:query, :ui_state]
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def post_run
|
|
570
|
+
unless panel[:class]
|
|
571
|
+
panel_class case @meta[:fields].size
|
|
572
|
+
when 1..3; ''
|
|
573
|
+
when 4..6; 'modal-large'
|
|
574
|
+
else; 'modal-huge'
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
super
|
|
579
|
+
@meta[:primary_fields] = assets[:model].primary_keys
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
# def find_renderer type_info
|
|
583
|
+
# renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
|
|
584
|
+
# raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
|
|
585
|
+
# renderer.(self, type_info)
|
|
586
|
+
# end
|
|
587
|
+
|
|
588
|
+
def post_process
|
|
589
|
+
if fields = @meta[:search_fields]
|
|
590
|
+
fields = fields - static.get[:search_fields] if dynamic?
|
|
591
|
+
|
|
592
|
+
decorate(fields)
|
|
593
|
+
fields.each do |name|
|
|
594
|
+
type_info = get_type_info(name)
|
|
595
|
+
|
|
596
|
+
# render = info[name][:render]
|
|
597
|
+
# if not render
|
|
598
|
+
# info[name][:render] = find_renderer(type_info)
|
|
599
|
+
# else
|
|
600
|
+
# info[name][:render].merge!(find_renderer(type_info)){|key, v1, v2|v1}
|
|
601
|
+
# end
|
|
602
|
+
|
|
603
|
+
info[name][:render] ||= begin # set before :fields
|
|
604
|
+
renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
|
|
605
|
+
raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
|
|
606
|
+
renderer.(self, type_info)
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
proc = SearchRendererPostProcessors[type_info[:type]] || ListRendererPostProcessors[type_info[:type]] # ?
|
|
610
|
+
proc.(self, name, type_info) if proc
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
if fields = @meta[:fields]
|
|
615
|
+
fields = fields - static.get[:fields] if dynamic?
|
|
616
|
+
|
|
617
|
+
decorate(fields)
|
|
618
|
+
fields.each do |name|
|
|
619
|
+
type_info = get_type_info(name)
|
|
620
|
+
proc = ListRendererPostProcessors[type_info[:type]]
|
|
621
|
+
proc.(self, name, type_info) if proc
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
super
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def search_template template
|
|
629
|
+
panel[:search_template] = template
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def sortable *flds
|
|
633
|
+
flds = @meta[:fields] if flds.empty?
|
|
634
|
+
info! *flds, sort: true
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def search_live *flds
|
|
638
|
+
flds = @meta[:search_fields] if flds.empty?
|
|
639
|
+
info! *flds, search_live: true
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def searchable *flds
|
|
643
|
+
@meta.delete(:tabs)
|
|
644
|
+
@meta[:search_fields] = *flds
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def searchable_tabs tabs
|
|
648
|
+
searchable *tabs.map{|name, fields|fields}.flatten
|
|
649
|
+
field_tabs tabs
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def template
|
|
653
|
+
SearchTemplates
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def filter name, &blk
|
|
657
|
+
(@filters ||= {})[name] = blk
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def order name, &blk
|
|
661
|
+
(@orders ||= {})[name] = blk
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
module MetaApproveSupport
|
|
666
|
+
include MetaModelSupport
|
|
667
|
+
attr_reader :validations
|
|
668
|
+
|
|
669
|
+
def validate_fields *fields
|
|
670
|
+
if fields.empty?
|
|
671
|
+
@validate_fields
|
|
672
|
+
else
|
|
673
|
+
@validate_fields = assets[:model].type_info.keys & (fields + assets[:model].primary_keys).uniq
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
def before_approve handler, record
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def after_approve handler, record
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def validate_and_approve handler, record, json
|
|
684
|
+
static.before_approve(handler, record)
|
|
685
|
+
record.valid?
|
|
686
|
+
validate_record(handler, record)
|
|
687
|
+
if record.errors.empty?
|
|
688
|
+
static.after_approve(handler, record)
|
|
689
|
+
true
|
|
690
|
+
else
|
|
691
|
+
false
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
def allocate_record handler, json
|
|
696
|
+
model = assets[:model]
|
|
697
|
+
json_rec = json[:record]
|
|
698
|
+
handler.permit json_rec.is_a?(Hash)
|
|
699
|
+
val_fields = (dynamic? ? static.validate_fields : @validate_fields) || model.type_info.keys
|
|
700
|
+
handler.permit (json_rec.keys - val_fields).empty?
|
|
701
|
+
|
|
702
|
+
record = model.call(json_rec)
|
|
703
|
+
record.validate_fields = val_fields
|
|
704
|
+
record
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def record handler, record
|
|
708
|
+
{errors: nil}
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def invoke handler
|
|
712
|
+
json = handler.post_to_json
|
|
713
|
+
record = allocate_record(handler, json)
|
|
714
|
+
validate_and_approve(handler, record, json) ? static.record(handler, record) : {record: record.to_hash, errors: record.errors}
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def validate name, &blk
|
|
718
|
+
(@validations ||= {})[name] = blk
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def validate_record handler, record
|
|
722
|
+
@validations.each do |name, val|
|
|
723
|
+
unless record.errors[name]
|
|
724
|
+
result = val.(record, handler)
|
|
725
|
+
record.errors.add(name, result) if result
|
|
726
|
+
end
|
|
727
|
+
end if @validations
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
def post_run
|
|
731
|
+
super
|
|
732
|
+
validate_fields *action.parent.*.get[:fields] unless validate_fields
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
module MetaViewSupport
|
|
737
|
+
include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport
|
|
738
|
+
|
|
739
|
+
def pre_run
|
|
740
|
+
super
|
|
741
|
+
panel_template 'scaffold/view'
|
|
742
|
+
panel_title LOCS[:view_title]
|
|
743
|
+
|
|
744
|
+
menu(:panel_menu).option :cancel, icon: "remove"
|
|
745
|
+
action.parent.*.menu(:item_menu).option action.name, icon: "file", button_loc: false
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def post_process
|
|
749
|
+
if fields = @meta[:fields]
|
|
750
|
+
fields = fields - static.get[:fields] if dynamic?
|
|
751
|
+
|
|
752
|
+
decorate(fields)
|
|
753
|
+
fields.each do |name|
|
|
754
|
+
type_info = get_type_info(name)
|
|
755
|
+
proc = ListRendererPostProcessors[type_info[:type]]
|
|
756
|
+
proc.(self, name, type_info) if proc
|
|
757
|
+
end
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
super
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
(FormRendererPostProcessors ||= {}).merge!(
|
|
765
|
+
boolean: lambda{|meta, field, info|
|
|
766
|
+
meta.info[field][:render].merge! true_value: info[:true_value], false_value: info[:false_value]
|
|
767
|
+
meta.info[field][:dont_strip] = info[:dont_strip] if info[:dont_strip]
|
|
768
|
+
},
|
|
769
|
+
date: lambda{|meta, field, info|
|
|
770
|
+
meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format]
|
|
771
|
+
if date_to = info[:other_date]
|
|
772
|
+
meta.info[field][:render].merge! other_date: date_to #, format: info[:format], model_format: info[:model_format]
|
|
773
|
+
meta.hide_fields date_to
|
|
774
|
+
elsif time = info[:other_time]
|
|
775
|
+
meta.info[field][:render].merge! other_time: time
|
|
776
|
+
meta.hide_fields time
|
|
777
|
+
end
|
|
778
|
+
},
|
|
779
|
+
time: lambda{|meta, field, info|
|
|
780
|
+
meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format]
|
|
781
|
+
},
|
|
782
|
+
decimal_date: lambda{|meta, field, info|
|
|
783
|
+
FormRendererPostProcessors[:date].(meta, field, info)
|
|
784
|
+
meta.info! field, type: :decimal_date
|
|
785
|
+
},
|
|
786
|
+
decimal_time: lambda{|meta, field, info|
|
|
787
|
+
FormRendererPostProcessors[:time].(meta, field, info)
|
|
788
|
+
meta.info! field, type: :decimal_time
|
|
789
|
+
},
|
|
790
|
+
datetime: lambda{|meta, field, info|
|
|
791
|
+
meta.info[field][:render].merge! date_format: info[:date_format], time_format: info[:time_format], date_model_format: info[:date_model_format], time_model_format: info[:time_model_format]
|
|
792
|
+
},
|
|
793
|
+
# date_range: lambda{|meta, field, info|
|
|
794
|
+
# meta.info[field][:render].merge! other_date: info[:other_date], format: info[:format], model_format: info[:model_format]
|
|
795
|
+
# meta.hide_fields info[:other_date]
|
|
796
|
+
# meta.info[field][:decimal_date] = true if info[:validations][:decimal_date]
|
|
797
|
+
# },
|
|
798
|
+
list_select: lambda{|meta, field, info|
|
|
799
|
+
meta.info[field][:render].merge! list: info[:list]
|
|
800
|
+
},
|
|
801
|
+
many_to_one: lambda{|meta, field, info|
|
|
802
|
+
field_info = meta.info[field]
|
|
803
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
804
|
+
field_info[:fields] = info[:keys]
|
|
805
|
+
field_info[:type] = info[:otype]
|
|
806
|
+
# field_info[:table_loc] = LOCS[info[:assoc_name]]
|
|
807
|
+
|
|
808
|
+
(info[:keys] - [field]).each do |of|
|
|
809
|
+
f_info = meta.info.fetch(of)
|
|
810
|
+
f_info[:hidden] = true
|
|
811
|
+
f_info[:type] = meta.assets[:model].type_info[of].fetch(:otype)
|
|
812
|
+
end
|
|
813
|
+
},
|
|
814
|
+
file_store: lambda{|meta, field, info|
|
|
815
|
+
meta.info[field][:render].merge! multiple: info[:multiple]
|
|
816
|
+
# meta[:model] = meta.action.model.table_name
|
|
817
|
+
},
|
|
818
|
+
star_to_many_field: lambda{|meta, field, info|
|
|
819
|
+
field_info = meta.info[field]
|
|
820
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
821
|
+
# meta.info[field][:render].merge! multiple: info[:multiple]
|
|
822
|
+
# field_info = meta.info[field]
|
|
823
|
+
# field_info[:resource] ||= "#{Handler::API}#{meta.model.namespace}/#{info[:assoc_name]}"
|
|
824
|
+
}
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
(ListRendererPostProcessors ||= {}).merge!(
|
|
828
|
+
boolean: lambda{|meta, field, info|
|
|
829
|
+
meta.info! field, type: :boolean # move to meta ?
|
|
830
|
+
meta.info[field][:render] ||= {}
|
|
831
|
+
meta.info[field][:render].merge! true_value: info[:true_value], false_value: info[:false_value]
|
|
832
|
+
},
|
|
833
|
+
list_select: lambda{|meta, field, info|
|
|
834
|
+
meta.info! field, type: :list_select
|
|
835
|
+
meta.info[field][:render] ||= {}
|
|
836
|
+
meta.info[field][:render].merge! list: info[:list]
|
|
837
|
+
},
|
|
838
|
+
datetime: lambda{|meta, field, info|
|
|
839
|
+
meta.info! field, type: :datetime
|
|
840
|
+
},
|
|
841
|
+
decimal_date: lambda{|meta, field, info|
|
|
842
|
+
meta.info! field, type: :decimal_date
|
|
843
|
+
},
|
|
844
|
+
decimal_time: lambda{|meta, field, info|
|
|
845
|
+
meta.info! field, type: :decimal_time
|
|
846
|
+
},
|
|
847
|
+
# date_range: lambda{|meta, field, info|
|
|
848
|
+
# meta.info[field][:type] = :decimal_date if info[:validations][:decimal_date] # ? :decimal_date : :date
|
|
849
|
+
# }
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
(SearchRendererPostProcessors ||= {}).merge!(
|
|
853
|
+
many_to_one: lambda{|meta, field, info|
|
|
854
|
+
model = meta.assets[:model]
|
|
855
|
+
if model.type_info[field]
|
|
856
|
+
keys = info[:keys]
|
|
857
|
+
else
|
|
858
|
+
meta.check_static_meta
|
|
859
|
+
model = Object.const_get(model.many_to_one_associations[field[/^\w+?(?=__)/].to_sym][:class_name])
|
|
860
|
+
# meta.action.define_action :"#{info[:assoc_name]}!" do # assoc_#{aname}
|
|
861
|
+
# define_action :decode, DecodeEntryMeta, assoc: model.association_reflections[info[:assoc_name]] do
|
|
862
|
+
# run_scheme :default_many_to_one
|
|
863
|
+
# end
|
|
864
|
+
# end
|
|
865
|
+
|
|
866
|
+
# verify associations ?
|
|
867
|
+
# model = Model.models.fetch(field[/^\w+?(?=__)/].to_sym)
|
|
868
|
+
keys = info[:keys].map{|k| :"#{model.table_name}__#{k}"}
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
field_info = meta.info[field]
|
|
872
|
+
field_info[:assoc] = :"#{info[:assoc_name]}!"
|
|
873
|
+
field_info[:fields] = keys
|
|
874
|
+
field_info[:type] = info[:otype]
|
|
875
|
+
# field_info[:table_loc] = LOCS[info[:assoc_name]]
|
|
876
|
+
|
|
877
|
+
(keys - [field]).each do |of|
|
|
878
|
+
f_info = meta.info[of]
|
|
879
|
+
raise E2Error.new("Missing searchable field: '#{of}' in model '#{meta.assets[:model]}'") unless f_info
|
|
880
|
+
f_info[:hidden_search] = true
|
|
881
|
+
f_info[:type] = model.type_info[of].fetch(:otype)
|
|
882
|
+
end
|
|
883
|
+
},
|
|
884
|
+
date: lambda{|meta, field, info|
|
|
885
|
+
meta.info[field][:render] ||= {}
|
|
886
|
+
meta.info[field][:render].merge! format: info[:format], model_format: info[:model_format] # Model::DEFAULT_DATE_FORMAT
|
|
887
|
+
},
|
|
888
|
+
decimal_date: lambda{|meta, field, info|
|
|
889
|
+
SearchRendererPostProcessors[:date].(meta, field, info)
|
|
890
|
+
}
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
(DefaultFormRenderers ||= {}).merge!(
|
|
894
|
+
date: lambda{|meta, info|
|
|
895
|
+
info[:other_date] ? Templates.date_range : (info[:other_time] ? Templates.date_time : Templates.date_picker)
|
|
896
|
+
|
|
897
|
+
},
|
|
898
|
+
time: lambda{|meta, info| Templates.time_picker},
|
|
899
|
+
datetime: lambda{|meta, info| Templates.datetime_picker},
|
|
900
|
+
file_store: lambda{|meta, info| Templates.file_store},
|
|
901
|
+
blob: lambda{|meta, info| Templates.blob}, # !!!
|
|
902
|
+
blob_store: lambda{|meta, info| Templates.blob},
|
|
903
|
+
foreign_blob_store: lambda{|meta, info| Templates.blob},
|
|
904
|
+
string: lambda{|meta, info| Templates.input_text(info[:length])},
|
|
905
|
+
text: lambda{|meta, info| Templates.text},
|
|
906
|
+
integer: lambda{|meta, info| Templates.integer},
|
|
907
|
+
decimal: lambda{|meta, info| Templates.decimal},
|
|
908
|
+
decimal_date: lambda{|meta, info| DefaultFormRenderers[:date].(meta, info)},
|
|
909
|
+
decimal_time: lambda{|meta, info| Templates.time_picker},
|
|
910
|
+
email: lambda{|meta, info| Templates.email(info[:length])},
|
|
911
|
+
password: lambda{|meta, info| Templates.password(info[:length])},
|
|
912
|
+
# date_range: lambda{|meta, info| Templates.date_range},
|
|
913
|
+
boolean: lambda{|meta, info| Templates.checkbox_buttons(optional: !info[:required])},
|
|
914
|
+
currency: lambda{|meta, info| Templates.currency},
|
|
915
|
+
list_select: lambda{|meta, info|
|
|
916
|
+
length = info[:list].length
|
|
917
|
+
if length <= 3
|
|
918
|
+
Templates.list_buttons(optional: !info[:required])
|
|
919
|
+
elsif length <= 15
|
|
920
|
+
max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
921
|
+
Templates.list_bsselect(max_length, optional: !info[:required])
|
|
922
|
+
else
|
|
923
|
+
max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
924
|
+
Templates.list_select(max_length, optional: !info[:required])
|
|
925
|
+
end
|
|
926
|
+
},
|
|
927
|
+
star_to_many_field: lambda{|meta, info| Templates.scaffold},
|
|
928
|
+
many_to_one: lambda{|meta, info| # Templates.scaffold_picker
|
|
929
|
+
tmpl_type = info[:decode][:form]
|
|
930
|
+
case
|
|
931
|
+
when tmpl_type[:scaffold]; Templates.scaffold_picker
|
|
932
|
+
when tmpl_type[:list]; Templates.bsselect_picker
|
|
933
|
+
when tmpl_type[:typeahead];Templates.typeahead_picker
|
|
934
|
+
else
|
|
935
|
+
raise E2Error.new("Unknown decode type #{tmpl_type}")
|
|
936
|
+
end
|
|
937
|
+
}, # required/opt
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
(DefaultSearchRenderers ||= {}).merge!(
|
|
941
|
+
date: lambda{|meta, info| SearchTemplates.date_range},
|
|
942
|
+
decimal_date: lambda{|meta, info| SearchTemplates.date_range},
|
|
943
|
+
integer: lambda{|meta, info| SearchTemplates.integer_range},
|
|
944
|
+
string: lambda{|meta, info| SearchTemplates.input_text},
|
|
945
|
+
boolean: lambda{|meta, info| SearchTemplates.checkbox_buttons},
|
|
946
|
+
list_select: lambda{|meta, info|
|
|
947
|
+
length = info[:list].length
|
|
948
|
+
if length <= 3
|
|
949
|
+
SearchTemplates.list_buttons
|
|
950
|
+
elsif length <= 15
|
|
951
|
+
# max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
952
|
+
SearchTemplates.list_bsselect(multiple: info[:multiple])
|
|
953
|
+
else
|
|
954
|
+
# max_length = info[:list].max_by{|a|a.last.length}.last.length
|
|
955
|
+
SearchTemplates.list_select
|
|
956
|
+
end
|
|
957
|
+
},
|
|
958
|
+
many_to_one: lambda{|meta, info|
|
|
959
|
+
tmpl_type = info[:decode][:search]
|
|
960
|
+
case
|
|
961
|
+
when tmpl_type[:scaffold]; SearchTemplates.scaffold_picker(multiple: tmpl_type[:multiple])
|
|
962
|
+
when tmpl_type[:list]; SearchTemplates.bsselect_picker(multiple: tmpl_type[:multiple])
|
|
963
|
+
when tmpl_type[:typeahead];SearchTemplates.typeahead_picker
|
|
964
|
+
else
|
|
965
|
+
raise E2Error.new("Unknown decode type #{tmpl_type}")
|
|
966
|
+
end
|
|
967
|
+
}
|
|
968
|
+
)
|
|
969
|
+
end
|