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.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -0
  3. data/Rakefile +1 -1
  4. data/app/{engine2actions.coffee → actions.coffee} +192 -142
  5. data/app/app.coffee +0 -0
  6. data/app/app.css +4 -0
  7. data/app/engine2.coffee +87 -175
  8. data/app/modal.coffee +133 -0
  9. data/bower.json +2 -1
  10. data/conf/message.yaml +0 -0
  11. data/conf/message_pl.yaml +0 -0
  12. data/config.coffee +11 -2
  13. data/engine2.gemspec +2 -2
  14. data/lib/engine2.rb +12 -10
  15. data/lib/engine2/action.rb +1290 -134
  16. data/lib/engine2/{meta/array_meta.rb → action/array.rb} +4 -3
  17. data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +16 -15
  18. data/lib/engine2/action/delete.rb +65 -0
  19. data/lib/engine2/action/form.rb +16 -0
  20. data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +118 -85
  21. data/lib/engine2/action/link.rb +117 -0
  22. data/lib/engine2/{meta/list_meta.rb → action/list.rb} +61 -62
  23. data/lib/engine2/action/save.rb +30 -0
  24. data/lib/engine2/action/view.rb +8 -0
  25. data/lib/engine2/action_node.rb +221 -0
  26. data/lib/engine2/core.rb +120 -77
  27. data/lib/engine2/handler.rb +9 -10
  28. data/lib/engine2/model.rb +4 -20
  29. data/lib/engine2/models/Files.rb +2 -1
  30. data/lib/engine2/models/UserInfo.rb +6 -3
  31. data/lib/engine2/post_bootstrap.rb +3 -2
  32. data/lib/engine2/pre_bootstrap.rb +1 -0
  33. data/lib/engine2/scheme.rb +98 -47
  34. data/lib/engine2/templates.rb +1 -0
  35. data/lib/engine2/type_info.rb +6 -4
  36. data/lib/engine2/version.rb +2 -1
  37. data/package.json +12 -10
  38. data/public/favicon.ico +0 -0
  39. data/public/img/ajax-loader-dark.gif +0 -0
  40. data/public/img/ajax-loader.gif +0 -0
  41. data/views/fields/blob.slim +0 -0
  42. data/views/fields/bs_select.slim +0 -0
  43. data/views/fields/bsselect_picker.slim +0 -0
  44. data/views/fields/bsselect_picker_opt.slim +0 -0
  45. data/views/fields/checkbox.slim +0 -0
  46. data/views/fields/checkbox_buttons.slim +0 -0
  47. data/views/fields/checkbox_buttons_opt.slim +0 -0
  48. data/views/fields/currency.slim +0 -0
  49. data/views/fields/date.slim +0 -0
  50. data/views/fields/date_range.slim +0 -0
  51. data/views/fields/date_time.slim +0 -0
  52. data/views/fields/datetime.slim +0 -0
  53. data/views/fields/decimal.slim +0 -0
  54. data/views/fields/decimal_date.slim +0 -0
  55. data/views/fields/decimal_time.slim +0 -0
  56. data/views/fields/email.slim +0 -0
  57. data/views/fields/file_store.slim +7 -7
  58. data/views/fields/input_text.slim +0 -0
  59. data/views/fields/integer.slim +0 -0
  60. data/views/fields/list_bsselect.slim +0 -0
  61. data/views/fields/list_bsselect_opt.slim +0 -0
  62. data/views/fields/list_buttons.slim +0 -0
  63. data/views/fields/list_buttons_opt.slim +0 -0
  64. data/views/fields/list_select.slim +0 -0
  65. data/views/fields/list_select_opt.slim +0 -0
  66. data/views/fields/password.slim +0 -0
  67. data/views/fields/radio_checkbox.slim +0 -0
  68. data/views/fields/scaffold.slim +0 -0
  69. data/views/fields/scaffold_picker.slim +0 -0
  70. data/views/fields/select_picker.slim +0 -0
  71. data/views/fields/select_picker_opt.slim +0 -0
  72. data/views/fields/text_area.slim +1 -0
  73. data/views/fields/time.slim +0 -0
  74. data/views/fields/typeahead_picker.slim +0 -0
  75. data/views/index.slim +3 -3
  76. data/views/infra/index.slim +0 -0
  77. data/views/infra/inspect.slim +20 -0
  78. data/views/modals/close_m.slim +0 -0
  79. data/views/modals/confirm_m.slim +0 -0
  80. data/views/modals/empty_m.slim +0 -0
  81. data/views/modals/menu_m.slim +1 -1
  82. data/views/modals/yes_no_m.slim +0 -0
  83. data/views/panels/menu_m.slim +1 -1
  84. data/views/scaffold/confirm.slim +0 -0
  85. data/views/scaffold/fields.slim +0 -0
  86. data/views/scaffold/form.slim +0 -0
  87. data/views/scaffold/form_collapse.slim +0 -0
  88. data/views/scaffold/form_tabs.slim +0 -0
  89. data/views/scaffold/list.slim +0 -0
  90. data/views/scaffold/message.slim +0 -0
  91. data/views/scaffold/search.slim +0 -0
  92. data/views/scaffold/search_collapse.slim +0 -0
  93. data/views/scaffold/search_tabs.slim +0 -0
  94. data/views/scaffold/view.slim +0 -0
  95. data/views/scaffold/view_collapse.slim +0 -0
  96. data/views/scaffold/view_tabs.slim +0 -0
  97. data/views/search_fields/bsmselect_picker.slim +0 -0
  98. data/views/search_fields/bsselect_picker.slim +0 -0
  99. data/views/search_fields/checkbox.slim +0 -0
  100. data/views/search_fields/checkbox2.slim +0 -0
  101. data/views/search_fields/checkbox_buttons.slim +0 -0
  102. data/views/search_fields/date_range.slim +0 -0
  103. data/views/search_fields/decimal_date_range.slim +0 -0
  104. data/views/search_fields/input_text.slim +0 -0
  105. data/views/search_fields/integer.slim +0 -0
  106. data/views/search_fields/integer_range.slim +0 -0
  107. data/views/search_fields/list_bsmselect.slim +0 -0
  108. data/views/search_fields/list_bsselect.slim +0 -0
  109. data/views/search_fields/list_buttons.slim +0 -0
  110. data/views/search_fields/list_select.slim +0 -0
  111. data/views/search_fields/scaffold_picker.slim +0 -0
  112. data/views/search_fields/select_picker.slim +0 -0
  113. data/views/search_fields/typeahead_picker.slim +0 -0
  114. metadata +41 -42
  115. data/lib/engine2/meta.rb +0 -1216
  116. data/lib/engine2/meta/delete_meta.rb +0 -60
  117. data/lib/engine2/meta/form_meta.rb +0 -15
  118. data/lib/engine2/meta/link_meta.rb +0 -134
  119. data/lib/engine2/meta/save_meta.rb +0 -50
  120. data/lib/engine2/meta/view_meta.rb +0 -7
  121. data/public/__sinatra__/404.png +0 -0
  122. 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 ListMeta < Meta
5
- meta_type :list
6
- include MetaListSupport, MetaQuerySupport
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)]}.inject{|q, c| q | c})
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 = order.qualify(model.table_name) if model.type_info[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.include?(name)
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] ? name.qualify(model.table_name) : Sequel.expr(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 ManyToOneListMeta < ListMeta
142
- meta_type :many_to_one_list
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 StarToManyListMeta < ListMeta
153
- meta_type :star_to_many_list
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| k.qualify(model.table_name)}.zip(parent_keys)]
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| k.qualify(j_table)}
195
- r_keys = assoc[:right_keys].map{|k| k.qualify(j_table)}
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, l_keys_vals).exists)
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.qualify.left_join(j_table, r_keys_vals).filter(l_keys_vals)
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 StarToManyLinkListMeta < StarToManyListMeta
211
- meta_type :star_to_many_link_list
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
- action.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
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 StarToManyFieldMeta < StarToManyListMeta
222
- meta_type :star_to_many_field
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
- unlinked = handler.param_to_json(:unlinked)
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.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}
239
- # query = query.or *unlinked.map{|unl| Hash[model.primary_keys.zip(split_keys(unl))]}.inject{|q, c| q | c}
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 = query.where *unlinked.map{|unl| pks.zip(split_keys(unl)).sql_negate}
244
- # query = query.or *linked.map{|ln| model.primary_keys.zip(split_keys(ln))}
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
- query = linked.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}
250
+ linked.reduce(query){|q, ln|q.or pks.zip(split_keys(ln))}
249
251
  when :many_to_many
250
- query = linked.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}.distinct
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
- query
256
- end
257
- end
258
-
259
- class StarToManyFieldUnlinkMeta < Meta
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
- def pre_run
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 StarToManyFieldLinkListMeta < StarToManyFieldMeta
269
- meta_type :star_to_many_field_link_list
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 'scaffold/list'
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
- action.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
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,8 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Engine2
5
+ class ViewAction < Action
6
+ include ActionViewSupport, ActionQuerySupport
7
+ end
8
+ 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