engine2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/Rakefile +138 -0
  4. data/conf/message.yaml +93 -0
  5. data/conf/message_pl.yaml +93 -0
  6. data/engine2.gemspec +34 -0
  7. data/lib/engine2.rb +34 -0
  8. data/lib/engine2/action.rb +217 -0
  9. data/lib/engine2/core.rb +572 -0
  10. data/lib/engine2/handler.rb +134 -0
  11. data/lib/engine2/meta.rb +969 -0
  12. data/lib/engine2/meta/decode_meta.rb +110 -0
  13. data/lib/engine2/meta/delete_meta.rb +73 -0
  14. data/lib/engine2/meta/form_meta.rb +144 -0
  15. data/lib/engine2/meta/infra_meta.rb +292 -0
  16. data/lib/engine2/meta/link_meta.rb +133 -0
  17. data/lib/engine2/meta/list_meta.rb +284 -0
  18. data/lib/engine2/meta/save_meta.rb +63 -0
  19. data/lib/engine2/meta/view_meta.rb +22 -0
  20. data/lib/engine2/model.rb +390 -0
  21. data/lib/engine2/models/Files.rb +38 -0
  22. data/lib/engine2/models/UserInfo.rb +24 -0
  23. data/lib/engine2/post_bootstrap.rb +83 -0
  24. data/lib/engine2/pre_bootstrap.rb +27 -0
  25. data/lib/engine2/scheme.rb +202 -0
  26. data/lib/engine2/templates.rb +229 -0
  27. data/lib/engine2/type_info.rb +342 -0
  28. data/lib/engine2/version.rb +9 -0
  29. data/public/assets/javascripts.js +13 -0
  30. data/public/assets/styles.css +4 -0
  31. data/public/css/angular-motion.css +1022 -0
  32. data/public/css/angular-ui-tree.min.css +1 -0
  33. data/public/css/app.css +196 -0
  34. data/public/css/bootstrap-additions.css +1560 -0
  35. data/public/css/bootstrap.min.css +11 -0
  36. data/public/css/font-awesome.min.css +4 -0
  37. data/public/favicon.ico +0 -0
  38. data/public/fonts/FontAwesome.otf +0 -0
  39. data/public/fonts/fontawesome-webfont.eot +0 -0
  40. data/public/fonts/fontawesome-webfont.svg +655 -0
  41. data/public/fonts/fontawesome-webfont.ttf +0 -0
  42. data/public/fonts/fontawesome-webfont.woff +0 -0
  43. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  44. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/public/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/public/images/file.png +0 -0
  50. data/public/images/folder-closed.png +0 -0
  51. data/public/images/folder.png +0 -0
  52. data/public/images/node-closed-2.png +0 -0
  53. data/public/images/node-closed-light.png +0 -0
  54. data/public/images/node-closed.png +0 -0
  55. data/public/images/node-opened-2.png +0 -0
  56. data/public/images/node-opened-light.png +0 -0
  57. data/public/images/node-opened.png +0 -0
  58. data/public/img/ajax-loader-dark.gif +0 -0
  59. data/public/img/ajax-loader-light.gif +0 -0
  60. data/public/img/ajax-loader.gif +0 -0
  61. data/public/js/angular-animate.js +4115 -0
  62. data/public/js/angular-cookies.js +322 -0
  63. data/public/js/angular-local-storage.js +455 -0
  64. data/public/js/angular-route.js +1022 -0
  65. data/public/js/angular-sanitize.js +717 -0
  66. data/public/js/angular-strap.js +4339 -0
  67. data/public/js/angular-strap.tpl.js +43 -0
  68. data/public/js/angular-ui-tree.js +1569 -0
  69. data/public/js/angular.js +30714 -0
  70. data/public/js/i18n/angular-locale_pl.js +115 -0
  71. data/public/js/lodash.custom.min.js +97 -0
  72. data/public/js/ng-file-upload-shim.min.js +2 -0
  73. data/public/js/ng-file-upload.min.js +3 -0
  74. data/views/app.coffee +3 -0
  75. data/views/engine2.coffee +557 -0
  76. data/views/engine2actions.coffee +849 -0
  77. data/views/engine2templates.coffee +0 -0
  78. data/views/fields/blob.slim +22 -0
  79. data/views/fields/bs_select.slim +10 -0
  80. data/views/fields/bsselect_picker.slim +18 -0
  81. data/views/fields/bsselect_picker_opt.slim +22 -0
  82. data/views/fields/checkbox.slim +11 -0
  83. data/views/fields/checkbox_buttons.slim +6 -0
  84. data/views/fields/checkbox_buttons_opt.slim +8 -0
  85. data/views/fields/currency.slim +10 -0
  86. data/views/fields/date.slim +21 -0
  87. data/views/fields/date_range.slim +44 -0
  88. data/views/fields/date_time.slim +42 -0
  89. data/views/fields/datetime.slim +42 -0
  90. data/views/fields/decimal.slim +11 -0
  91. data/views/fields/decimal_date.slim +22 -0
  92. data/views/fields/decimal_time.slim +26 -0
  93. data/views/fields/email.slim +13 -0
  94. data/views/fields/file_store.slim +61 -0
  95. data/views/fields/input_text.slim +14 -0
  96. data/views/fields/integer.slim +11 -0
  97. data/views/fields/list_bsselect.slim +18 -0
  98. data/views/fields/list_bsselect_opt.slim +21 -0
  99. data/views/fields/list_buttons.slim +3 -0
  100. data/views/fields/list_buttons_opt.slim +5 -0
  101. data/views/fields/list_select.slim +11 -0
  102. data/views/fields/list_select_opt.slim +15 -0
  103. data/views/fields/password.slim +14 -0
  104. data/views/fields/radio_checkbox.slim +10 -0
  105. data/views/fields/scaffold.slim +2 -0
  106. data/views/fields/scaffold_picker.slim +20 -0
  107. data/views/fields/select_picker.slim +12 -0
  108. data/views/fields/select_picker_opt.slim +16 -0
  109. data/views/fields/text_area.slim +10 -0
  110. data/views/fields/time.slim +22 -0
  111. data/views/fields/typeahead_picker.slim +25 -0
  112. data/views/index.slim +44 -0
  113. data/views/infra/index.slim +5 -0
  114. data/views/infra/inspect.slim +81 -0
  115. data/views/modals/close_m.slim +15 -0
  116. data/views/modals/confirm_m.slim +19 -0
  117. data/views/modals/empty_m.slim +12 -0
  118. data/views/modals/menu_m.slim +13 -0
  119. data/views/modals/yes_no_m.slim +19 -0
  120. data/views/panels/menu_m.slim +9 -0
  121. data/views/scaffold/confirm.slim +3 -0
  122. data/views/scaffold/fields.slim +10 -0
  123. data/views/scaffold/form.slim +11 -0
  124. data/views/scaffold/list.slim +42 -0
  125. data/views/scaffold/message.slim +3 -0
  126. data/views/scaffold/search.slim +20 -0
  127. data/views/scaffold/view.slim +18 -0
  128. data/views/search_fields/bsmselect_picker.slim +25 -0
  129. data/views/search_fields/bsselect_picker.slim +24 -0
  130. data/views/search_fields/checkbox.slim +11 -0
  131. data/views/search_fields/checkbox2.slim +14 -0
  132. data/views/search_fields/checkbox_buttons.slim +10 -0
  133. data/views/search_fields/date_range.slim +46 -0
  134. data/views/search_fields/decimal_date_range.slim +47 -0
  135. data/views/search_fields/input_text.slim +18 -0
  136. data/views/search_fields/integer.slim +18 -0
  137. data/views/search_fields/integer_range.slim +27 -0
  138. data/views/search_fields/list_bsmselect.slim +24 -0
  139. data/views/search_fields/list_bsselect.slim +22 -0
  140. data/views/search_fields/list_buttons.slim +8 -0
  141. data/views/search_fields/list_select.slim +17 -0
  142. data/views/search_fields/scaffold_picker.slim +19 -0
  143. data/views/search_fields/select_picker.slim +17 -0
  144. data/views/search_fields/typeahead_picker.slim +25 -0
  145. metadata +327 -0
@@ -0,0 +1,133 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ class StarToManyLinkMeta < Meta
5
+ include MetaModelSupport
6
+ http_method :post
7
+ meta_type :star_to_many_link
8
+
9
+ def invoke handler
10
+ json = handler.post_to_json
11
+ parent = json[:parent_id]
12
+ ids = json[:ids]
13
+ handler.permit parent && ids
14
+ model = assets[:model]
15
+ assoc = assets[:assoc]
16
+ case assoc[:type]
17
+ when :one_to_many
18
+ model.db.transaction do
19
+ self.class.one_to_many_link_db model, assoc, split_keys(parent), ids
20
+ end
21
+ {}
22
+ when :many_to_many
23
+ self.class.many_to_many_link_db model, assoc, split_keys(parent), ids
24
+ {}
25
+ else unsupported_association
26
+ end
27
+ end
28
+
29
+ def self.one_to_many_link_db model, assoc, parent, ids
30
+ pk = Hash[assoc[:keys].zip(parent)]
31
+ ids.each do |id|
32
+ model.where(model.primary_keys_hash(Sequel::split_keys(id))).update(pk)
33
+ end
34
+ end
35
+
36
+ def self.many_to_many_link_db model, assoc, parent, ids
37
+ p_pk = Hash[assoc[:left_keys].zip(parent)]
38
+ values = ids.map do |id|
39
+ p_pk.merge Hash[assoc[:right_keys].zip(Sequel::split_keys(id))]
40
+ end
41
+ model.db[assoc[:join_table]].multi_insert values
42
+ end
43
+ end
44
+
45
+ class StarToManyUnlinkMetaBase < Meta
46
+ include MetaModelSupport
47
+ http_method :delete
48
+
49
+ def one_to_many_unlink_db handler, ids
50
+ model = assets[:model]
51
+ assoc = assets[:assoc]
52
+ keys = assoc[:keys]
53
+ if keys.all?{|k|model.db_schema[k][:allow_null] == true}
54
+ model.db.transaction do
55
+ self.class.one_to_many_unlink_db model, assoc, ids
56
+ end
57
+ {}
58
+ else
59
+ handler.halt_method_not_allowed LOCS[:"non_nullable"]
60
+ end
61
+ end
62
+
63
+ def self.one_to_many_unlink_db model, assoc, ids
64
+ keys = assoc[:keys]
65
+ ids.each do |id|
66
+ model.where(model.primary_keys_hash(Sequel::split_keys(id))).update(Hash[keys.zip([nil])])
67
+ end
68
+ end
69
+
70
+ def many_to_many_unlink_db parent, ids
71
+ model = assets[:model]
72
+ assoc = assets[:assoc]
73
+
74
+ model.db.transaction do
75
+ self.class.many_to_many_unlink_db model, assoc, Sequel::split_keys(parent), ids
76
+ end
77
+ {}
78
+ end
79
+
80
+ def self.many_to_many_unlink_db model, assoc, parent, ids
81
+ p_pk = Hash[assoc[:left_keys].zip(parent)]
82
+ ds = model.db[assoc[:join_table]]
83
+ ids.each do |id|
84
+ ds.where(p_pk, Hash[assoc[:right_keys].zip(Sequel::split_keys(id))]).delete
85
+ end
86
+ end
87
+ end
88
+
89
+ class StarToManyUnlinkMeta < StarToManyUnlinkMetaBase
90
+ meta_type :star_to_many_unlink
91
+
92
+ def pre_run
93
+ super
94
+ action.parent.parent.*.menu(:item_menu).option :confirm_unlink, icon: "minus", show: "action.selected_size() == 0", button_loc: false
95
+ end
96
+
97
+ def invoke handler
98
+ handler.permit id = handler.params[:id]
99
+ assoc = assets[:assoc]
100
+ case assoc[:type]
101
+ when :one_to_many
102
+ one_to_many_unlink_db(handler, [id])
103
+ when :many_to_many
104
+ handler.permit parent = handler.params[:parent_id]
105
+ many_to_many_unlink_db(parent, [id])
106
+ else unsupported_association
107
+ end
108
+ end
109
+ end
110
+
111
+ class StarToManyBulkUnlinkMeta < StarToManyUnlinkMetaBase
112
+ meta_type :star_to_many_bulk_unlink
113
+
114
+ def pre_run
115
+ super
116
+ action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_unlink, icon: "minus", show: "action.selected_size() > 0", button_loc: false
117
+ end
118
+
119
+ def invoke handler
120
+ ids = handler.param_to_json(:ids)
121
+ handler.permit ids.is_a?(Array)
122
+ assoc = assets[:assoc]
123
+ case assoc[:type]
124
+ when :one_to_many
125
+ one_to_many_unlink_db(handler, ids)
126
+ when :many_to_many
127
+ handler.permit parent = handler.params[:parent_id]
128
+ many_to_many_unlink_db(parent, ids)
129
+ else unsupported_association
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,284 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ class ListMeta < Meta
5
+ meta_type :list
6
+ include MetaListSupport, MetaQuerySupport
7
+
8
+ (DefaultFilters ||= {}).merge!(
9
+ string: lambda{|query, name, value, type_info, hash|
10
+ case type_info[:type]
11
+ when :many_to_one
12
+ query.where(name => value)
13
+ else
14
+ query.where(name.like("%#{value}%"))
15
+ end
16
+ },
17
+ date: lambda{|query, name, value, type_info, hash|
18
+ if value.is_a? Hash
19
+ from, to = value[:from], value[:to]
20
+ if from && to
21
+ query.where(name => from .. to)
22
+ elsif from
23
+ query.where(name >= Sequel.string_to_date(from))
24
+ elsif to
25
+ query.where(name <= Sequel.string_to_date(to))
26
+ else
27
+ query # ?
28
+ end
29
+ else
30
+ query.where(name => Sequel.string_to_date(value))
31
+ end
32
+ },
33
+ integer: lambda{|query, name, value, type_info, hash|
34
+ if value.is_a? Hash
35
+ from, to = value[:from], value[:to]
36
+ if from && to
37
+ query.where(name => from .. to)
38
+ else
39
+ query.where(from ? name >= from.to_i : name <= to.to_i)
40
+ end
41
+ elsif value.is_a? Integer
42
+ query.where(name => value)
43
+ elsif value.is_a? Array
44
+ if !value.empty?
45
+ case type_info[:type]
46
+ when :many_to_one
47
+ keys = type_info[:keys]
48
+ if keys.length == 1
49
+ query.where(name => value)
50
+ else
51
+ query.where(keys.map{|k| hash[k]}.transpose.map{|vals| Hash[keys.zip(vals)]}.inject{|q, c| q | c
52
+ })
53
+ end
54
+ when :list_select
55
+ query.where(name => value) # decode in sql query ?
56
+ when :integer
57
+ query
58
+ else
59
+ nil
60
+ end
61
+ else
62
+ nil
63
+ end
64
+ else
65
+ nil
66
+ end
67
+ }
68
+ )
69
+
70
+ def post_run
71
+ query select(*assets[:model].columns.reject{|col| assets[:model].type_info[col][:length].to_i > 20}.take(10)) unless @query
72
+ super
73
+ end
74
+
75
+ def invoke handler
76
+ params = handler.params
77
+ model = assets[:model]
78
+ query = list_context(get_query, handler)
79
+
80
+ if search = params[:search]
81
+ query = list_search(query, handler, search)
82
+ end
83
+
84
+ count = query.count if lookup(:config, :use_count)
85
+
86
+ if order_str = params[:order]
87
+ order = order_str.to_sym
88
+ handler.permit lookup(:info, order, :sort)
89
+
90
+ if order_blk = (@orders && @orders[order]) || (dynamic? && (static.orders && static.orders[order]))
91
+ query = order_blk.(query, handler)
92
+ else
93
+ order = order.qualify(model.table_name) if model.type_info[order]
94
+ query = query.order(order)
95
+ end
96
+
97
+ query = query.reverse if params[:asc] == "true"
98
+ end
99
+
100
+ per_page = lookup(:config, :per_page)
101
+ page = params[:page].to_i
102
+ handler.permit page >= 0 && page < 1000
103
+
104
+ query = query.limit(per_page, page)
105
+
106
+ res = {entries: query.all}
107
+ res[:count] = count if count
108
+ res
109
+ end
110
+
111
+ def list_search query, handler, search
112
+ hash = JSON.parse(search, symbolize_names: true) rescue handler.halt_forbidden
113
+ model = assets[:model]
114
+ sfields = lookup(:search_fields)
115
+ handler.permit sfields
116
+ hash.each_pair do |name, value|
117
+ handler.permit sfields.include?(name)
118
+
119
+ type_info = get_type_info(name)
120
+ query = if filter = (@filters && @filters[name]) || (dynamic? && (static.filters && static.filters[name]))
121
+ filter.(query, hash, handler)
122
+ elsif filter = DefaultFilters[type_info[:otype]]
123
+ name = model.type_info[name] ? name.qualify(model.table_name) : Sequel.expr(name)
124
+ filter.(query, name, value, type_info, hash)
125
+ else
126
+ raise E2Error.new("Filter not found for field #{name} in model #{model}") unless filter
127
+ end
128
+
129
+ handler.permit query
130
+ end
131
+ query
132
+ end
133
+
134
+ def list_context query, handler
135
+ query
136
+ end
137
+ end
138
+
139
+ #
140
+ # Many to One
141
+ #
142
+ class ManyToOneListMeta < ListMeta
143
+ meta_type :many_to_one_list
144
+
145
+ def pre_run
146
+ super
147
+ config.merge!(selectable: false)
148
+ end
149
+ end
150
+
151
+ #
152
+ # * to Many
153
+ #
154
+ class StarToManyListMeta < ListMeta
155
+ meta_type :star_to_many_list
156
+ def pre_run
157
+ super
158
+ panel_title "#{glyphicon('list')} #{LOCS[assets[:assoc][:name]]}"
159
+ end
160
+
161
+ # def decode_panel_title handler
162
+ # if handler.initial?
163
+ # # Hash[assets[:model].primary_keys.zip(split_keys(id))]]
164
+ # p action.parent.decode_entry.*.invoke_decode([[handler.params[:parent_id]]])
165
+ # panel_title "ADFASDF"
166
+ # end
167
+ # end
168
+
169
+ # def post_run
170
+ # super
171
+ # unless @request_meta_proc
172
+ # request{|h| decode_panel_title h}
173
+ # end
174
+ # end
175
+
176
+ def list_context query, handler
177
+ handler.permit parent = handler.params[:parent_id]
178
+ model = assets[:model]
179
+ assoc = assets[:assoc]
180
+ case assoc[:type]
181
+ when :one_to_many
182
+ keys = assoc[:keys]
183
+ condition = parent.empty? ? false : Hash[keys.map{|k| k.qualify(model.table_name)}.zip(split_keys(parent))]
184
+ if handler.params[:negate]
185
+ query = query.exclude(condition)
186
+ query = query.or(Hash[keys.zip([nil])]) if keys.all?{|k|model.db_schema[k][:allow_null] == true} # type_info[:required] ?
187
+ query
188
+ else
189
+ query.where(condition)
190
+ end
191
+ when :many_to_many
192
+ q_pk = model.primary_keys_qualified
193
+ 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)}
196
+ r_keys_vals = Hash[r_keys.zip(q_pk)]
197
+ l_keys_vals = parent.empty? ? false : Hash[l_keys.zip(split_keys(parent))]
198
+
199
+ if handler.params[:negate]
200
+ query.exclude(model.db[j_table].select(nil).where(r_keys_vals, l_keys_vals).exists)
201
+ else
202
+ # 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)
204
+ end
205
+ else unsupported_association
206
+ end
207
+ end
208
+ end
209
+
210
+ class StarToManyLinkListMeta < StarToManyListMeta
211
+ meta_type :star_to_many_link_list
212
+ def pre_run
213
+ super
214
+ config.merge!(selectable: false)
215
+ panel_title LOCS[:link_title]
216
+ menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
217
+ action.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
218
+ end
219
+ end
220
+
221
+ # *_to_many_field
222
+ class StarToManyFieldMeta < StarToManyListMeta
223
+ meta_type :star_to_many_field
224
+
225
+ def pre_run
226
+ super
227
+ modal_action false
228
+ panel_panel_template false
229
+ # panel_template nil
230
+ end
231
+
232
+ def list_context query, handler
233
+ unlinked = handler.param_to_json(:unlinked)
234
+ linked = handler.param_to_json(:linked)
235
+ query = super(query, handler)
236
+ model = assets[:model]
237
+ pks = model.primary_keys_qualified
238
+
239
+ if handler.params[:negate]
240
+ query = unlinked.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}
241
+ # query = query.or *unlinked.map{|unl| Hash[model.primary_keys.zip(split_keys(unl))]}.inject{|q, c| q | c}
242
+ query = query.where *linked.map{|ln| pks.zip(split_keys(ln)).sql_negate}
243
+
244
+ else
245
+ query = query.where *unlinked.map{|unl| pks.zip(split_keys(unl)).sql_negate}
246
+ # query = query.or *linked.map{|ln| model.primary_keys.zip(split_keys(ln))}
247
+ # query = query.or *linked.map{|ln| Hash[model.primary_keys.zip(split_keys(ln))]}.inject{|q, c| q | c}
248
+ case assets[:assoc][:type]
249
+ when :one_to_many
250
+ query = linked.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}
251
+ when :many_to_many
252
+ query = linked.map{|ln| pks.zip(split_keys(ln))}.inject(query){|q, c| q.or c}.distinct
253
+ else unsupported_association
254
+ end unless linked.empty?
255
+ end
256
+
257
+ query
258
+ end
259
+ end
260
+
261
+ class StarToManyFieldUnlinkMeta < Meta
262
+ meta_type :star_to_many_field_unlink
263
+
264
+ def pre_run
265
+ super
266
+ action.parent.parent.*.menu(:item_menu).option :confirm_unlink, icon: "minus", show: "action.selected_size() == 0", button_loc: false
267
+ end
268
+ end
269
+
270
+ class StarToManyFieldLinkListMeta < StarToManyFieldMeta
271
+ meta_type :star_to_many_field_link_list
272
+
273
+ def pre_run
274
+ super
275
+ config.merge!(selectable: false)
276
+ modal_action true
277
+ panel_panel_template 'scaffold/list'
278
+ panel_title LOCS[:link_title]
279
+ menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
280
+ action.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
281
+ end
282
+ end
283
+ end
284
+
@@ -0,0 +1,63 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+
5
+ class SaveMeta < Meta
6
+ include MetaApproveSupport
7
+ http_method :post
8
+
9
+ def validate_and_approve handler, record, json
10
+ record.skip_save_refresh = true
11
+ record.raise_on_save_failure = false
12
+ model = assets[:model]
13
+ assoc = assets[:assoc]
14
+ mtm_insert = record.new? && assoc && assoc[:type] == :many_to_many
15
+
16
+ save = lambda do|c|
17
+ if super(handler, record, json)
18
+ result = record.save(transaction: false, validate: false)
19
+ if result && mtm_insert
20
+ handler.permit parent_id = json[:parent_id]
21
+ model.db[assoc[:join_table]].insert(assoc[:left_keys] + assoc[:right_keys], split_keys(parent_id) + record.primary_key_values)
22
+ end
23
+ result
24
+ end
25
+ end
26
+ (model.validation_in_transaction || mtm_insert) ? model.db.transaction(&save) : save.(nil)
27
+ end
28
+ end
29
+
30
+ class InsertMeta < SaveMeta
31
+ meta_type :save
32
+ def allocate_record handler, json
33
+ record = super(handler, json)
34
+ record.instance_variable_set(:"@new", true)
35
+ model = assets[:model]
36
+ model.primary_keys.each{|k|record.values.delete k} unless model.natural_key
37
+ handler.permit !record.has_primary_key? unless model.natural_key
38
+ record
39
+ end
40
+ end
41
+
42
+ class UpdateMeta < SaveMeta
43
+ meta_type :save
44
+ def allocate_record handler, json
45
+ record = super(handler, json)
46
+ model = assets[:model]
47
+ handler.permit record.has_primary_key? unless model.natural_key
48
+ record
49
+ end
50
+ end
51
+
52
+ module TimeStampMeta
53
+ def before_approve handler, record
54
+ super
55
+ puts "before approve"
56
+ end
57
+
58
+ def after_approve handler, record
59
+ super
60
+ puts "after approve"
61
+ end
62
+ end
63
+ end