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
|
@@ -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
|