engine2 1.0.8 → 1.0.9

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -3
  3. data/app/actions.coffee +87 -50
  4. data/app/app.css +2 -1
  5. data/app/engine2.coffee +37 -19
  6. data/app/modal.coffee +8 -3
  7. data/bower.json +3 -2
  8. data/conf/message.yaml +3 -0
  9. data/conf/message_pl.yaml +5 -2
  10. data/config.coffee +16 -15
  11. data/engine2.gemspec +1 -1
  12. data/lib/engine2/action.rb +51 -23
  13. data/lib/engine2/action/array.rb +94 -1
  14. data/lib/engine2/action/decode.rb +29 -3
  15. data/lib/engine2/action/delete.rb +1 -2
  16. data/lib/engine2/action/infra.rb +5 -4
  17. data/lib/engine2/action/list.rb +70 -25
  18. data/lib/engine2/action/save.rb +0 -2
  19. data/lib/engine2/action_node.rb +2 -4
  20. data/lib/engine2/core.rb +30 -21
  21. data/lib/engine2/handler.rb +5 -5
  22. data/lib/engine2/model.rb +18 -9
  23. data/lib/engine2/models/Files.rb +2 -0
  24. data/lib/engine2/scheme.rb +1 -6
  25. data/lib/engine2/templates.rb +32 -6
  26. data/lib/engine2/type_info.rb +9 -5
  27. data/lib/engine2/version.rb +1 -1
  28. data/package.json +9 -8
  29. data/views/fields/checkbox_button.slim +6 -0
  30. data/views/fields/checkbox_buttons.slim +1 -1
  31. data/views/fields/list_mbuttons.slim +9 -0
  32. data/views/fields/list_mbuttons_opt.slim +11 -0
  33. data/views/fields/list_mselect.slim +12 -0
  34. data/views/fields/scaffold.slim +1 -1
  35. data/views/fields/typeahead_picker.slim +8 -5
  36. data/views/infra/inspect.slim +19 -16
  37. data/views/scaffold/fields.slim +3 -1
  38. data/views/scaffold/search.slim +2 -2
  39. data/views/scaffold/search_collapse.slim +2 -2
  40. data/views/scaffold/search_tabs.slim +2 -2
  41. data/views/search_fields/date.slim +20 -0
  42. data/views/search_fields/list_mbuttons.slim +12 -0
  43. data/views/search_fields/typeahead_picker.slim +4 -3
  44. metadata +11 -5
@@ -26,6 +26,8 @@ module Engine2
26
26
  @before_destroy_processors = nil
27
27
  @after_destroy_processors = nil
28
28
  @type_info_synchronized = nil
29
+ @model_icon = :"list"
30
+ @model_route = cls.name.to_sym
29
31
  end
30
32
  cls.setup_schema
31
33
  end
@@ -54,12 +56,12 @@ module Engine2
54
56
  when :integer
55
57
  integer_field name
56
58
  when :string
57
- if db_info[:db_type] == 'text'
58
- string_field name, 10
59
+ string_field name, case db_info[:db_type]
60
+ when 'text', 'character varying'
61
+ 100
59
62
  else
60
- string_field name, Integer(db_info[:column_size] || db_info[:db_type][/\((\d+)\)/, 1])
63
+ Integer(db_info[:column_size] || db_info[:db_type][/\((\d+)\)/, 1])
61
64
  end
62
-
63
65
  when :time
64
66
  time_field name, LOCS[:default_time_format], LOCS[:default_time_model_format]
65
67
  when :date
@@ -176,10 +178,17 @@ module Engine2
176
178
 
177
179
  def scheme s_name = :default, opts = nil, &blk
178
180
  @scheme_name = s_name
179
- @scheme_args = [name.to_sym, self, opts]
180
- SCHEMES::define_scheme name.to_sym, &blk
181
+ @scheme_args = [model_route, self, opts]
182
+ SCHEMES::define_scheme model_route, &blk
183
+ end
184
+
185
+ def model_icon icn = nil
186
+ icn ? @model_icon = icn : @model_icon
181
187
  end
182
188
 
189
+ def model_route rt = nil
190
+ rt ? @model_route = rt : @model_route
191
+ end
183
192
  end
184
193
 
185
194
  # def define_dummy_model
@@ -374,7 +383,7 @@ module Engine2
374
383
  value.join(info[:separator])
375
384
  when :integer
376
385
  value.reduce(0, :|)
377
- end if value && info[:multiselect]
386
+ end if value && info[:multiselect] && !info[:dummy]
378
387
  }
379
388
  )
380
389
 
@@ -382,7 +391,7 @@ module Engine2
382
391
  file_store: lambda{|m, v, info|
383
392
  value = m.values[v]
384
393
  files = E2Files.db[:files]
385
- owner = m.primary_key_values.join('|')
394
+ owner = Sequel::join_keys(m.primary_key_values)
386
395
  upload = info[:store][:upload]
387
396
  files_dir = info[:store][:files]
388
397
  value.each do |entry|
@@ -426,7 +435,7 @@ module Engine2
426
435
  file_store: lambda{|m, v, info|
427
436
  files = E2Files.db[:files]
428
437
  files_dir = info[:store][:files]
429
- owner = m.primary_key_values.join('|')
438
+ owner = Sequel::join_keys(m.primary_key_values)
430
439
  files.select(:id, :name).where(owner: owner, model: m.model.name, field: v.to_s).all.each do |entry|
431
440
  File.delete("#{files_dir}/#{entry[:name]}_#{entry[:id]}")
432
441
  end
@@ -14,6 +14,8 @@ module Engine2
14
14
 
15
15
  class E2Files < Sequel::Model(E2DB[:files])
16
16
  extend Engine2::Model
17
+ model_icon :'file'
18
+ model_route :E2Files
17
19
 
18
20
  type_info do
19
21
  # list_select :model, Hash[@model.db.models.keys.map{|m| [m, m]}]
@@ -133,7 +133,7 @@ module Engine2
133
133
  end
134
134
 
135
135
  define_scheme :star_to_many do |assoc_name, assoc, model|
136
- action.menu(:item_menu).option assoc_name, icon: "list" # , click: "action.show_assoc($index, \"#{assoc_name}!\")"
136
+ action.menu(:item_menu).option assoc_name, icon: Kernel::const_get(assoc[:class_name]).model_icon # , click: "action.show_assoc($index, \"#{assoc_name}!\")"
137
137
  options = assoc[:options] || Schemes::LINK
138
138
  define_node assoc_name, StarToManyListAction, model: model, assoc: assoc do
139
139
  options.each{|k, v| run_scheme(k) if v}
@@ -236,10 +236,5 @@ module Engine2
236
236
  end
237
237
  end
238
238
  end
239
-
240
- define_scheme :array do |name, model|
241
- define_node name, ArrayListAction, model: model do
242
- end
243
- end
244
239
  end
245
240
  end
@@ -63,8 +63,13 @@ module Engine2
63
63
  end
64
64
 
65
65
  def list_select length, options = {}
66
+ template = if options[:multiple]
67
+ "fields/list_mselect"
68
+ else
69
+ options[:optional] ? "fields/list_select_opt" : "fields/list_select"
70
+ end
66
71
  options.merge({
67
- template: options[:optional] ? "fields/list_select_opt" : "fields/list_select",
72
+ template: template,
68
73
  length: length
69
74
  })
70
75
  end
@@ -91,6 +96,12 @@ module Engine2
91
96
  })
92
97
  end
93
98
 
99
+ def list_mbuttons options = {}
100
+ options.merge({
101
+ template: options[:optional] ? "fields/list_mbuttons_opt" : "fields/list_mbuttons"
102
+ })
103
+ end
104
+
94
105
  def select_picker options = {}
95
106
  options.merge({
96
107
  template: options[:optional] ? "fields/select_picker_opt" : "fields/select_picker"
@@ -117,8 +128,8 @@ module Engine2
117
128
  })
118
129
  end
119
130
 
120
- def typeahead_picker
121
- {template: "fields/typeahead_picker", animation: BS_ANIMATION}
131
+ def typeahead_picker options = {}
132
+ {template: "fields/typeahead_picker", length: 20, limit: 10, min_length: 0, animation: BS_ANIMATION}.merge(options)
122
133
  end
123
134
 
124
135
  def email length
@@ -147,6 +158,10 @@ module Engine2
147
158
  })
148
159
  end
149
160
 
161
+ def checkbox_button options = {}
162
+ {template: "fields/checkbox_button"}.merge(options)
163
+ end
164
+
150
165
  def radio_checkbox
151
166
  {template: "fields/radio_checkbox"}
152
167
  end
@@ -171,6 +186,13 @@ module Engine2
171
186
  })
172
187
  end
173
188
 
189
+ def date_picker options = {}
190
+ options.merge({
191
+ template: "search_fields/date",
192
+ animation: BS_ANIMATION
193
+ })
194
+ end
195
+
174
196
  def integer_range
175
197
  {template: "search_fields/integer_range"}
176
198
  end
@@ -199,8 +221,8 @@ module Engine2
199
221
  })
200
222
  end
201
223
 
202
- def typeahead_picker
203
- {template: "search_fields/typeahead_picker", animation: BS_ANIMATION}
224
+ def typeahead_picker options = {}
225
+ {template: "search_fields/typeahead_picker", limit: 10, min_length: 0, animation: BS_ANIMATION}.merge(options)
204
226
  end
205
227
 
206
228
  # def checkbox_search true_v = "1", false_v = "0"
@@ -230,9 +252,13 @@ module Engine2
230
252
  {template: "search_fields/list_buttons"}
231
253
  end
232
254
 
255
+ def list_mbuttons
256
+ {template: "search_fields/list_mbuttons"}
257
+ end
258
+
233
259
  def decimal_date
234
260
  {template: "search_fields/decimal_date_range", animation: BS_ANIMATION}
235
261
  end
236
262
  end
237
263
  end
238
- end
264
+ end
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Engine2
5
-
6
5
  class TypeInfo
7
6
  def initialize model
8
7
  @model = model
@@ -74,7 +73,7 @@ module Engine2
74
73
  end
75
74
  end
76
75
 
77
- def boolean field, true_value = 1, false_value = 0
76
+ def boolean field, true_value = true, false_value = false
78
77
  modify_field field do |info|
79
78
  info[:type] = :boolean
80
79
  info[:true_value] = true_value
@@ -244,6 +243,12 @@ module Engine2
244
243
  end
245
244
  end
246
245
 
246
+ def length field, len
247
+ modify_field field do |info|
248
+ info[:length] = len
249
+ end
250
+ end
251
+
247
252
  def date_range from, to
248
253
  depends_on(from, to)
249
254
  modify_field from do |info|
@@ -338,7 +343,7 @@ module Engine2
338
343
 
339
344
  raise E2Error.new("type '#{values.class}' not supported for list_select modifier for field #{name}") unless values.is_a?(Hash)
340
345
  info[:values] = values.to_a
341
- info[:validations][:list_select] = true
346
+ info[:validations][:list_select] = true unless values.empty?
342
347
  end
343
348
  end
344
349
 
@@ -364,6 +369,5 @@ module Engine2
364
369
  info[:sequence] = seq_name
365
370
  end
366
371
  end
367
-
368
372
  end
369
- end
373
+ end
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Engine2
5
- MAJOR, MINOR, TINY = [1, 0, 8]
5
+ MAJOR, MINOR, TINY = [1, 0, 9]
6
6
  VERSION = [MAJOR, MINOR, TINY].join('.').freeze
7
7
  def self.version
8
8
  VERSION
@@ -9,23 +9,24 @@
9
9
  "build": "brunch build --production"
10
10
  },
11
11
  "dependencies": {
12
- "@uirouter/angularjs": "^1.0.22",
13
- "@uirouter/core": "^5.0.23",
14
- "angular": "^1.7.8",
15
- "angular-animate": "^1.7.8",
16
- "angular-cookies": "^1.7.8",
12
+ "@uirouter/angularjs": "1.0.23",
13
+ "@uirouter/core": "6.0.1",
14
+ "angular": "^1.8.0",
15
+ "angular-animate": "^1.8.0",
16
+ "angular-cookies": "^1.8.0",
17
17
  "angular-drag-and-drop-lists": "^2.1.0",
18
18
  "angular-load": "^0.5.0",
19
19
  "angular-local-storage": "^0.7.0",
20
20
  "angular-motion": "^0.4.0",
21
- "angular-sanitize": "^1.7.8",
21
+ "angular-sanitize": "^1.8.0",
22
22
  "angular-ui-tree": "^2.22.0",
23
23
  "bootstrap": "^3.4.1",
24
24
  "bootstrap-additions": "0.3.1",
25
25
  "fork-awesome": "^1.1.7",
26
- "lodash": "^4.17.11",
26
+ "json-to-pretty-yaml": "1.2.2",
27
+ "lodash": "^4.17.15",
27
28
  "ng-file-upload": "^12.2.0",
28
- "push.js": "^1.0.9"
29
+ "push.js": "^1.0.12"
29
30
  },
30
31
  "devDependencies": {
31
32
  "auto-reload-brunch": "^2.7.1",
@@ -0,0 +1,6 @@
1
+ .col-lg-12
2
+ .div ng-init="(action.record[f] === null || action.record[f] === undefined) && (action.record[f] = field.render.false_value)" ng-model="action.record[f]" e2-field=""
3
+ a.btn.btn-default.btn-sm ng-show="action.record[f] === field.render.true_value" ng-class="action.record[f] === field.render.true_value && 'active'" ng-click="action.record[f] = field.render.false_value" ng-disabled="field.disabled"
4
+ span.glyphicon.glyphicon-check
5
+ a.btn.btn-default.btn-sm ng-show="action.record[f] === field.render.false_value" ng-click="action.record[f] = field.render.true_value" ng-disabled="field.disabled"
6
+ span.glyphicon.glyphicon-unchecked
@@ -1,5 +1,5 @@
1
1
  .col-lg-12
2
- .btn-group.btn-group-sm ng-init="(action.record[f] === undefined) && (action.record[f] = field.render.false_value)" ng-model="action.record[f]" e2-field=""
2
+ .btn-group.btn-group-sm ng-init="(action.record[f] === null || action.record[f] === undefined) && (action.record[f] = field.render.false_value)" ng-model="action.record[f]" e2-field=""
3
3
  a.btn.btn-default ng-class="action.record[f] === field.render.true_value && 'active'" ng-click="action.record[f] = field.render.true_value" ng-disabled="field.disabled"
4
4
  span.glyphicon.glyphicon-check
5
5
  a.btn.btn-default ng-class="action.record[f] === field.render.false_value && 'active'" ng-click="action.record[f] = field.render.false_value" ng-disabled="field.disabled"
@@ -0,0 +1,9 @@
1
+ .col-lg-12
2
+ .btn-group.btn-group-sm ng-model="action.record[f]" e2-field=""
3
+ a.btn.btn-default[
4
+ ng-class="action.record[f].includes(v[0]) && 'active'"
5
+ ng-click="action.record[f].includes(v[0]) ? action._().pull(action.record[f], v[0]) : action.record[f].push(v[0])"
6
+ ng-repeat="v in field.render.values"
7
+ ng-bind-html="v[1]"
8
+ ng-disabled="field.disabled"
9
+ ]
@@ -0,0 +1,11 @@
1
+ .col-lg-12
2
+ .btn-group.btn-group-sm ng-model="action.record[f]" e2-field=""
3
+ a.btn.btn-default[
4
+ ng-class="action.record[f].includes(v[0]) && 'active'"
5
+ ng-click="action.record[f].includes(v[0]) ? action._().pull(action.record[f], v[0]) : action.record[f].push(v[0])"
6
+ ng-repeat="v in field.render.values"
7
+ ng-bind-html="v[1]"
8
+ ng-disabled="field.disabled"
9
+ ]
10
+ a.btn.btn-default ng-disabled="action.record[f].length == 0 || field.disabled" ng-click="action.record[f] = []"
11
+ span.glyphicon.glyphicon-remove
@@ -0,0 +1,12 @@
1
+ /.col-lg-7.col-md-8.col-sm-12
2
+ div e2-input-cols="{{::field.render.length}}"
3
+ select.btn.btn-default.btn-sm [
4
+ multiple="true"
5
+ size="{{field.render.size}}"
6
+ ng-model="action.record[f]"
7
+ ng-options="v[0] as v[1] for v in field.render.values"
8
+ ng-disabled="field.disabled"
9
+ ng-class="field.render.class"
10
+ class="form-control input-sm"
11
+ e2-field=""
12
+ ]
@@ -1,2 +1,2 @@
1
- .col-lg-10.col-md-10.col-sm-10
1
+ .col-lg-12.col-md-12.col-sm-10
2
2
  e2-action action="field.assoc"
@@ -1,11 +1,12 @@
1
1
  e2-action action="[field.assoc, 'typeahead']"
2
- .col-lg-7.col-md-8.col-sm-12
2
+ /.col-lg-7.col-md-8.col-sm-12
3
+ div e2-input-cols="{{::field.render.length}}"
3
4
  .input-group.input-group-sm
4
5
  input.btn.btn-default [
6
+ style="text-align:left"
5
7
  type="text"
6
8
  ng-model="action.decode"
7
- bs-options="v.value for v in action.load($viewValue)"
8
- ng-keypress="action.key_pressed = true"
9
+ bs-options="v.value for v in action.load(action.decode)"
9
10
 
10
11
  ng-disabled="field.disabled"
11
12
  ng-class="field.render.class"
@@ -14,11 +15,13 @@ e2-action action="[field.assoc, 'typeahead']"
14
15
  data-animation="{{::field.render.animation}}"
15
16
  placeholder="..."
16
17
  data-html="true"
17
- data-limit="10"
18
+ data-limit="{{::field.render.limit}}"
19
+ data-min-length="{{::field.render.min_length}}"
20
+ data-trigger="focus"
18
21
  bs-typeahead=""
19
22
  e2-field=""
20
23
  ]
21
24
  span.input-group-addon: span.glyphicon.glyphicon-font
22
25
  .input-group-btn
23
- a.btn.btn-default ng-disabled="field.disabled || action.decode == null" ng-click="!field.disabled && action.reset()"
26
+ a.btn.btn-default ng-disabled="field.disabled || !action.decode" ng-click="!field.disabled && action.reset()"
24
27
  span.glyphicon.glyphicon-off
@@ -1,4 +1,4 @@
1
- e2-action action="'inspect'"
1
+ e2-action action="'inspect_' + action.globals().application"
2
2
  div bs-tabs=""
3
3
  br
4
4
  div title="API" bs-pane=""
@@ -8,7 +8,7 @@ e2-action action="'inspect'"
8
8
  .panel-body
9
9
  script type="text/ng-template" id="nodes_renderer.html"
10
10
  div data-nodrag="" ng-init="stack = stack.concat([$index])"
11
- span.glyphicon.glyphicon-cog ng-if="node.terminal"
11
+ span.glyphicon ng-class="{'glyphicon-cog': node.access, 'glyphicon-exclamation-sign': !node.access}" ng-if="node.terminal"
12
12
  a ng-click="action.open(stack, node, collapsed, true); toggle(this)" ng-if="!node.terminal"
13
13
  span.glyphicon ng-class="{'glyphicon-play-circle': collapsed && node.access, 'glyphicon-download': !collapsed, 'glyphicon-exclamation-sign': collapsed && !node.access}"
14
14
  span< ng-bind="node.name + ' ' + node.number" ng-click="action.open(stack, node, collapsed, false)" style="cursor: pointer; {{action.number == node.number && 'font-weight: bold;'}}"
@@ -24,13 +24,13 @@ e2-action action="'inspect'"
24
24
  div bs-tabs="" bs-active-pane="actionTab" ng-init="actionTab = 0"
25
25
  div title="Action" bs-pane=""
26
26
  br
27
- pre ng-bind="action.action_json | json"
27
+ pre ng-bind="action.action_json | yaml"
28
28
  div title="Meta" bs-pane=""
29
29
  br
30
- pre ng-bind="action.meta_json | json"
30
+ pre ng-bind="action.meta_json | yaml"
31
31
  div title="State" bs-pane=""
32
32
  br
33
- pre ng-bind="action.action_state | json"
33
+ pre ng-bind="action.action_state | yaml"
34
34
 
35
35
  div title="Model" bs-pane=""
36
36
  .row
@@ -58,13 +58,13 @@ e2-action action="'inspect'"
58
58
  div bs-tabs="" bs-active-pane="modelTab" ng-init="modelTab = 0"
59
59
  div title="Model" bs-pane=""
60
60
  br
61
- pre ng-bind="action.model.info | json"
61
+ pre ng-bind="action.model.info | yaml"
62
62
  div title="Schema" bs-pane=""
63
63
  br
64
- pre ng-bind="action.model.schema | json"
64
+ pre ng-bind="action.model.schema | yaml"
65
65
  div title="Type info" bs-pane=""
66
66
  br
67
- pre ng-bind="action.model.type_info | json"
67
+ pre ng-bind="action.model.type_info | yaml"
68
68
  div title="Associations" bs-pane="" ng-if="action.model && action.has_assoc(action.model)"
69
69
  br
70
70
  .row
@@ -78,36 +78,39 @@ e2-action action="'inspect'"
78
78
  br
79
79
  .row
80
80
  .col-lg-12
81
- pre ng-bind="action.activeAssoc | json"
81
+ pre ng-bind="action.activeAssoc | yaml"
82
82
  div title="Globals" bs-pane=""
83
83
  .panel.panel-default
84
84
  .panel-body
85
- pre ng-bind="action.globals() | json"
85
+ pre ng-bind="action.globals() | yaml"
86
86
 
87
87
  div title="Environment" bs-pane=""
88
88
  .panel.panel-default
89
89
  .panel-body
90
- pre ng-bind="action.environment | json"
90
+ pre ng-bind="action.environment | yaml"
91
91
 
92
92
  div title="Local storage" bs-pane=""
93
93
  .panel.panel-default
94
94
  .panel-body
95
- pre ng-bind="action.local_storage | json"
96
- pre ng-bind="action.local_storage.keys() | json"
95
+ pre ng-bind="action.local_storage | yaml"
96
+ pre ng-bind="action.local_storage.keys() | yaml"
97
97
  a.btn.btn-default ng-click="action.local_storage.clearAll()"
98
98
  | Clear
99
99
 
100
100
  div title="Diagnostics" bs-pane=""
101
101
  .panel.panel-default
102
102
  .panel-body
103
- a.btn.btn-default ng-click="action.globals().notification('Name', 'Body', undefined, 2000)"
104
- | Push
103
+ .btn-group
104
+ a.btn.btn-default ng-click="action.globals().notification('Name', 'Body', undefined, 2000)"
105
+ | Push
106
+ a.btn.btn-default ng-click="action.globals().toastr().success('Name', 'Body')"
107
+ | Toastr
105
108
  hr
106
109
  a.btn.btn-default> ng-click="action.web_socket().send({number: action.number})"
107
110
  | Websocket
108
111
  | {{action.number}}
109
112
  br
110
113
  | Event
111
- pre ng-bind="action.event | json"
114
+ pre ng-bind="action.event | yaml"
112
115
  | Socket
113
116
  pre ng-bind="action.web_socket() | json"