engine2 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,117 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Engine2
|
5
|
+
class StarToManyLinkAction < Action
|
6
|
+
include ActionModelSupport
|
7
|
+
http_method :post
|
8
|
+
action_type :star_to_many_link
|
9
|
+
|
10
|
+
def pre_run
|
11
|
+
super
|
12
|
+
execute "action.errors || [action.parent().invoke(), action.panel_close()]"
|
13
|
+
end
|
14
|
+
|
15
|
+
def invoke handler
|
16
|
+
json = handler.post_to_json
|
17
|
+
parent = json[:parent_id]
|
18
|
+
ids = json[:ids]
|
19
|
+
handler.permit parent && ids
|
20
|
+
invoke_link_db handler, split_keys(parent), ids
|
21
|
+
end
|
22
|
+
|
23
|
+
def invoke_link_db handler, parent, ids
|
24
|
+
model = assets[:model]
|
25
|
+
assoc = assets[:assoc]
|
26
|
+
|
27
|
+
case assoc[:type]
|
28
|
+
when :one_to_many
|
29
|
+
model.db.transaction do
|
30
|
+
pk = Hash[assoc[:keys].zip(parent)]
|
31
|
+
ids.each do |id|
|
32
|
+
model.where(model.primary_keys_hash(split_keys(id))).update(pk)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
when :many_to_many
|
36
|
+
p_pk = Hash[assoc[:left_keys].zip(parent)]
|
37
|
+
values = ids.map do |id|
|
38
|
+
p_pk.merge Hash[assoc[:right_keys].zip(split_keys(id))]
|
39
|
+
end
|
40
|
+
model.db[assoc[:join_table]].multi_insert values
|
41
|
+
else unsupported_association
|
42
|
+
end
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class StarToManyUnlinkActionBase < Action
|
48
|
+
include ActionModelSupport
|
49
|
+
http_method :delete
|
50
|
+
|
51
|
+
def pre_run
|
52
|
+
super
|
53
|
+
execute "[action.parent().invoke(), action.panel_close()]"
|
54
|
+
end
|
55
|
+
|
56
|
+
def invoke_unlink_db handler, parent, ids
|
57
|
+
model = assets[:model]
|
58
|
+
assoc = assets[:assoc]
|
59
|
+
|
60
|
+
case assoc[:type]
|
61
|
+
when :one_to_many
|
62
|
+
keys = assoc[:keys]
|
63
|
+
if keys.all?{|k|model.db_schema[k][:allow_null] == true}
|
64
|
+
model.db.transaction do
|
65
|
+
ids.each do |id|
|
66
|
+
model.where(model.primary_keys_hash(split_keys(id))).update(Hash[keys.zip([nil])])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
handler.halt_method_not_allowed LOCS[:"non_nullable"]
|
71
|
+
end
|
72
|
+
when :many_to_many
|
73
|
+
model.db.transaction do
|
74
|
+
p_pk = Hash[assoc[:left_keys].zip(parent)]
|
75
|
+
ds = model.db[assoc[:join_table]]
|
76
|
+
ids.each do |id|
|
77
|
+
ds.where(p_pk & Hash[assoc[:right_keys].zip(split_keys(id))]).delete
|
78
|
+
end
|
79
|
+
end
|
80
|
+
else unsupported_association
|
81
|
+
end
|
82
|
+
{}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class StarToManyUnlinkAction < StarToManyUnlinkActionBase
|
87
|
+
action_type :star_to_many_unlink
|
88
|
+
|
89
|
+
def pre_run
|
90
|
+
super
|
91
|
+
node.parent.parent.*.menu(:item_menu).option :confirm_unlink, icon: "minus", show: "action.selected_size() == 0", button_loc: false
|
92
|
+
end
|
93
|
+
|
94
|
+
def invoke handler
|
95
|
+
handler.permit id = handler.params[:id]
|
96
|
+
handler.permit parent = handler.params[:parent_id]
|
97
|
+
invoke_unlink_db handler, split_keys(parent), [id]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class StarToManyBulkUnlinkAction < StarToManyUnlinkActionBase
|
102
|
+
action_type :star_to_many_bulk_unlink
|
103
|
+
|
104
|
+
def pre_run
|
105
|
+
super
|
106
|
+
node.parent.parent.*.select_toggle_menu
|
107
|
+
node.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_unlink, icon: "minus", show: "action.selected_size() > 0", button_loc: false
|
108
|
+
end
|
109
|
+
|
110
|
+
def invoke handler
|
111
|
+
ids = handler.param_to_json(:ids)
|
112
|
+
handler.permit parent = handler.params[:parent_id]
|
113
|
+
handler.permit ids.is_a?(Array)
|
114
|
+
invoke_unlink_db handler, split_keys(parent), ids
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Engine2
|
4
|
-
class
|
5
|
-
|
6
|
-
include
|
5
|
+
class ListAction < Action
|
6
|
+
action_type :list
|
7
|
+
include ActionListSupport, ActionQuerySupport
|
7
8
|
|
8
9
|
(DefaultFilters ||= {}).merge!(
|
9
10
|
string: lambda{|query, name, value, type_info, hash|
|
@@ -48,7 +49,7 @@ module Engine2
|
|
48
49
|
if keys.length == 1
|
49
50
|
query.where(name => value)
|
50
51
|
else
|
51
|
-
query.where(keys.map{|k| hash[k]}.transpose.map{|vals| Hash[keys.zip(vals)]}.
|
52
|
+
query.where(keys.map{|k| hash[k]}.transpose.map{|vals| Hash[keys.zip(vals)]}.reduce{|q, c| q | c})
|
52
53
|
end
|
53
54
|
when :list_select
|
54
55
|
query.where(name => value) # decode in sql query ?
|
@@ -63,6 +64,9 @@ module Engine2
|
|
63
64
|
else
|
64
65
|
nil
|
65
66
|
end
|
67
|
+
},
|
68
|
+
boolean: lambda{|query, name, value, type_info, hash|
|
69
|
+
query.where(name => value)
|
66
70
|
}
|
67
71
|
)
|
68
72
|
|
@@ -89,7 +93,7 @@ module Engine2
|
|
89
93
|
if order_blk = (@orders && @orders[order]) || (dynamic? && (static.orders && static.orders[order]))
|
90
94
|
query = order_blk.(query, handler)
|
91
95
|
else
|
92
|
-
order =
|
96
|
+
order = model.table_name.q(order) if model.type_info[order]
|
93
97
|
query = query.order(order)
|
94
98
|
end
|
95
99
|
|
@@ -113,13 +117,13 @@ module Engine2
|
|
113
117
|
sfields = lookup(:search_fields)
|
114
118
|
handler.permit sfields
|
115
119
|
hash.each_pair do |name, value|
|
116
|
-
handler.permit sfields.
|
120
|
+
handler.permit name = sfields.find{|sf|sf.to_sym == name}
|
117
121
|
|
118
122
|
type_info = get_type_info(name)
|
119
123
|
query = if filter = (@filters && @filters[name]) || (dynamic? && (static.filters && static.filters[name]))
|
120
124
|
filter.(query, hash, handler)
|
121
125
|
elsif filter = DefaultFilters[type_info[:otype]]
|
122
|
-
name = model.type_info[name] ?
|
126
|
+
name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
|
123
127
|
filter.(query, name, value, type_info, hash)
|
124
128
|
else
|
125
129
|
raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
|
@@ -138,8 +142,8 @@ module Engine2
|
|
138
142
|
#
|
139
143
|
# Many to One
|
140
144
|
#
|
141
|
-
class
|
142
|
-
|
145
|
+
class ManyToOneListAction < ListAction
|
146
|
+
action_type :many_to_one_list
|
143
147
|
|
144
148
|
def pre_run
|
145
149
|
super
|
@@ -149,29 +153,14 @@ module Engine2
|
|
149
153
|
#
|
150
154
|
# * to Many
|
151
155
|
#
|
152
|
-
class
|
153
|
-
|
156
|
+
class StarToManyListAction < ListAction
|
157
|
+
action_type :star_to_many_list
|
154
158
|
def pre_run
|
155
159
|
super
|
156
160
|
menu(:panel_menu).option_at 0, :cancel, icon: "remove"
|
157
161
|
panel_title "#{:list.icon} #{LOCS[assets[:assoc][:name]]}"
|
158
162
|
end
|
159
163
|
|
160
|
-
# def decode_panel_title handler
|
161
|
-
# if handler.initial?
|
162
|
-
# # Hash[assets[:model].primary_keys.zip(split_keys(id))]]
|
163
|
-
# p action.parent.decode_entry.*.invoke_decode([[handler.params[:parent_id]]])
|
164
|
-
# panel_title "ADFASDF"
|
165
|
-
# end
|
166
|
-
# end
|
167
|
-
|
168
|
-
# def post_run
|
169
|
-
# super
|
170
|
-
# unless @request_meta_proc
|
171
|
-
# request{|h| decode_panel_title h}
|
172
|
-
# end
|
173
|
-
# end
|
174
|
-
|
175
164
|
def list_context query, handler
|
176
165
|
handler.permit parent = handler.params[:parent_id]
|
177
166
|
model = assets[:model]
|
@@ -180,7 +169,7 @@ module Engine2
|
|
180
169
|
case assoc[:type]
|
181
170
|
when :one_to_many
|
182
171
|
keys = assoc[:keys]
|
183
|
-
condition = parent_keys.all?(&:empty?) ? false : Hash[keys.map{|k|
|
172
|
+
condition = parent_keys.all?(&:empty?) ? false : Hash[keys.map{|k| model.table_name.q(k)}.zip(parent_keys)]
|
184
173
|
if handler.params[:negate]
|
185
174
|
query = query.exclude(condition)
|
186
175
|
query = query.or(Hash[keys.zip([nil])]) if keys.all?{|k|model.db_schema[k][:allow_null] == true} # type_info[:required] ?
|
@@ -191,35 +180,50 @@ module Engine2
|
|
191
180
|
when :many_to_many
|
192
181
|
q_pk = model.primary_keys_qualified
|
193
182
|
j_table = assoc[:join_table]
|
194
|
-
l_keys = assoc[:left_keys].map{|k|
|
195
|
-
r_keys = assoc[:right_keys].map{|k|
|
183
|
+
l_keys = assoc[:left_keys].map{|k| j_table.q(k)}
|
184
|
+
r_keys = assoc[:right_keys].map{|k| j_table.q(k)}
|
196
185
|
r_keys_vals = Hash[r_keys.zip(q_pk)]
|
197
186
|
l_keys_vals = parent_keys.all?(&:empty?) ? false : Hash[l_keys.zip(parent_keys)]
|
198
187
|
|
199
188
|
if handler.params[:negate]
|
200
|
-
query.exclude(model.db[j_table].select(nil).where(r_keys_vals
|
189
|
+
query.exclude(model.db[j_table].select(nil).where(r_keys_vals & l_keys_vals).exists)
|
201
190
|
else
|
202
191
|
# query.qualify.join(j_table, [r_keys_vals, l_keys_vals])
|
203
|
-
query.
|
192
|
+
if joins = query.opts[:join] and joins.find{|j|j.table == j_table}
|
193
|
+
query
|
194
|
+
else
|
195
|
+
query.qualify.left_join(j_table, r_keys_vals)
|
196
|
+
end.filter(l_keys_vals)
|
204
197
|
end
|
205
198
|
else unsupported_association
|
206
199
|
end
|
207
200
|
end
|
201
|
+
|
202
|
+
def post_run
|
203
|
+
super
|
204
|
+
request do |h|
|
205
|
+
if h.initial? && nd = node.parent.nodes[:decode_entry]
|
206
|
+
action = nd.*
|
207
|
+
rec = action.invoke_decode(h, [[h.params[:parent_id]]]).first
|
208
|
+
panel_title "#{static.panel[:title]} - #{rec}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
208
212
|
end
|
209
213
|
|
210
|
-
class
|
211
|
-
|
214
|
+
class StarToManyLinkListAction < StarToManyListAction
|
215
|
+
action_type :star_to_many_link_list
|
212
216
|
def pre_run
|
213
217
|
super
|
214
218
|
panel_title LOCS[:link_title]
|
215
219
|
menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
|
216
|
-
|
220
|
+
node.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
|
217
221
|
end
|
218
222
|
end
|
219
223
|
|
220
224
|
# *_to_many_field
|
221
|
-
class
|
222
|
-
|
225
|
+
class StarToManyFieldAction < StarToManyListAction
|
226
|
+
action_type :star_to_many_field
|
223
227
|
|
224
228
|
def pre_run
|
225
229
|
super
|
@@ -228,53 +232,48 @@ module Engine2
|
|
228
232
|
end
|
229
233
|
|
230
234
|
def list_context query, handler
|
231
|
-
|
232
|
-
linked = handler.param_to_json(:linked)
|
233
|
-
query = super(query, handler)
|
235
|
+
changes = handler.param_to_json(:changes)
|
234
236
|
model = assets[:model]
|
237
|
+
unlinked = changes[:unlink].to_a + changes[:delete].to_a + changes[:modify].to_a.map{|m|Sequel::join_keys(model.primary_keys.map{|k|m[k]})}
|
238
|
+
linked = changes[:link]
|
239
|
+
query = super(query, handler)
|
240
|
+
|
235
241
|
pks = model.primary_keys_qualified
|
236
242
|
|
237
243
|
if handler.params[:negate]
|
238
|
-
query = unlinked.
|
239
|
-
|
240
|
-
query = query.where *linked.map{|ln| pks.zip(split_keys(ln)).sql_negate}
|
241
|
-
|
244
|
+
query = unlinked.reduce(query){|q, unl|q.or pks.zip(split_keys(unl))}
|
245
|
+
query = linked.reduce(query){|q, ln|q.where(pks.zip(split_keys(ln)).sql_negate)}
|
242
246
|
else
|
243
|
-
query =
|
244
|
-
|
245
|
-
# query = query.or *linked.map{|ln| Hash[model.primary_keys.zip(split_keys(ln))]}.inject{|q, c| q | c}
|
246
|
-
case assets[:assoc][:type]
|
247
|
+
query = unlinked.reduce(query){|q, unl|q.where(pks.zip(split_keys(unl)).sql_negate)}
|
248
|
+
query = case assets[:assoc][:type]
|
247
249
|
when :one_to_many
|
248
|
-
|
250
|
+
linked.reduce(query){|q, ln|q.or pks.zip(split_keys(ln))}
|
249
251
|
when :many_to_many
|
250
|
-
|
252
|
+
linked.reduce(query){|q, ln|q.or pks.zip(split_keys(ln))}.distinct
|
251
253
|
else unsupported_association
|
252
254
|
end unless linked.empty?
|
253
255
|
end
|
254
256
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
meta_type :star_to_many_field_unlink
|
257
|
+
added = changes[:create].to_a + changes[:modify].to_a
|
258
|
+
cols = get_query.columns
|
259
|
+
query = added.reduce query do |q, a|
|
260
|
+
q.union(model.db.select(*cols.map{|c|a[c]}), all: true, alias: model.table_name)
|
261
|
+
end
|
261
262
|
|
262
|
-
|
263
|
-
super
|
264
|
-
action.parent.parent.*.menu(:item_menu).option :confirm_unlink, icon: "minus", show: "action.selected_size() == 0", button_loc: false
|
263
|
+
query
|
265
264
|
end
|
266
265
|
end
|
267
266
|
|
268
|
-
class
|
269
|
-
|
267
|
+
class StarToManyFieldLinkListAction < StarToManyFieldAction
|
268
|
+
action_type :star_to_many_field_link_list
|
270
269
|
|
271
270
|
def pre_run
|
272
271
|
super
|
273
272
|
modal_action true
|
274
|
-
panel_panel_template
|
273
|
+
panel_panel_template nil
|
275
274
|
panel_title LOCS[:link_title]
|
276
275
|
menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
|
277
|
-
|
276
|
+
node.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
|
278
277
|
end
|
279
278
|
end
|
280
279
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Engine2
|
5
|
+
|
6
|
+
class SaveAction < Action
|
7
|
+
include ActionSaveSupport
|
8
|
+
end
|
9
|
+
|
10
|
+
class InsertAction < SaveAction
|
11
|
+
include ActionInsertSupport
|
12
|
+
action_type :approve
|
13
|
+
end
|
14
|
+
|
15
|
+
class UpdateAction < SaveAction
|
16
|
+
include ActionUpdateSupport
|
17
|
+
action_type :approve
|
18
|
+
end
|
19
|
+
|
20
|
+
class StarToManyFieldInsertAction < InsertAction
|
21
|
+
self.validate_only = true
|
22
|
+
action_type :star_to_many_field_approve
|
23
|
+
end
|
24
|
+
|
25
|
+
class StarToManyFieldUpdateAction < UpdateAction
|
26
|
+
self.validate_only = true
|
27
|
+
action_type :star_to_many_field_approve
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Engine2
|
5
|
+
|
6
|
+
class ActionNode < BasicObject
|
7
|
+
ACCESS_FORBIDDEN ||= ->h{false}
|
8
|
+
attr_reader :parent, :name, :number, :nodes, :recheck_access
|
9
|
+
attr_reader :meta_proc, :access_block
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :count
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize parent, name, action_class, assets
|
16
|
+
ActionNode.count += 1
|
17
|
+
@number = ActionNode.count
|
18
|
+
@parent = parent
|
19
|
+
@name = name
|
20
|
+
@action = action_class.new(self, assets)
|
21
|
+
@nodes = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def * &blk
|
25
|
+
@meta_proc = @meta_proc ? @meta_proc.chain(&blk) : blk if blk
|
26
|
+
@action
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :action :*
|
30
|
+
|
31
|
+
def access! &blk
|
32
|
+
::Kernel.raise E2Error.new("Access for node #{name} already defined") if @access_block
|
33
|
+
@access_block = blk
|
34
|
+
end
|
35
|
+
|
36
|
+
def access_forbidden!
|
37
|
+
access! &ACCESS_FORBIDDEN
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_access! handler
|
41
|
+
!@access_block || @access_block.(handler)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_scheme name, *args, &blk
|
45
|
+
result = instance_exec(*args, &SCHEMES[name])
|
46
|
+
result.instance_eval(&blk) if blk
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_node name, action_class = InlineAction.inherit, assets = {}, &blk
|
51
|
+
::Kernel.raise E2Error.new("ActionNode #{name} already defined") if @nodes[name]
|
52
|
+
node = @nodes[name] = ActionNode.new(self, name, action_class, assets)
|
53
|
+
node.*.pre_run
|
54
|
+
define_singleton_method! name do |&ablk| # forbidden list
|
55
|
+
node.instance_eval(&ablk) if ablk
|
56
|
+
node
|
57
|
+
end
|
58
|
+
node.instance_eval(&blk) if blk
|
59
|
+
node.*.node_defined
|
60
|
+
node
|
61
|
+
end
|
62
|
+
|
63
|
+
def define_action name, action_class = InlineAction.inherit, assets = {}, &blk
|
64
|
+
define_node name, action_class, assets do
|
65
|
+
self.* &blk
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def define_invoke name, action_class = InlineAction.inherit, assets = {}, &blk
|
70
|
+
define_node name, action_class, assets do
|
71
|
+
self.*.define_invoke &blk
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def define_node_bundle name, *nodes
|
76
|
+
define_singleton_method!(name) do |&blk|
|
77
|
+
if blk
|
78
|
+
nodes.each{|a|__send__(a, &blk)} # if @nodes[node] ?
|
79
|
+
else
|
80
|
+
ActionNodeBundle.new(self, nodes)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def define_singleton_method! name, &blk
|
86
|
+
class << self;self;end.instance_eval do # __realclass__
|
87
|
+
define_method name, &blk
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def [] name
|
92
|
+
@nodes[name]
|
93
|
+
end
|
94
|
+
|
95
|
+
def nodes_info handler
|
96
|
+
info = nodes.reduce({}) do |h, (name, a)|
|
97
|
+
action = a.*
|
98
|
+
act = {
|
99
|
+
action_type: action.action_type,
|
100
|
+
method: action.http_method,
|
101
|
+
number: a.number,
|
102
|
+
terminal: a.nodes.empty?,
|
103
|
+
meta: !action.meta.empty?
|
104
|
+
}
|
105
|
+
|
106
|
+
act[:access] = true if !recheck_access && a.check_access!(handler)
|
107
|
+
act[:recheck_access] = true if a.recheck_access
|
108
|
+
|
109
|
+
if Handler::development?
|
110
|
+
act[:action_class] = action.class
|
111
|
+
act[:access_block] = a.access_block if a.access_block
|
112
|
+
act[:model] = action.assets[:model]
|
113
|
+
end
|
114
|
+
|
115
|
+
h[name] = act
|
116
|
+
h
|
117
|
+
end
|
118
|
+
|
119
|
+
info.first[1][:default] = true unless nodes.empty?
|
120
|
+
info
|
121
|
+
end
|
122
|
+
|
123
|
+
def access_info handler
|
124
|
+
@nodes.reduce({}) do |h, (name, a)|
|
125
|
+
h[name] = a.check_access!(handler)
|
126
|
+
h
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def recheck_access!
|
131
|
+
@recheck_access = true
|
132
|
+
end
|
133
|
+
|
134
|
+
def each_node &blk
|
135
|
+
# no self
|
136
|
+
@nodes.each_pair do |n, a|
|
137
|
+
a.each_node(&blk) if yield a
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_a_rec root = true, result = [], &blk # optimize
|
142
|
+
if root && (yield self)
|
143
|
+
result << self
|
144
|
+
@nodes.each_pair do |n, a|
|
145
|
+
if yield a
|
146
|
+
result << a
|
147
|
+
a.to_a_rec(false, result, &blk)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
result
|
152
|
+
end
|
153
|
+
|
154
|
+
def inspect
|
155
|
+
"ActionNode: #{@name}, action: #{@action.class}, action_type: #{@action.action_type}"
|
156
|
+
end
|
157
|
+
|
158
|
+
def setup_node_tree
|
159
|
+
time = ::Time.now
|
160
|
+
|
161
|
+
model_nodes = {}
|
162
|
+
each_node do |node|
|
163
|
+
if model = node.*.assets[:model]
|
164
|
+
model_name = model.name.to_sym
|
165
|
+
model.synchronize_type_info
|
166
|
+
model_nodes[model_name] = node.to_a_rec{|a| !a.*.assets[:assoc]}
|
167
|
+
node.run_scheme(model_name) if SCHEMES[model_name, false]
|
168
|
+
false
|
169
|
+
else
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
thefts = 0
|
175
|
+
each_node do |node|
|
176
|
+
action = node.*
|
177
|
+
model = action.assets[:model]
|
178
|
+
assoc = action.assets[:assoc]
|
179
|
+
if model && assoc
|
180
|
+
if source_nodes = model_nodes[model.name.to_sym]
|
181
|
+
source_node = source_nodes.select{|sa| sa.meta_proc && sa.*.class >= action.class}
|
182
|
+
# source_node = source_nodes.select{|sa| sa.meta_proc && action.class <= sa.*.class}
|
183
|
+
unless source_node.empty?
|
184
|
+
raise E2Error.new("Multiple action candidates for #{node.inspect} found in '#{source_node.inspect}'") if source_node.size > 1
|
185
|
+
# puts "#{node.inspect} => #{source_node.inspect}\n"
|
186
|
+
action.instance_eval(&source_node.first.meta_proc)
|
187
|
+
thefts += 1
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
action.instance_eval(&node.meta_proc) if node.meta_proc
|
193
|
+
true
|
194
|
+
end
|
195
|
+
|
196
|
+
each_node do |node|
|
197
|
+
node.*.post_run
|
198
|
+
node.*.freeze_action
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
202
|
+
::Kernel::puts "ACTION NODES: #{ActionNode.count}, Time: #{::Time.now - time}, Thefts: #{thefts}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def p *args
|
206
|
+
::Kernel::p *args
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
class ActionNodeBundle
|
212
|
+
def initialize node, node_names
|
213
|
+
@node = node
|
214
|
+
@node_names = node_names
|
215
|
+
end
|
216
|
+
|
217
|
+
def method_missing name, *args, &blk
|
218
|
+
@node_names.each{|an| @node[an].__send__(name, *args, &blk)}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|