engine2 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
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