engine2 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +0 -0
  3. data/Rakefile +4 -4
  4. data/app/{engine2actions.coffee → actions.coffee} +341 -215
  5. data/app/app.coffee +0 -0
  6. data/app/app.css +17 -0
  7. data/app/engine2.coffee +158 -208
  8. data/app/modal.coffee +138 -0
  9. data/bower.json +4 -2
  10. data/conf/message.yaml +5 -0
  11. data/conf/message_pl.yaml +7 -2
  12. data/config.coffee +24 -12
  13. data/engine2.gemspec +8 -8
  14. data/lib/engine2.rb +12 -10
  15. data/lib/engine2/action.rb +1338 -133
  16. data/lib/engine2/action/array.rb +189 -0
  17. data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
  18. data/lib/engine2/action/delete.rb +64 -0
  19. data/lib/engine2/action/form.rb +16 -0
  20. data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
  21. data/lib/engine2/action/link.rb +117 -0
  22. data/lib/engine2/action/list.rb +333 -0
  23. data/lib/engine2/action/save.rb +28 -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 +175 -87
  27. data/lib/engine2/handler.rb +14 -13
  28. data/lib/engine2/model.rb +85 -43
  29. data/lib/engine2/models/Files.rb +4 -1
  30. data/lib/engine2/models/UserInfo.rb +6 -3
  31. data/lib/engine2/post_bootstrap.rb +4 -3
  32. data/lib/engine2/pre_bootstrap.rb +10 -6
  33. data/lib/engine2/scheme.rb +107 -65
  34. data/lib/engine2/templates.rb +41 -6
  35. data/lib/engine2/type_info.rb +51 -23
  36. data/lib/engine2/version.rb +2 -1
  37. data/package.json +22 -16
  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 +1 -1
  42. data/views/fields/bs_select.slim +2 -2
  43. data/views/fields/bsselect_picker.slim +4 -4
  44. data/views/fields/bsselect_picker_opt.slim +5 -5
  45. data/views/fields/checkbox.slim +4 -4
  46. data/views/fields/checkbox_button.slim +6 -0
  47. data/views/fields/checkbox_buttons.slim +3 -3
  48. data/views/fields/checkbox_buttons_opt.slim +3 -3
  49. data/views/fields/currency.slim +2 -2
  50. data/views/fields/date.slim +4 -4
  51. data/views/fields/date_range.slim +9 -9
  52. data/views/fields/date_time.slim +9 -9
  53. data/views/fields/datetime.slim +8 -8
  54. data/views/fields/decimal.slim +1 -1
  55. data/views/fields/decimal_date.slim +3 -3
  56. data/views/fields/decimal_time.slim +3 -3
  57. data/views/fields/email.slim +3 -3
  58. data/views/fields/file_store.slim +11 -11
  59. data/views/fields/input_text.slim +4 -4
  60. data/views/fields/integer.slim +1 -1
  61. data/views/fields/list_bsmselect.slim +20 -0
  62. data/views/fields/list_bsselect.slim +5 -5
  63. data/views/fields/list_bsselect_opt.slim +6 -6
  64. data/views/fields/list_buttons.slim +1 -1
  65. data/views/fields/list_buttons_opt.slim +2 -2
  66. data/views/fields/list_mbuttons.slim +9 -0
  67. data/views/fields/list_mbuttons_opt.slim +11 -0
  68. data/views/fields/list_mselect.slim +12 -0
  69. data/views/fields/list_select.slim +4 -4
  70. data/views/fields/list_select_opt.slim +5 -5
  71. data/views/fields/password.slim +4 -4
  72. data/views/fields/radio_checkbox.slim +3 -3
  73. data/views/fields/scaffold.slim +2 -2
  74. data/views/fields/scaffold_picker.slim +5 -5
  75. data/views/fields/select_picker.slim +3 -3
  76. data/views/fields/select_picker_opt.slim +4 -4
  77. data/views/fields/text_area.slim +4 -3
  78. data/views/fields/time.slim +5 -4
  79. data/views/fields/typeahead_picker.slim +12 -9
  80. data/views/index.slim +3 -3
  81. data/views/infra/index.slim +0 -0
  82. data/views/infra/inspect.slim +41 -10
  83. data/views/modals/close_m.slim +0 -0
  84. data/views/modals/confirm_m.slim +0 -0
  85. data/views/modals/empty_m.slim +0 -0
  86. data/views/modals/menu_m.slim +1 -1
  87. data/views/modals/yes_no_m.slim +0 -0
  88. data/views/panels/menu_m.slim +1 -1
  89. data/views/scaffold/confirm.slim +0 -0
  90. data/views/scaffold/fields.slim +6 -4
  91. data/views/scaffold/form.slim +1 -1
  92. data/views/scaffold/form_collapse.slim +4 -3
  93. data/views/scaffold/form_tabs.slim +3 -2
  94. data/views/scaffold/list.slim +0 -0
  95. data/views/scaffold/message.slim +0 -0
  96. data/views/scaffold/search.slim +4 -4
  97. data/views/scaffold/search_collapse.slim +8 -7
  98. data/views/scaffold/search_tabs.slim +6 -5
  99. data/views/scaffold/view.slim +2 -2
  100. data/views/scaffold/view_collapse.slim +5 -4
  101. data/views/scaffold/view_tabs.slim +4 -3
  102. data/views/search_fields/bsmselect_picker.slim +4 -4
  103. data/views/search_fields/bsselect_picker.slim +4 -4
  104. data/views/search_fields/checkbox.slim +3 -3
  105. data/views/search_fields/checkbox2.slim +5 -5
  106. data/views/search_fields/checkbox_buttons.slim +3 -3
  107. data/views/search_fields/date.slim +20 -0
  108. data/views/search_fields/date_range.slim +8 -8
  109. data/views/search_fields/decimal_date_range.slim +5 -5
  110. data/views/search_fields/input_text.slim +2 -2
  111. data/views/search_fields/integer.slim +1 -1
  112. data/views/search_fields/integer_range.slim +2 -2
  113. data/views/search_fields/list_bsmselect.slim +4 -4
  114. data/views/search_fields/list_bsselect.slim +4 -4
  115. data/views/search_fields/list_buttons.slim +2 -2
  116. data/views/search_fields/list_mbuttons.slim +12 -0
  117. data/views/search_fields/list_select.slim +3 -3
  118. data/views/search_fields/scaffold_picker.slim +2 -2
  119. data/views/search_fields/select_picker.slim +3 -3
  120. data/views/search_fields/typeahead_picker.slim +8 -7
  121. metadata +53 -48
  122. data/lib/engine2/meta.rb +0 -1216
  123. data/lib/engine2/meta/array_meta.rb +0 -82
  124. data/lib/engine2/meta/delete_meta.rb +0 -60
  125. data/lib/engine2/meta/form_meta.rb +0 -15
  126. data/lib/engine2/meta/link_meta.rb +0 -134
  127. data/lib/engine2/meta/list_meta.rb +0 -281
  128. data/lib/engine2/meta/save_meta.rb +0 -50
  129. data/lib/engine2/meta/view_meta.rb +0 -7
  130. data/public/__sinatra__/404.png +0 -0
  131. data/public/__sinatra__/500.png +0 -0
@@ -0,0 +1,138 @@
1
+ 'use strict'
2
+ angular.module('Engine2')
3
+ .provider '$e2Modal', ->
4
+ $get: ($rootScope, $modal, $timeout, $window, $injector) ->
5
+ class MManager
6
+ @Z_INDEX: 1050
7
+ @index: 0
8
+ constructor: () ->
9
+ backdrop_z_index: (num, index) -> angular.element(document.querySelectorAll('.modal-backdrop')).eq(num).css('z-index', index)
10
+ modal_num: (num) -> angular.element(document.querySelectorAll('.modal')).eq(num)
11
+ backdrop: (bdr) -> bdr ? 'static'
12
+ show_before: ->
13
+ @z_index = MManager.Z_INDEX + MManager.index * 2
14
+ @modal.css('z-index', @z_index + 1)
15
+ hide_before: ->
16
+ @z_index = MManager.Z_INDEX + ((MManager.index - 1) * 2)
17
+ show: ->
18
+ hide: -> angular.element($window.document.body).addClass('modal-open modal-with-am-fade') if MManager.index > 0
19
+
20
+ class DefaultMManager extends MManager
21
+ show_before: ->
22
+ super()
23
+ @backdrop.css('z-index', @z_index) # @backdrop_z_index(-MManager.index - 1, z_index)
24
+
25
+ class FirstMManager extends MManager
26
+ constructor: () ->
27
+ super()
28
+ @threshold = 2
29
+ backdrop: (bdr) -> if MManager.index > @threshold then false else super(bdr)
30
+ show_before: ->
31
+ super()
32
+ if MManager.index > @threshold
33
+ @modal_num((MManager.index - 1) - @threshold).css('display', 'none')
34
+ @backdrop_z_index(0, @z_index)
35
+ else
36
+ @backdrop.css('z-index', @z_index) # @backdrop_z_index(-MManager.index - 1, z_index)
37
+
38
+ hide_before: ->
39
+ super()
40
+ if MManager.index > @threshold
41
+ @backdrop_z_index(0, @z_index)
42
+ @modal_num((MManager.index - 1) - @threshold).css('display', 'block')
43
+
44
+ class SingleBackdropMManager extends MManager
45
+ backdrop: (bdr) -> if MManager.index > 0 then false else super(bdr)
46
+ show_before: ->
47
+ super()
48
+ @backdrop_z_index(0, @z_index)
49
+ hide_before: ->
50
+ super()
51
+ @backdrop_z_index(0, @z_index)
52
+
53
+ is_modal: -> MManager.index > 0
54
+
55
+ show: (action) ->
56
+ scope = if action.scope then action.scope().$new(true) else $rootScope.$new()
57
+ scope.action = action
58
+ manager = new SingleBackdropMManager()
59
+
60
+ scope.$on 'modal.show.before', (e, m) ->
61
+ e.stopPropagation()
62
+ manager.modal = m.$element
63
+ manager.backdrop = m.$backdrop
64
+ throw "Modal has element" if action.element?()
65
+ action.element = -> m.$element
66
+ action.panel_show?()
67
+ manager.show_before()
68
+ MManager.index++
69
+
70
+ scope.$on 'modal.show', (e, m) ->
71
+ e.stopPropagation()
72
+ manager.show()
73
+ action.panel_shown?()
74
+
75
+ scope.$on 'modal.hide.before', (e) ->
76
+ e.stopPropagation()
77
+ MManager.index--
78
+ manager.hide_before()
79
+ action.panel_hide?()
80
+
81
+ scope.$on 'modal.hide', (e) ->
82
+ e.stopPropagation()
83
+ manager.hide()
84
+ action.panel_hidden?()
85
+ scope.$destroy()
86
+
87
+ $injector.get('E2').fetch_panel(action.meta.panel, true).then (template) ->
88
+ modal = $modal
89
+ scope: scope
90
+ show: false
91
+ template: template
92
+ backdrop: manager.backdrop(action.meta.panel.backdrop)
93
+ animation: action.meta.panel.animation ? 'am-fade'
94
+
95
+ action.modal_hide = -> modal.$scope.$hide()
96
+ modal.$promise.then ->
97
+ modal.show()
98
+ modal
99
+
100
+ show_modal: (title, msg, options = {html: false, alert_class: 'alert-danger', modal_class: 'modal-large'}) ->
101
+ body = if options.html then msg else "<div class='alert alert-#{options.alert_class}'>#{msg}</div>"
102
+ clazz = if options.html then "modal-huge" else options.modal_class
103
+ @show meta: panel: (panel_template: "close_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
104
+
105
+
106
+ info: (title, msg, options = {alert_class: 'info', modal_class: 'modal-large'}) -> @show_modal(title, msg, options)
107
+ warning: (title, msg, options = {alert_class: 'warning', modal_class: 'modal-large'}) -> @show_modal(title, msg, options)
108
+ error: (title, msg, options = {alert_class: 'danger', modal_class: 'modal-huge'}) -> @show_modal(title, msg, options)
109
+
110
+ confirm: (title, msg, action) ->
111
+ body = "<div class='alert alert-warning'>#{msg}</div>"
112
+ clazz = "modal-large"
113
+ @show
114
+ confirm: action,
115
+ meta: panel: (panel_template: "confirm_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
116
+
117
+ .directive 'e2Modal', ($e2Modal) ->
118
+ restrict: 'E'
119
+ # replace: true
120
+ # transclude: true
121
+ scope: true
122
+ compile: (celem, cattr) ->
123
+ obody = celem[0].children[0]
124
+ celem.empty() if obody
125
+ (scope, elem, attrs) ->
126
+ scope.$on attrs.name, (ev, args) ->
127
+ return if ev.defaultPrevented
128
+ ev.preventDefault()
129
+
130
+ panel = panel_template: attrs.panelTemplate, title: attrs.title, class: attrs.clazz, footer: true
131
+ if obody then panel.template_string = obody.outerHTML else panel.template = attrs.template
132
+ action = meta: (panel: panel), scope: -> scope
133
+ _.assign(action, args)
134
+
135
+ modal = $e2Modal.show(action)
136
+ hide_off = scope.$on "#{attrs.name}_close", ->
137
+ hide_off()
138
+ modal.then (m) -> m.$scope.$hide()
data/bower.json CHANGED
@@ -2,8 +2,10 @@
2
2
  "name": "engine2",
3
3
  "version": "1.0.0",
4
4
  "dependencies": {
5
- "angular-strap": "^2.3.10",
6
- "angular": "^1.5.8"
5
+ "angular-strap": "^2.3.12",
6
+ "angular": "^1.5.8",
7
+ "angular-websocket": "^2.0.0",
8
+ "angular-toastr": "^2.1.1"
7
9
  },
8
10
  "overrides": {
9
11
  "angular": {
@@ -8,12 +8,16 @@
8
8
  :error: Error
9
9
  :no_entry: Entry doesnt exist
10
10
  :decode_selected: selected
11
+ :list_select_selected: Selected
11
12
 
13
+ :E2Files: Files
14
+ :files: File
12
15
  :name: Name
13
16
  :mime: Mime
14
17
  :owner: Owner
15
18
  :model: Model
16
19
  :field: Field
20
+ :uploaded: Uploaded
17
21
  :updated: Updated
18
22
 
19
23
  :ok: Ok
@@ -51,6 +55,7 @@
51
55
  :save: Save
52
56
  :approve: Approve
53
57
  :cancel: Cancel
58
+ :close: Close
54
59
 
55
60
  :inspect_modal: Inspect
56
61
 
@@ -8,12 +8,16 @@
8
8
  :error: Błąd
9
9
  :no_entry: Wpis nie istnieje
10
10
  :decode_selected: wybranych
11
+ :list_select_selected: Wybranych
11
12
 
13
+ :E2Files: Pliki
14
+ :files: Plik
12
15
  :name: Nazwa
13
16
  :mime: Mime
14
17
  :owner: Właściciel
15
18
  :model: Model
16
19
  :field: Pole
20
+ :uploaded: Wysłany
17
21
  :updated: Aktualizowany
18
22
 
19
23
  :ok: Ok
@@ -30,12 +34,12 @@
30
34
  :select_toggle: Zaznacz
31
35
  :confirm_delete: Usuń
32
36
  :confirm_delete_title: <span class='glyphicon glyphicon-trash'></span> Potwierdzenie
33
- :confirm_bulk_delete: Usuń zaznacznone
37
+ :confirm_bulk_delete: Usuń zaznaczone
34
38
  :delete_restricted: Blokujące relacje
35
39
  :confirm_bulk_delete_title: <span class='glyphicon glyphicon-trash'></span> Potwierdzenie
36
40
  :confirm_unlink: Odłącz
37
41
  :confirm_unlink_title: <span class='glyphicon glyphicon-minus'></span> Potwierdzenie
38
- :confirm_bulk_unlink: Odłącz zaznacznone
42
+ :confirm_bulk_unlink: Odłącz zaznaczone
39
43
  :confirm_bulk_unlink_title: <span class='glyphicon glyphicon-minus'></span> Potwierdzenie
40
44
  :debug_info: Debug info
41
45
  :show_meta: Metainfo
@@ -51,6 +55,7 @@
51
55
  :save: Zapisz
52
56
  :approve: Zatwierdź
53
57
  :cancel: Anuluj
58
+ :close: Zamknij
54
59
 
55
60
  :inspect_modal: Inspekcja
56
61
 
@@ -9,11 +9,13 @@ exports.config =
9
9
  "bootstrap-additions": ["dist/bootstrap-additions.css"]
10
10
  "angular-motion": ["dist/angular-motion.css"]
11
11
  "angular-ui-tree": ["dist/angular-ui-tree.css"]
12
- "font-awesome": ["css/font-awesome.css"]
12
+ "fork-awesome": ["css/fork-awesome.css"]
13
+ # "ui-select": ["dist/select.css"]
13
14
 
14
15
  modules:
15
16
  definition: 'commonjs'
16
17
  wrapper: false
18
+ nameCleaner: (path) -> path
17
19
 
18
20
  paths:
19
21
  public: 'public'
@@ -22,18 +24,23 @@ exports.config =
22
24
  files:
23
25
  javascripts:
24
26
  joinTo:
25
- 'engine2vendor.js': /^node_modules|bower_components/
26
- 'engine2.js': /^app/
27
-
28
- stylesheets:
29
- joinTo:
30
- 'engine2vendor.css': /^node_modules/
31
- 'engine2app.css': /^app/
27
+ 'assets/engine2vendor.js': /^node_modules|bower_components/
28
+ 'assets/engine2.js': /^app/
32
29
  order:
33
30
  before: [
34
- /bootstrap.css$/
31
+ "app/engine2.coffee"
35
32
  ]
36
33
 
34
+ stylesheets:
35
+ joinTo:
36
+ 'assets/engine2vendor.css': /^(?:node_modules||bower_components)\/(?!(bootstrap\/))/
37
+ 'assets/bootstrap.css': /^node_modules\/(bootstrap\/)/
38
+ 'assets/engine2.css': /^app/
39
+ # order:
40
+ # before: [
41
+ # /bootstrap\.css$/
42
+ # ]
43
+
37
44
  plugins:
38
45
  on: ["ng-annotate-brunch"]
39
46
 
@@ -45,17 +52,22 @@ exports.config =
45
52
 
46
53
  replacement:
47
54
  replacements: [
48
- files: [/vendor.js$/]
55
+ files: [/vendor\.js$/]
49
56
  match: (
50
57
  fix = "$modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});"
51
58
  find: fix.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1")
52
59
  replace: "#{fix}$modal.$backdrop = backdropElement;"
53
- )
60
+ )
61
+ # files: [/\.css$/]
62
+ # match: (
63
+ # find: "../fonts"
64
+ # replace: "fonts"
65
+ # )
54
66
  ]
55
67
 
56
68
  copycat:
57
69
  fonts: [
58
- "node_modules/font-awesome/fonts"
70
+ "node_modules/fork-awesome/fonts"
59
71
  "node_modules/bootstrap/fonts"
60
72
  ]
61
73
  verbose: true
@@ -13,19 +13,19 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "http://none.for.now"
14
14
  spec.license = 'MIT'
15
15
 
16
- spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) rescue []
17
17
  spec.require_paths = ["lib"]
18
18
 
19
- spec.add_dependency "sequel", '~> 4'
20
- spec.add_dependency "rack-contrib", '~> 1.4'
19
+ spec.add_dependency "sequel", '~> 5'
21
20
  if defined? JRUBY_VERSION
22
- spec.add_dependency 'jdbc-sqlite3', '~> 3.8'
21
+ spec.add_dependency 'jdbc-sqlite3', '~> 3.0'
23
22
  else
24
23
  spec.add_dependency 'sqlite3', '~> 1.3'
25
24
  end
26
- spec.add_dependency "sinatra", '~> 1.4'
27
- spec.add_dependency 'slim', '~> 3.0'
25
+ spec.add_dependency "sinatra", '~> 2.0'
26
+ spec.add_dependency 'slim', '~> 4.0'
27
+ spec.add_dependency 'faye-websocket', '~> 0.10'
28
28
 
29
- spec.add_development_dependency "bundler", "~> 1.11"
30
- spec.add_development_dependency "rake", "~> 11"
29
+ spec.add_development_dependency "bundler", "~> 2.00"
30
+ spec.add_development_dependency "rake", "~> 13"
31
31
  end
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+ # frozen-string-literal: true
2
3
 
3
4
  require 'yaml'
4
5
  require 'logger'
@@ -7,6 +8,7 @@ require 'sinatra'
7
8
  require 'json'
8
9
  require 'slim'
9
10
  require 'engine2/version'
11
+ require 'faye/websocket'
10
12
 
11
13
  %w[
12
14
  core.rb
@@ -14,19 +16,19 @@ require 'engine2/version'
14
16
  type_info.rb
15
17
  model.rb
16
18
  templates.rb
17
- meta.rb
18
19
  action.rb
20
+ action_node.rb
19
21
  scheme.rb
20
22
 
21
- meta/array_meta.rb
22
- meta/list_meta.rb
23
- meta/view_meta.rb
24
- meta/form_meta.rb
25
- meta/save_meta.rb
26
- meta/delete_meta.rb
27
- meta/decode_meta.rb
28
- meta/link_meta.rb
29
- meta/infra_meta.rb
23
+ action/array.rb
24
+ action/list.rb
25
+ action/view.rb
26
+ action/form.rb
27
+ action/save.rb
28
+ action/delete.rb
29
+ action/decode.rb
30
+ action/link.rb
31
+ action/infra.rb
30
32
  ].each do |f|
31
33
  load "engine2/#{f}"
32
34
  end
@@ -1,213 +1,1418 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Engine2
4
-
5
- class Action < BasicObject
6
- ACCESS_FORBIDDEN ||= ->h{false}
7
- attr_reader :parent, :name, :number, :actions, :recheck_access
8
- attr_reader :meta_proc
5
+ class Action
6
+ attr_reader :node, :meta, :assets, :static, :invokable
9
7
 
10
8
  class << self
11
- attr_accessor :count
9
+ def action_type at = nil
10
+ at ? @action_type = at : @action_type
11
+ end
12
+
13
+ def http_method hm = nil
14
+ hm ? @http_method = hm : @http_method
15
+ end
12
16
 
13
- def default_meta
14
- Class.new(InlineMeta){meta_type :inline}
17
+ def inherited cls
18
+ cls.http_method http_method
19
+ end
20
+
21
+ def inherit &blk
22
+ cls = Class.new self do
23
+ action_type superclass.action_type
24
+ end
25
+
26
+ cls.instance_eval &blk if block_given?
27
+ cls
15
28
  end
16
29
  end
17
30
 
18
- def initialize parent, name, meta_class, assets
19
- Action.count += 1
20
- @number = Action.count
21
- @parent = parent
22
- @name = name
23
- @meta = meta_class.new(self, assets)
24
- @actions = {}
31
+ http_method :get
32
+
33
+ def initialize node, assets, static = self
34
+ @meta = {}
35
+ @node = node
36
+ @assets = assets
37
+ @static = static
25
38
  end
26
39
 
27
- def * &blk
28
- @meta_proc = @meta_proc ? @meta_proc.chain(&blk) : blk if blk
29
- @meta
40
+ def http_method
41
+ @http_method # || (raise E2Error.new("No http method for action #{self.class}"))
30
42
  end
31
43
 
32
- alias :meta :*
44
+ def action_type
45
+ @action_type || (raise E2Error.new("No action_type for action #{self.class}"))
46
+ end
33
47
 
34
- def access! &blk
35
- ::Kernel.raise E2Error.new("Access for action #{name} already defined") if @access_block
36
- @access_block = blk
48
+ def check_static_action
49
+ raise E2Error.new("Static action required") if dynamic?
37
50
  end
38
51
 
39
- def access_forbidden!
40
- access! &ACCESS_FORBIDDEN
52
+ def check_anonymous_action_class name
53
+ raise E2Error.new("Defining method '#{name}'' for named class '#{self.class}', consider using #inherit") if self.class.name
41
54
  end
42
55
 
43
- def check_access! handler
44
- !@access_block || @access_block.(handler)
56
+ def define_method name, &blk
57
+ check_anonymous_action_class name
58
+ self.class.class_eval{define_method name, &blk}
45
59
  end
46
60
 
47
- def run_scheme name, *args, &blk
48
- result = instance_exec(*args, &SCHEMES[name])
49
- result.instance_eval(&blk) if blk
50
- result
61
+ def define_invoke &blk
62
+ check_static_action
63
+ define_method :invoke, &blk
64
+ # self.class.class_eval{define_method :invoke, &blk}
51
65
  end
52
66
 
53
- def define_action name, meta_class = Action.default_meta, assets = {}, &blk
54
- ::Kernel.raise E2Error.new("Action #{name} already defined") if @actions[name]
55
- action = @actions[name] = Action.new(self, name, meta_class, assets)
56
- action.*.pre_run
57
- define_singleton_method! name do |&ablk| # forbidden list
58
- action.instance_eval(&ablk) if ablk
59
- action
67
+ def invoke! handler
68
+ if rmp = @request_action_proc
69
+ action = self.class.new(node, assets, self)
70
+ result = action.instance_exec(handler, *action.request_action_proc_params(handler), &rmp)
71
+ action.post_process
72
+ response = @requestable ? (result.is_a?(Hash) ? result : {}) : action.invoke(handler)
73
+ response[:meta] = action.meta
74
+ response
75
+ else
76
+ invoke(handler)
60
77
  end
61
- action.instance_eval(&blk) if blk
62
- action.*.action_defined
63
- action
64
78
  end
65
79
 
66
- def define_action_meta name, meta_class = Action.default_meta, assets = {}, &blk
67
- define_action name, meta_class, assets do
68
- self.* &blk
80
+ def repeat time
81
+ @meta[:repeat] = time
82
+ end
83
+
84
+ def arguments args
85
+ (@meta[:arguments] ||= {}).merge! args
86
+ end
87
+
88
+ def execute command
89
+ (@meta[:execute] ||= []) << command
90
+ end
91
+
92
+ def dynamic?
93
+ self != @static
94
+ end
95
+
96
+ # def [] *keys
97
+ # @meta.path(*keys)
98
+ # end
99
+
100
+ # def []= *keys, value
101
+ # @meta.path!(*keys, value)
102
+ # end
103
+
104
+ def lookup *keys
105
+ if dynamic? # we are the request action
106
+ value = @meta.path(*keys)
107
+ value.nil? ? @static.meta.path(*keys) : value
108
+ # value || @static.value.path(keys)
109
+ else
110
+ @meta.path(*keys)
69
111
  end
70
112
  end
71
113
 
72
- def define_action_invoke name, meta_class = Action.default_meta, assets = {}, &blk
73
- define_action name, meta_class, assets do
74
- self.*.define_invoke &blk
114
+ def merge *keys
115
+ if keys.length == 1
116
+ key = keys.first
117
+ dynamic? ? @static.meta[key].merge(@meta[key] || {}) : @meta[key]
118
+ else
119
+ dynamic? ? @static.meta.path(*keys).merge(@meta.path(*keys)) : @meta.path(*keys)
75
120
  end
76
121
  end
77
122
 
78
- def define_action_bundle name, *actions
79
- define_singleton_method!(name) do |&blk|
80
- if blk
81
- actions.each{|a|__send__(a, &blk)} # if @actions[action] ?
123
+ def freeze_action
124
+ hash = @meta
125
+ hash.freeze
126
+ # hash.each_pair{|k, v| freeze(v) if v.is_a? Hash}
127
+ freeze
128
+ end
129
+
130
+ def request_action_proc_params handler
131
+ []
132
+ end
133
+
134
+ def request &blk
135
+ raise E2Error.new("No block given for request action") unless blk
136
+ raise E2Error.new("No request block in request action allowed") if dynamic?
137
+ @request_action_proc = @request_action_proc ? @request_action_proc.chain_args(&blk) : blk
138
+ nil
139
+ end
140
+
141
+ def pre_run
142
+ @action_type = self.class.action_type
143
+ @http_method = self.class.http_method
144
+ end
145
+
146
+ def node_defined
147
+ end
148
+
149
+ def post_run
150
+ if respond_to? :invoke
151
+ @invokable = true
152
+ else
153
+ if @request_action_proc
154
+ @invokable = true
155
+ @requestable = true
82
156
  else
83
- ActionBundle.new(self, actions)
157
+ @meta[:invokable] = false
84
158
  end
85
159
  end
160
+ @meta[:dynamic_meta] = true if @request_action_proc
161
+ post_process
86
162
  end
87
163
 
88
- def define_singleton_method! name, &blk
89
- class << self;self;end.instance_eval do # __realclass__
90
- define_method name, &blk
164
+ def post_process
165
+ end
166
+
167
+ def split_keys id
168
+ Sequel::split_keys(id)
169
+ end
170
+
171
+ def join_keys id
172
+ Sequel::join_keys(id)
173
+ end
174
+ end
175
+
176
+ module ActionWebSocketSupport
177
+ WS_METHODS ||= Faye::WebSocket::API::TYPES.keys.map(&:to_sym)
178
+ WS_METHODS.each do |method|
179
+ define_method :"ws_#{method}" do |&blk|
180
+ @ws_methods[method] = blk
181
+ end
182
+ end
183
+
184
+ def pre_run
185
+ super
186
+ @ws_methods = {}
187
+ @meta[:websocket] = {options: {}}
188
+ end
189
+
190
+ def ws_options opts
191
+ @meta[:websocket][:options].merge! opts
192
+ end
193
+
194
+ def ws_execute execute
195
+ (@meta[:websocket][:execute] ||= {}).merge! execute
196
+ end
197
+
198
+ def post_run
199
+ super
200
+ @invokable = true
201
+ end
202
+
203
+ def invoke! handler
204
+ if Faye::WebSocket.websocket?(handler.env)
205
+ ws = Faye::WebSocket.new(handler.env)
206
+ @ws_methods.each do |method, blk|
207
+ ws.on(method) do |evt|
208
+ begin
209
+ data = method == :message ? JSON.parse(evt.data, symbolize_names: true) : evt
210
+ action = self.class.new(node, assets, self)
211
+ result = action.instance_exec(data, ws, evt, &blk)
212
+ result = {} unless result.is_a?(Hash)
213
+ result[:meta] = action.meta
214
+ ws.send! result unless action.meta.empty?
215
+ rescue Exception => e
216
+ ws.send! error: {exception: e, method: method}
217
+ end
218
+ end
219
+ end
220
+ ws.rack_response
221
+ else
222
+ super
91
223
  end
92
224
  end
225
+ end
226
+
227
+ class WebSocketAction < Action
228
+ include ActionWebSocketSupport
229
+ end
93
230
 
94
- def [] name
95
- @actions[name]
231
+ class InlineAction < Action
232
+ action_type :inline
233
+ end
234
+
235
+ class RootAction < Action
236
+ def initialize *args
237
+ super
238
+ @meta.merge! environment: Handler::environment, application: Engine2::SETTINGS[:name], uuid: SecureRandom.uuid,
239
+ key_separator: Engine2::SETTINGS[:key_separator], ws_methods: ActionWebSocketSupport::WS_METHODS
96
240
  end
241
+ end
97
242
 
98
- def actions_info handler
99
- info = actions.inject({}) do |h, (name, a)|
100
- meta = a.*
101
- h[name] = {
102
- meta_type: meta.meta_type,
103
- method: meta.http_method,
104
- number: a.number,
105
- access: recheck_access ? nil : a.check_access!(handler),
106
- recheck_access: a.recheck_access,
107
- terminal: a.actions.empty?,
108
- meta: !meta.get.empty?
109
- }
110
- h
243
+ module ActionAPISupport
244
+ def fields field
245
+ (@meta[:fields] ||= {})[field.to_sym] ||= {}
246
+ end
247
+
248
+ def config
249
+ @meta[:config] ||= {}
250
+ end
251
+
252
+ def fields! *fields, options
253
+ raise E2Error.new("No fields given to info") if fields.empty?
254
+ fields.each do |field|
255
+ fields(field).merge! options # rmerge ?
111
256
  end
257
+ end
112
258
 
113
- info.first[1][:default] = true unless actions.empty?
114
- info
259
+ def loc! hash
260
+ (@meta[:loc] ||= {}).merge! hash
115
261
  end
116
262
 
117
- def access_info handler
118
- @actions.inject({}) do |h, (name, a)|
119
- h[name] = a.check_access!(handler)
120
- h
263
+ def decorate list
264
+ list.each do |f|
265
+ fields(f)[:loc] ||= LOCS[f.to_sym]
121
266
  end
122
267
  end
123
268
 
124
- def recheck_access!
125
- @recheck_access = true
269
+ def render field, options
270
+ fields! field, render: options
271
+ end
272
+
273
+ def hide_fields *flds
274
+ fields! *flds, hidden: true
126
275
  end
127
276
 
128
- def each_action &blk
129
- # no self
130
- @actions.each_pair do |n, a|
131
- a.each_action(&blk) if yield a
277
+ def show_fields *flds
278
+ fields! *flds, hidden: false
279
+ end
280
+
281
+ def field_filter *flds, filter
282
+ fields! *flds, filter: filter
283
+ end
284
+ end
285
+
286
+ module ActionMenuSupport
287
+ def menu menu_name, &blk
288
+ @menus ||= {}
289
+ @menus[menu_name] ||= ActionMenuBuilder.new(:root)
290
+ @menus[menu_name].instance_eval(&blk) if blk
291
+ @menus[menu_name]
292
+ end
293
+
294
+ def menu? menu_name
295
+ @menus && @menus[menu_name]
296
+ end
297
+
298
+ def post_process
299
+ super
300
+ if @menus && !@menus.empty?
301
+ @meta[:menus] = {}
302
+ @menus.each_pair do |name, menu|
303
+ @meta[:menus][name] = {entries: menu.to_a, properties: menu.properties}
304
+ end
132
305
  end
133
306
  end
307
+ end
308
+
309
+ module ActionModelSupport
310
+ def pre_run
311
+ if !(mdl = @assets[:model])
312
+ act = node
313
+ begin
314
+ act = act.parent
315
+ raise E2Error.new("Model not found in tree for node: #{node.name}") unless act
316
+ mdl = act.*.assets[:model]
317
+ end until mdl
134
318
 
135
- def to_a_rec root = true, result = [], &blk # optimize
136
- if root && (yield self)
137
- result << self
138
- @actions.each_pair do |n, a|
139
- if yield a
140
- result << a
141
- a.to_a_rec(false, result, &blk)
319
+ if asc = @assets[:assoc]
320
+ @assets[:model] = asc.associated_class
321
+ # raise E2Error.new("Association '#{asc}' for model '#{asc[:class_name]}' not found") unless @assets[:model]
322
+ else
323
+ @assets[:model] = mdl
324
+ asc = act.*.assets[:assoc]
325
+ @assets[:assoc] = asc if asc
326
+ end
327
+ end
328
+
329
+ # @meta[:model!] = assets[:model]
330
+ # @meta[:assoc!] = assets[:assoc] ? assets[:assoc][:name] : nil
331
+ # @meta[:action_class!] = self.class
332
+ super
333
+ end
334
+
335
+ def hide_pk
336
+ hide_fields *assets[:model].primary_keys
337
+ end
338
+
339
+ def show_pk
340
+ show_fields *assets[:model].primary_keys
341
+ end
342
+
343
+ # def parent_model_name
344
+ # model = @assets[:model]
345
+ # prnt = node.parent
346
+
347
+ # while prnt && prnt.*.assets[:model] == model
348
+ # prnt = prnt.parent
349
+ # end
350
+ # m = prnt.*.assets[:model]
351
+ # m ? m.name : nil
352
+ # end
353
+
354
+ def node_defined
355
+ super
356
+ # p_model_name = parent_model_name
357
+ model = @assets[:model]
358
+
359
+ at = action_type
360
+ case at
361
+ when :list, :star_to_many_list, :star_to_many_link_list, :star_to_many_field, :star_to_many_field_link_list # :many_to_one_list
362
+ model.many_to_one_associations.each do |assoc_name, assoc|
363
+ unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
364
+ dc = model.type_info[assoc[:keys].first][:decode]
365
+ node.run_scheme :decode, model, assoc_name, dc[:search]
366
+ end
367
+ end
368
+ end
369
+
370
+ case at
371
+ when :modify, :create
372
+ model.many_to_one_associations.each do |assoc_name, assoc|
373
+ unless assoc[:propagate] == false # || p_model_name == assoc[:class_name]
374
+ dc = model.type_info[assoc[:keys].first][:decode]
375
+ node.run_scheme :decode, model, assoc_name, dc[:form]
376
+ end
377
+ end
378
+ end
379
+
380
+ case at
381
+ when :list #, :star_to_many_list, :many_to_one_list # list dropdowns
382
+ model.one_to_many_associations.merge(model.many_to_many_associations).each do |assoc_name, assoc|
383
+ unless assoc[:propagate] == false
384
+ node.run_scheme :star_to_many, :"#{assoc_name}!", assoc
385
+ end
386
+ end
387
+ end
388
+
389
+ case at
390
+ when :modify, :create
391
+ model.type_info.each do |field, info|
392
+ case info[:type]
393
+ when :blob_store
394
+ node.run_scheme :blob_store, model, field
395
+ when :foreign_blob_store
396
+ node.run_scheme :foreign_blob_store, model, field
397
+ when :file_store
398
+ node.run_scheme :file_store, model, field
399
+ when :star_to_many_field
400
+ assoc = model.association_reflections[info[:assoc_name]] # info[:name] ?
401
+ raise E2Error.new("Association '#{info[:assoc_name]}' not found for model '#{model}'") unless assoc
402
+ node.run_scheme :star_to_many_field, assoc, field
142
403
  end
143
404
  end
144
405
  end
145
- result
146
406
  end
147
407
 
148
- def inspect
149
- "Action: #{@name}, meta: #{@meta.class}, meta_type: #{@meta.meta_type}"
408
+ def unsupported_association assoc
409
+ raise E2Error.new("Unsupported association: #{assoc}")
150
410
  end
411
+ end
151
412
 
152
- def setup_action_tree
153
- time = ::Time.now
413
+ module ActionQuerySupport
414
+ def query q, &blk
415
+ @query = blk ? q.naked.with_row_proc(blk) : q.naked
416
+ end
417
+
418
+ def post_run
419
+ query select(*assets[:model].columns) unless @query
420
+ super
421
+ end
422
+
423
+ def get_query # move to query ?
424
+ if dynamic?
425
+ @query || @static.get_query
426
+ else
427
+ @query
428
+ end
429
+ end
430
+
431
+ def find_record handler, id
432
+ get_query.load assets[:model].primary_keys_hash_qualified(split_keys(id))
433
+ end
434
+
435
+ def select *args, use_pk: true, &blk
436
+ ds = assets[:model].select(*args, &blk)
437
+ ds = ds.ensure_primary_key if use_pk
438
+ ds.setup_query(@meta[:field_list] = [])
439
+ end
440
+ end
441
+
442
+ module ActionTabSupport
443
+ def select_tabs tabs, *args, &blk
444
+ field_tabs tabs
445
+ select *tabs.map{|name, fields|fields}.flatten, *args, &blk
446
+ end
447
+
448
+ def field_tabs hash
449
+ @meta[:tab_list] = hash.keys
450
+ @meta[:tabs] = hash.reduce({}){|h, (k, v)| h[k] = {name: k, loc: LOCS[k], field_list: v}; h}
451
+ end
452
+
453
+ def tab *tabs, options
454
+ raise E2Error.new("No tabs given to info") if tabs.empty?
455
+ tabs.each do |tab|
456
+ @meta[:tabs][tab].merge! options # rmerge ?
457
+ end
458
+ end
459
+ end
460
+
461
+ module ActionAngularSupport
462
+ def ng_execute expr
463
+ (@meta[:execute] ||= String.new) << expr + ";"
464
+ end
465
+
466
+ def ng_record! name, value
467
+ value = case value
468
+ when String
469
+ "'#{value}'"
470
+ when nil
471
+ 'null'
472
+ else
473
+ value
474
+ end
475
+
476
+ "action.record['#{name}'] = #{value}"
477
+ end
478
+
479
+ def ng_record name
480
+ "action.record['#{name}']"
481
+ end
482
+
483
+ def ng_info! name, *selector, expression
484
+ # expression = "'#{expression}'" if expression.is_a? String
485
+ "action.meta.fields['#{name}'].#{selector.join('.')} = #{expression}"
486
+ end
487
+
488
+ def ng_call name, *args
489
+ # TODO
490
+ end
491
+ end
492
+
493
+ module ActionPanelSupport
494
+ def pre_run
495
+ modal_action true
496
+ super
497
+ end
498
+
499
+ def post_run
500
+ super
501
+ if @meta[:panel]
502
+ panel_panel_template 'menu_m' if panel[:panel_template].nil?
503
+ # modal_action false if panel[:panel_template] == false
504
+ panel_class '' unless panel[:class]
505
+ panel_footer true if panel[:footer] != false && menu?(:panel_menu)
506
+ panel_header true if panel[:header] != false
507
+ end
508
+ end
509
+
510
+ def panel
511
+ @meta[:panel] ||= {}
512
+ end
513
+
514
+ def modal_action modal = true
515
+ panel[:modal_action] = modal
516
+ end
517
+
518
+ def panel_template tmpl
519
+ panel[:template] = tmpl
520
+ end
521
+
522
+ def panel_panel_template tmpl
523
+ panel[:panel_template] = tmpl
524
+ end
525
+
526
+ def panel_class cls
527
+ panel[:class] = cls
528
+ end
529
+
530
+ def panel_title tle
531
+ panel[:title] = tle
532
+ end
533
+
534
+ def panel_header hdr
535
+ panel[:header] = hdr
536
+ end
154
537
 
155
- model_actions = {}
156
- each_action do |action|
157
- if model = action.*.assets[:model]
158
- model_name = model.name.to_sym
159
- model.synchronize_type_info
160
- model_actions[model_name] = action.to_a_rec{|a| !a.*.assets[:assoc]}
161
- action.run_scheme(model_name) if SCHEMES[model_name, false]
162
- false
538
+ def panel_footer ftr
539
+ panel[:footer] = ftr
540
+ end
541
+ end
542
+
543
+ module ActionDraggableSupport
544
+ def draggable
545
+ @meta[:draggable] ||= {}
546
+ end
547
+
548
+ def post_run
549
+ super
550
+ draggable[:position_field] ||= 'position' if @meta[:draggable]
551
+ end
552
+ end
553
+
554
+ class MenuAction < Action
555
+ include ActionMenuSupport
556
+ action_type :menu
557
+
558
+ def invoke handler
559
+ {}
560
+ end
561
+ end
562
+
563
+ class ConfirmAction < Action
564
+ include ActionPanelSupport, ActionMenuSupport
565
+ action_type :confirm
566
+
567
+ def message msg
568
+ @meta[:message] = msg
569
+ end
570
+
571
+ def pre_run
572
+ super
573
+ panel_template 'scaffold/message'
574
+ panel_title LOCS[:confirmation]
575
+ panel_class 'modal-default'
576
+
577
+ menu :panel_menu do
578
+ option :approve, icon: "ok", loc: LOCS[:ok], disabled: "action.action_pending()"
579
+ option :cancel, icon: "remove"
580
+ end
581
+ end
582
+
583
+ def invoke handler
584
+ params = handler.request.params
585
+ # params.merge({arguments: params.keys})
586
+ end
587
+ end
588
+
589
+ module ActionOnChangeSupport
590
+ def on_change field, trigger_on_start = false, &blk
591
+ node_name = :"#{field}_on_change"
592
+ nd = node.define_node node_name, (blk.arity <= 2 ? OnChangeGetAction : OnChangePostAction)
593
+ nd.*{request &blk}
594
+
595
+ fields! field, remote_onchange: {action: node_name, record: blk.arity > 2, trigger_on_start: trigger_on_start}
596
+ end
597
+
598
+ class OnChangeAction < Action
599
+ include ActionAPISupport, ActionAngularSupport
600
+
601
+ def request_action_proc_params handler
602
+ if handler.request.post?
603
+ json = handler.post_to_json
604
+ [json[:value], json[:record]]
163
605
  else
164
- true
165
- end
166
- end
167
-
168
- each_action do |action|
169
- meta = action.*
170
- model = meta.assets[:model]
171
- assoc = meta.assets[:assoc]
172
- if model && assoc
173
- if source_actions = model_actions[model.name.to_sym]
174
- source_action = source_actions.select{|sa| sa.meta_proc && sa.*.class >= meta.class}
175
- # source_action = source_actions.select{|sa| sa.meta_proc && meta.class <= sa.*.class}
176
- unless source_action.empty?
177
- # raise E2Error.new("Multiple meta candidates for #{action.inspect} found in '#{source_action.inspect}'") if source_action.size > 1
178
- # puts "#{action.inspect} => #{source_action.inspect}\n"
179
- meta.instance_eval(&source_action.first.meta_proc)
180
- end
606
+ params = handler.request.params
607
+ [params["value"], params["record"]]
608
+ end
609
+ end
610
+
611
+ # def invoke handler
612
+ # {}
613
+ # end
614
+ end
615
+
616
+ class OnChangeGetAction < OnChangeAction
617
+ action_type :on_change
618
+
619
+ def request_action_proc_params handler
620
+ params = handler.request.params
621
+ [params["value"], params["record"]]
622
+ end
623
+ end
624
+
625
+ class OnChangePostAction < OnChangeAction
626
+ http_method :post
627
+ action_type :on_change
628
+
629
+ def request_action_proc_params handler
630
+ json = handler.post_to_json
631
+ [json[:value], json[:record]]
632
+ end
633
+ end
634
+ end
635
+
636
+ module ActionListSupport
637
+ include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport, ActionOnChangeSupport, ActionDraggableSupport
638
+ attr_reader :filters, :orders, :default_order_field
639
+
640
+ def pre_run
641
+ super
642
+ config.merge!(per_page: 10, use_count: false, selectable: true) # search_active: false,
643
+
644
+ panel_template 'scaffold/list'
645
+ panel_title "#{assets[:model].model_icon.icon} #{LOCS[assets[:model].model_route]}"
646
+ loc! LOCS[:list_locs]
647
+ menu :menu do
648
+ properties break: 2, group_class: "btn-group-sm"
649
+ option :search_toggle, icon: "search", show: "action.meta.search_field_list", active: "action.ui_state.search_active", button_loc: false
650
+ # divider
651
+ option :refresh, icon: "refresh", button_loc: false
652
+ option :default_order, icon: "signal", button_loc: false
653
+ divider
654
+ option :debug_info, icon: "list-alt" do
655
+ option :show_meta, icon: "eye-open"
656
+ end if Handler::development?
657
+ end
658
+
659
+ menu :item_menu do
660
+ properties break: 1, group_class: "btn-group-sm"
661
+ end
662
+
663
+ @meta[:state] = [:query, :ui_state]
664
+ end
665
+
666
+ def field_tabs hash
667
+ super
668
+ search_template 'scaffold/search_tabs'
669
+ end
670
+
671
+ def select_toggle_menu
672
+ m = menu :menu
673
+ unless m.option_index(:select_toggle, false)
674
+ m.option_after :default_order, :select_toggle, icon: "check", enabled: "action.meta.config.selectable", active: "action.selection", button_loc: false
675
+ end
676
+ end
677
+
678
+ def post_run
679
+ super
680
+
681
+ unless panel[:class]
682
+ panel_class case @meta[:field_list].size
683
+ when 1..3; ''
684
+ when 4..6; 'modal-large'
685
+ else; 'modal-huge'
686
+ end
687
+ end
688
+
689
+ @meta[:primary_fields] = assets[:model].primary_keys
690
+ end
691
+
692
+ # def find_renderer type_info
693
+ # renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
694
+ # raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
695
+ # renderer.(self, type_info)
696
+ # end
697
+
698
+ def post_process
699
+ model = assets[:model]
700
+ if fields = @meta[:search_field_list]
701
+ fields = fields - static.meta[:search_field_list] if dynamic?
702
+
703
+ decorate(fields)
704
+ fields.each do |name|
705
+ type_info = model.find_type_info(name)
706
+
707
+ # render = fields[name][:render]
708
+ # if not render
709
+ # fields[name][:render] = find_renderer(type_info)
710
+ # else
711
+ # fields[name][:render].merge!(find_renderer(type_info)){|key, v1, v2|v1}
712
+ # end
713
+
714
+ fields(name)[:render] ||= begin # set before :field_list
715
+ renderer = DefaultSearchRenderers[type_info[:type]] || DefaultSearchRenderers[type_info[:otype]]
716
+ raise E2Error.new("No search renderer found for field '#{type_info[:name]}'") unless renderer
717
+ renderer.(self, type_info)
181
718
  end
719
+
720
+ proc = SearchRendererPostProcessors[type_info[:type]] || ListRendererPostProcessors[type_info[:type]] # ?
721
+ proc.(self, name, type_info) if proc
182
722
  end
723
+ end
183
724
 
184
- meta.instance_eval(&action.meta_proc) if action.meta_proc
185
- true
725
+ if fields = @meta[:field_list]
726
+ fields = fields - static.meta[:field_list] if dynamic?
727
+
728
+ decorate(fields)
729
+ fields.each do |name|
730
+ type_info = model.find_type_info(name)
731
+ proc = ListRendererPostProcessors[type_info[:type]]
732
+ proc.(self, name, type_info) if proc
733
+ end
186
734
  end
187
735
 
188
- each_action do |action|
189
- action.*.post_run
190
- action.*.freeze_meta
736
+ super
737
+ end
738
+
739
+ def search_template template
740
+ panel[:search_template] = template
741
+ end
742
+
743
+ def sortable *flds
744
+ flds = @meta[:field_list] if flds.empty?
745
+ fields! *flds, sort: true
746
+ end
747
+
748
+ def default_order order
749
+ @default_order_field = order
750
+ end
751
+
752
+ def search_live *flds
753
+ if flds.empty?
754
+ flds = @meta[:search_field_list]
755
+ @meta[:disable_search_button] = true
756
+ end
757
+ fields! *flds, search_live: true
758
+ end
759
+
760
+ def searchable *flds
761
+ @meta.delete(:tab_list)
762
+ @meta.delete(:tabs)
763
+ search_template 'scaffold/search'
764
+ @meta[:search_field_list] = *flds
765
+ end
766
+
767
+ def searchable_tabs tabs
768
+ searchable *tabs.map{|name, fields|fields}.flatten
769
+ field_tabs tabs
770
+ end
771
+
772
+ def template
773
+ SearchTemplates
774
+ end
775
+
776
+ def filter name, &blk
777
+ (@filters ||= {})[name] = blk
778
+ end
779
+
780
+ def filter_case_insensitive name
781
+ model = assets[:model]
782
+ raise E2Error.new("Field '#{name}' needs to be a string") unless model.find_type_info(name)[:otype] == :string
783
+ filter name do |handler, query, hash|
784
+ value = hash[name]
785
+ value ? query.where(model.table_name.q(name).ilike("%#{value}%")) : query
786
+ end
787
+ end
788
+
789
+ def order name, &blk
790
+ (@orders ||= {})[name] = blk
791
+ end
792
+ end
793
+
794
+ module ActionApproveSupport
795
+ include ActionModelSupport
796
+ attr_reader :validations
797
+
798
+ def self.included action
799
+ action.http_method :post if action.is_a? Class
800
+ end
801
+
802
+ def validate_fields *fields
803
+ if fields.empty?
804
+ @validate_fields
805
+ else
806
+ @validate_fields = assets[:model].type_info.keys & (fields + assets[:model].primary_keys).uniq
807
+ end
808
+ end
809
+
810
+ def before_approve handler, record
811
+ end
812
+
813
+ def after_approve handler, record
814
+ end
815
+
816
+ def validate_and_approve handler, record, parent_id
817
+ static.before_approve(handler, record)
818
+ record.valid?
819
+ validate_record(handler, record, parent_id)
820
+ if record.errors.empty?
821
+ static.after_approve(handler, record)
191
822
  true
823
+ else
824
+ false
192
825
  end
826
+ end
827
+
828
+ def allocate_record handler, json_rec
829
+ model = assets[:model]
830
+ handler.permit json_rec.is_a?(Hash)
831
+ val_fields = (dynamic? ? static.validate_fields : @validate_fields) || model.type_info.keys
832
+ left_fields = (json_rec.keys - val_fields)
833
+
834
+ puts "Left: #{left_fields.inspect}" unless left_fields.empty?
835
+ handler.permit left_fields.empty?
836
+
837
+ record = model.call(json_rec)
838
+ record.validate_fields = val_fields
839
+ record
840
+ end
841
+
842
+ def record handler, record
843
+ {errors: nil}
844
+ end
845
+
846
+ def invoke handler
847
+ json = handler.post_to_json
848
+ record = allocate_record(handler, json[:record])
849
+ validate_and_approve(handler, record, json[:parent_id]) ? static.record(handler, record) : {record!: record.to_hash, errors!: record.errors}
850
+ end
851
+
852
+ def validate name, &blk
853
+ (@validations ||= {})[name] = blk
854
+ end
855
+
856
+ def validate_record handler, record, parent_id
857
+ @validations.each do |name, val|
858
+ unless record.errors[name]
859
+ result = val.(handler, record, parent_id)
860
+ record.errors.add(name, result) if result
861
+ end
862
+ end if @validations
863
+ end
193
864
 
194
- ::Kernel::puts "ACTIONS: #{Action.count}, Time: #{::Time.now - time}"
865
+ def pre_run
866
+ super
867
+ execute "action.errors || [action.parent().invoke(), action.panel_close()]"
195
868
  end
196
869
 
197
- def p *args
198
- ::Kernel::p *args
870
+ def post_run
871
+ super
872
+ validate_fields *node.parent.*.meta[:field_list] unless validate_fields
199
873
  end
200
874
  end
201
875
 
876
+ module ActionSaveSupport
877
+ include ActionApproveSupport
202
878
 
203
- class ActionBundle
204
- def initialize action, action_names
205
- @action = action
206
- @action_names = action_names
879
+ def self.included action
880
+ action.http_method :post
881
+ class << action
882
+ attr_accessor :validate_only
883
+ end
207
884
  end
208
885
 
209
- def method_missing name, *args, &blk
210
- @action_names.each{|an| @action[an].__send__(name, *args, &blk)}
886
+ def validate_and_approve handler, record, parent_id, validate_only = self.class.validate_only
887
+ if validate_only
888
+ super(handler, record, parent_id)
889
+ else
890
+ record.skip_save_refresh = true
891
+ record.raise_on_save_failure = false
892
+ model = assets[:model]
893
+ assoc = assets[:assoc]
894
+ new_assoc = record.new? && assoc && assoc[:type]
895
+
896
+ save = lambda do |c|
897
+ if super(handler, record, parent_id)
898
+ if new_assoc == :one_to_many
899
+ handler.permit parent_id
900
+ assoc[:keys].zip(split_keys(parent_id)).each{|k, v|record[k] = v}
901
+ end
902
+
903
+ result = record.save(transaction: false, validate: false)
904
+ if result && new_assoc == :many_to_many
905
+ handler.permit parent_id
906
+ model.db[assoc[:join_table]].insert(assoc[:left_keys] + assoc[:right_keys], split_keys(parent_id) + record.primary_key_values)
907
+ end
908
+
909
+ model.association_reflections.each do |name, assoc|
910
+ hash = record[name]
911
+ if hash.is_a?(Hash)
912
+ validate_and_approve_association(handler, record, name, :create, hash)
913
+ validate_and_approve_association(handler, record, name, :modify, hash)
914
+ nd = node.parent[:"#{name}!"]
915
+ raise Sequel::Rollback unless record.errors.empty?
916
+ nd.confirm_delete.delete.*.invoke_delete_db(handler, hash[:delete].to_a, model.table_name) unless hash[:delete].to_a.empty?
917
+ nd.link.*.invoke_link_db(handler, record.primary_key_values, hash[:link].to_a) unless hash[:link].to_a.empty?
918
+ nd.confirm_unlink.unlink.*.invoke_unlink_db(handler, record.primary_key_values, hash[:unlink].to_a) unless hash[:unlink].to_a.empty?
919
+ end
920
+ end
921
+ after_save(handler, record)
922
+ result
923
+ end
924
+ end
925
+ (model.validation_in_transaction || new_assoc == :many_to_many) ? model.db.transaction(&save) : save.(nil)
926
+ end
927
+ end
928
+
929
+ def validate_and_approve_association handler, record, assoc_name, node_name, hash
930
+ records = hash[node_name].to_a
931
+ unless records.empty?
932
+ action = node.parent[:"#{assoc_name}!"][node_name].approve.*
933
+ parent_id = join_keys(record.primary_key_values)
934
+ records.each do |arec|
935
+ rec = action.allocate_record(handler, arec)
936
+ action.validate_and_approve(handler, rec, parent_id, false)
937
+ rec.errors.each do |k, v|
938
+ (record.errors[assoc_name] ||= []).concat(v)
939
+ end unless rec.errors.empty?
940
+ end
941
+ end
942
+ end
943
+
944
+ def after_save handler, record
945
+ end
946
+ end
947
+
948
+ module ActionInsertSupport
949
+ def allocate_record handler, json_rec
950
+ record = super(handler, json_rec)
951
+ record.instance_variable_set(:"@new", true)
952
+ model = assets[:model]
953
+ model.primary_keys.each{|k|record.values.delete k} unless model.natural_key
954
+ handler.permit !record.has_primary_key? unless model.natural_key
955
+ record
956
+ end
957
+ end
958
+
959
+ module ActionUpdateSupport
960
+ def allocate_record handler, json_rec
961
+ record = super(handler, json_rec)
962
+ model = assets[:model]
963
+ handler.permit record.has_primary_key? unless model.natural_key or self.class.validate_only
964
+ record
211
965
  end
212
966
  end
213
- end
967
+
968
+ module ActionFormSupport
969
+ include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport, ActionAngularSupport, ActionOnChangeSupport
970
+
971
+ def field_template template
972
+ panel[:field_template] = template
973
+ end
974
+
975
+ def pre_run
976
+ super
977
+ panel_template 'scaffold/form'
978
+ field_template 'scaffold/fields'
979
+ panel_class 'modal-large'
980
+ top = node.parent.parent == nil
981
+ menu :panel_menu do
982
+ option :approve, icon: "ok", disabled: "action.action_pending()" # text: true,
983
+ option :cancel, icon: "remove" unless top # text: true,
984
+ end
985
+ # modal_action false
986
+ end
987
+
988
+ def field_tabs hash
989
+ super
990
+ panel_template 'scaffold/form_tabs'
991
+ end
992
+
993
+ def record handler, record
994
+ end
995
+
996
+ def post_process
997
+ if fields = @meta[:field_list]
998
+ model = assets[:model]
999
+ fields = fields - static.meta[:field_list] if dynamic?
1000
+
1001
+ decorate(fields)
1002
+
1003
+ fields.each do |name|
1004
+ type_info = model.find_type_info(name)
1005
+
1006
+ fields(name)[:render] ||= begin
1007
+ renderer = DefaultFormRenderers[type_info[:type]] # .merge(default: true)
1008
+ raise E2Error.new("No form renderer found for field '#{type_info[:name]}' of type '#{type_info[:type]}'") unless renderer
1009
+ renderer.(self, type_info)
1010
+ end
1011
+
1012
+ proc = FormRendererPostProcessors[type_info[:type]]
1013
+ proc.(self, name, type_info) if proc
1014
+ end
1015
+
1016
+ assoc = assets[:assoc]
1017
+ if assoc && assoc[:type] == :one_to_many
1018
+ # fields.select{|f| assoc[:keys].include? f}.each do |key|
1019
+ # # hide_fields(key) if self[:fields, key, :hidden] == nil
1020
+ # fields! key, disabled: true
1021
+ # end
1022
+ assoc[:keys].each do |key|
1023
+ fields! key, disabled: true if fields.include? key
1024
+ end
1025
+ end
1026
+ end
1027
+
1028
+ super
1029
+ end
1030
+
1031
+ def post_run
1032
+ super
1033
+ @meta[:primary_fields] = assets[:model].primary_keys
1034
+ end
1035
+
1036
+ def template
1037
+ Templates
1038
+ end
1039
+
1040
+ def hr_after field, message = '-'
1041
+ fields! field, hr: message
1042
+ end
1043
+
1044
+ def create?
1045
+ @action_type == :create
1046
+ end
1047
+
1048
+ def modify?
1049
+ @action_type == :modify
1050
+ end
1051
+ end
1052
+
1053
+ module ActionCreateSupport
1054
+ include ActionFormSupport
1055
+
1056
+ def self.included action
1057
+ action.action_type :create
1058
+ end
1059
+
1060
+ def pre_run
1061
+ super
1062
+ panel_title "#{LOCS[:create_title]} - #{LOCS[assets[:model].table_name]}"
1063
+ node.parent.*.menu(:menu).option_at 0, node.name, icon: "plus-sign", button_loc: false if node.parent.*.is_a?(ActionListSupport)
1064
+
1065
+ hide_pk unless assets[:model].natural_key
1066
+ end
1067
+
1068
+ def record handler, record
1069
+ create_record(handler, record)
1070
+ end
1071
+
1072
+ def create_record handler, record
1073
+ end
1074
+
1075
+ def invoke handler
1076
+ record = {}
1077
+ # if assoc = assets[:assoc]
1078
+ # case assoc[:type]
1079
+ # when :one_to_many
1080
+ # parent = handler.params[:parent_id]
1081
+ # assoc[:keys].zip(split_keys(parent)).each{|key, val| record[key] = val} if parent
1082
+ # end
1083
+ # end
1084
+ static.record(handler, record)
1085
+ {record: record, new: true}
1086
+ end
1087
+ end
1088
+
1089
+ module ActionModifySupport
1090
+ include ActionFormSupport
1091
+
1092
+ def self.included action
1093
+ action.action_type :modify
1094
+ end
1095
+
1096
+ def pre_run
1097
+ super
1098
+ panel_title "#{LOCS[:modify_title]} - #{LOCS[assets[:model].table_name]}"
1099
+ node.parent.*.menu(:item_menu).option node.name, icon: "pencil", button_loc: false
1100
+ end
1101
+
1102
+ def record handler, record
1103
+ modify_record(handler, record)
1104
+ end
1105
+
1106
+ def modify_record handler, record
1107
+ end
1108
+
1109
+ def invoke handler
1110
+ handler.permit id = handler.params[:id]
1111
+ record = find_record(handler, id)
1112
+
1113
+ if record
1114
+ static.record(handler, record)
1115
+ {record: record}
1116
+ else
1117
+ handler.halt_not_found LOCS[:no_entry]
1118
+ end
1119
+ end
1120
+
1121
+ def post_run
1122
+ super
1123
+ assets[:model].primary_keys.each do |key| # pre_run ?
1124
+ fields! key, disabled: true
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ module ActionViewSupport
1130
+ include ActionModelSupport, ActionAPISupport, ActionTabSupport, ActionPanelSupport, ActionMenuSupport
1131
+
1132
+ def self.included action
1133
+ action.action_type :view
1134
+ end
1135
+
1136
+ def pre_run
1137
+ super
1138
+ panel_template 'scaffold/view'
1139
+ panel_title "#{LOCS[:view_title]} - #{LOCS[assets[:model].table_name]}"
1140
+ panel[:backdrop] = true
1141
+
1142
+ menu(:panel_menu).option :close, icon: "remove"
1143
+ node.parent.*.menu(:item_menu).option node.name, icon: "file", button_loc: false
1144
+ end
1145
+
1146
+ def field_tabs hash
1147
+ super
1148
+ panel_template 'scaffold/view_tabs'
1149
+ end
1150
+
1151
+ def record handler, record
1152
+ end
1153
+
1154
+ def invoke handler
1155
+ handler.permit id = handler.params[:id]
1156
+ record = find_record(handler, id)
1157
+ if record
1158
+ static.record(handler, record)
1159
+ {record: record}
1160
+ else
1161
+ handler.halt_not_found LOCS[:no_entry]
1162
+ end
1163
+ end
1164
+
1165
+ def post_process
1166
+ if fields = @meta[:field_list]
1167
+ model = assets[:model]
1168
+ fields = fields - static.meta[:field_list] if dynamic?
1169
+
1170
+ decorate(fields)
1171
+ fields.each do |name|
1172
+ type_info = model.find_type_info(name)
1173
+ proc = ListRendererPostProcessors[type_info[:type]]
1174
+ proc.(self, name, type_info) if proc
1175
+ end
1176
+ end
1177
+
1178
+ super
1179
+ end
1180
+ end
1181
+
1182
+ module ActionDeleteSupport
1183
+ include ActionModelSupport
1184
+
1185
+ def self.included action
1186
+ action.http_method :delete
1187
+ action.action_type :delete
1188
+ end
1189
+
1190
+ def pre_run
1191
+ super
1192
+ execute "action.errors || [action.parent().invoke(), action.panel_close()]"
1193
+ node.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
1194
+ end
1195
+ end
1196
+
1197
+ module ActionBulkDeleteSupport
1198
+ include ActionModelSupport
1199
+
1200
+ def self.included action
1201
+ action.http_method :delete
1202
+ action.action_type :bulk_delete
1203
+ end
1204
+
1205
+ def pre_run
1206
+ super
1207
+ execute "action.errors || [action.parent().invoke(), action.panel_close()]"
1208
+ node.parent.parent.*.select_toggle_menu
1209
+ node.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
1210
+ end
1211
+ end
1212
+
1213
+ (FormRendererPostProcessors ||= {}).merge!(
1214
+ boolean: lambda{|action, field, info|
1215
+ action.fields(field)[:render].merge! true_value: info[:true_value], false_value: info[:false_value]
1216
+ action.fields(field)[:dont_strip] = info[:dont_strip] if info[:dont_strip]
1217
+ },
1218
+ date: lambda{|action, field, info|
1219
+ action.fields(field)[:render].merge! format: info[:format], model_format: info[:model_format]
1220
+ if date_to = info[:other_date]
1221
+ action.fields(field)[:render].merge! other_date: date_to #, format: info[:format], model_format: info[:model_format]
1222
+ action.hide_fields date_to
1223
+ elsif time = info[:other_time]
1224
+ action.fields(field)[:render].merge! other_time: time
1225
+ action.hide_fields time
1226
+ end
1227
+ },
1228
+ time: lambda{|action, field, info|
1229
+ render = action.fields(field)[:render]
1230
+ render[:type] ||= info[:otype] == :string ? :string : :number
1231
+ render.merge! format: info[:format], model_format: info[:model_format]
1232
+ },
1233
+ decimal_date: lambda{|action, field, info|
1234
+ FormRendererPostProcessors[:date].(action, field, info)
1235
+ action.fields! field, type: :decimal_date
1236
+ },
1237
+ decimal_time: lambda{|action, field, info|
1238
+ FormRendererPostProcessors[:time].(action, field, info)
1239
+ action.fields! field, type: :decimal_time
1240
+ },
1241
+ datetime: lambda{|action, field, info|
1242
+ action.fields(field)[:render].merge! date_format: info[:date_format], time_format: info[:time_format], date_model_format: info[:date_model_format], time_model_format: info[:time_model_format]
1243
+ },
1244
+ currency: lambda{|action, field, info|
1245
+ action.fields(field)[:render].merge! symbol: info[:symbol]
1246
+ },
1247
+ # date_range: lambda{|action, field, info|
1248
+ # action.fields[field][:render].merge! other_date: info[:other_date], format: info[:format], model_format: info[:model_format]
1249
+ # action.hide_fields info[:other_date]
1250
+ # action.fields[field][:decimal_date] = true if info[:validations][:decimal_date]
1251
+ # },
1252
+ list_select: lambda{|action, field, info|
1253
+ render = action.fields(field)[:render]
1254
+ render.merge! values: info[:values]
1255
+ render.merge! max_length: info[:max_length], max_length_html: info[:max_length_html], separator: info[:separator] if info[:multiselect]
1256
+ },
1257
+ many_to_one: lambda{|action, field, info|
1258
+ field_info = action.fields(field)
1259
+ field_info[:assoc] = :"#{info[:assoc_name]}!"
1260
+ field_info[:fields] = info[:keys]
1261
+ field_info[:type] = info[:otype]
1262
+
1263
+ (info[:keys] - [field]).each do |of|
1264
+ f_info = action.fields(of)
1265
+ f_info[:hidden] = true
1266
+ f_info[:type] = action.assets[:model].type_info[of].fetch(:otype)
1267
+ end
1268
+ },
1269
+ file_store: lambda{|action, field, info|
1270
+ action.fields(field)[:render].merge! multiple: info[:multiple]
1271
+ },
1272
+ star_to_many_field: lambda{|action, field, info|
1273
+ field_info = action.fields(field)
1274
+ field_info[:assoc] = :"#{info[:assoc_name]}!"
1275
+ }
1276
+ )
1277
+
1278
+ (ListRendererPostProcessors ||= {}).merge!(
1279
+ boolean: lambda{|action, field, info|
1280
+ action.fields! field, type: :boolean # move to action ?
1281
+ action.fields(field)[:render] ||= {}
1282
+ action.fields(field)[:render].merge! true_value: info[:true_value], false_value: info[:false_value]
1283
+ },
1284
+ list_select: lambda{|action, field, info|
1285
+ action.fields! field, type: :list_select
1286
+ render = (action.fields(field)[:render] ||= {})
1287
+ render.merge! values: info[:values]
1288
+ render.merge! multiselect: true if info[:multiselect]
1289
+ },
1290
+ datetime: lambda{|action, field, info|
1291
+ action.fields! field, type: :datetime
1292
+ },
1293
+ decimal_date: lambda{|action, field, info|
1294
+ action.fields! field, type: :decimal_date
1295
+ },
1296
+ decimal_time: lambda{|action, field, info|
1297
+ action.fields! field, type: :decimal_time
1298
+ },
1299
+ # date_range: lambda{|action, field, info|
1300
+ # action.fields[field][:type] = :decimal_date if info[:validations][:decimal_date] # ? :decimal_date : :date
1301
+ # }
1302
+ )
1303
+
1304
+ (SearchRendererPostProcessors ||= {}).merge!(
1305
+ many_to_one: lambda{|action, field, info|
1306
+ model = action.assets[:model]
1307
+ if model.type_info[field]
1308
+ keys = info[:keys]
1309
+ else
1310
+ action.check_static_action
1311
+ model = model.many_to_one_associations[field.table].associated_class
1312
+ keys = info[:keys].map{|k| model.table_name.q(k)}
1313
+ end
1314
+
1315
+ field_info = action.fields(field)
1316
+ field_info[:assoc] = :"#{info[:assoc_name]}!"
1317
+ field_info[:fields] = keys
1318
+ field_info[:type] = info[:otype]
1319
+
1320
+ (keys - [field]).each do |of|
1321
+ f_info = action.fields(of)
1322
+ raise E2Error.new("Missing searchable field: '#{of}' in model '#{action.assets[:model]}'") unless f_info
1323
+ f_info[:hidden_search] = true
1324
+ f_info[:type] = model.type_info[of].fetch(:otype)
1325
+ end
1326
+ },
1327
+ date: lambda{|action, field, info|
1328
+ action.fields(field)[:render] ||= {}
1329
+ action.fields(field)[:render].merge! format: info[:format], model_format: info[:model_format] # Model::DEFAULT_DATE_FORMAT
1330
+ },
1331
+ datetime: lambda{|action, field, info|
1332
+ action.fields(field)[:render] ||= {}
1333
+ action.fields(field)[:render].merge! format: info[:date_format], model_format: info[:date_model_format] # Model::DEFAULT_DATE_FORMAT
1334
+ },
1335
+ decimal_date: lambda{|action, field, info|
1336
+ SearchRendererPostProcessors[:date].(action, field, info)
1337
+ }
1338
+ )
1339
+
1340
+ (DefaultFormRenderers ||= {}).merge!(
1341
+ date: lambda{|action, info|
1342
+ info[:other_date] ? Templates.date_range : (info[:other_time] ? Templates.date_time : Templates.date_picker)
1343
+
1344
+ },
1345
+ time: lambda{|action, info| Templates.time_picker},
1346
+ datetime: lambda{|action, info| Templates.datetime_picker},
1347
+ file_store: lambda{|action, info| Templates.file_store},
1348
+ blob: lambda{|action, info| Templates.blob}, # !!!
1349
+ blob_store: lambda{|action, info| Templates.blob},
1350
+ foreign_blob_store: lambda{|action, info| Templates.blob},
1351
+ string: lambda{|action, info| Templates.input_text(info[:length])},
1352
+ text: lambda{|action, info| Templates.text},
1353
+ integer: lambda{|action, info| Templates.integer},
1354
+ decimal: lambda{|action, info| Templates.decimal},
1355
+ decimal_date: lambda{|action, info| DefaultFormRenderers[:date].(action, info)},
1356
+ decimal_time: lambda{|action, info| Templates.time_picker},
1357
+ email: lambda{|action, info| Templates.email(info[:length])},
1358
+ password: lambda{|action, info| Templates.password(info[:length])},
1359
+ # date_range: lambda{|action, info| Templates.date_range},
1360
+ boolean: lambda{|action, info| Templates.checkbox_button},
1361
+ currency: lambda{|action, info| Templates.currency},
1362
+ list_select: lambda{|action, info|
1363
+ length = info[:values].length
1364
+ max_length = length > 0 ? info[:values].map(&:last).max_by(&:length).length : 0
1365
+ if info[:multiselect]
1366
+ Templates.list_bsmselect(max_length)
1367
+ elsif length <= 3
1368
+ Templates.list_buttons(optional: !info[:required])
1369
+ elsif length <= 15
1370
+ Templates.list_bsselect(max_length, optional: !info[:required])
1371
+ else
1372
+ Templates.list_select(max_length, optional: !info[:required])
1373
+ end
1374
+ },
1375
+ star_to_many_field: lambda{|action, info| Templates.scaffold},
1376
+ many_to_one: lambda{|action, info|
1377
+ tmpl_type = info[:decode][:form]
1378
+ case
1379
+ when tmpl_type[:scaffold]; Templates.scaffold_picker
1380
+ when tmpl_type[:list]; Templates.bsselect_picker
1381
+ when tmpl_type[:typeahead];Templates.typeahead_picker
1382
+ else
1383
+ raise E2Error.new("Unknown decode type #{tmpl_type}")
1384
+ end
1385
+ }, # required/opt
1386
+ )
1387
+
1388
+ (DefaultSearchRenderers ||= {}).merge!(
1389
+ date: lambda{|action, info| SearchTemplates.date_range},
1390
+ datetime: lambda{|action, info| SearchTemplates.date_range},
1391
+ decimal_date: lambda{|action, info| SearchTemplates.date_range},
1392
+ integer: lambda{|action, info| SearchTemplates.integer_range},
1393
+ string: lambda{|action, info| SearchTemplates.input_text},
1394
+ boolean: lambda{|action, info| SearchTemplates.checkbox_buttons},
1395
+ list_select: lambda{|action, info|
1396
+ length = info[:values].length
1397
+ if length <= 3
1398
+ SearchTemplates.list_buttons
1399
+ elsif length <= 15
1400
+ # max_length = info[:list].max_by{|a|a.last.length}.last.length
1401
+ SearchTemplates.list_bsselect(multiple: info[:multiple])
1402
+ else
1403
+ # max_length = info[:list].max_by{|a|a.last.length}.last.length
1404
+ SearchTemplates.list_select
1405
+ end
1406
+ },
1407
+ many_to_one: lambda{|action, info|
1408
+ tmpl_type = info[:decode][:search]
1409
+ case
1410
+ when tmpl_type[:scaffold]; SearchTemplates.scaffold_picker(multiple: tmpl_type[:multiple])
1411
+ when tmpl_type[:list]; SearchTemplates.bsselect_picker(multiple: tmpl_type[:multiple])
1412
+ when tmpl_type[:typeahead];SearchTemplates.typeahead_picker
1413
+ else
1414
+ raise E2Error.new("Unknown decode type #{tmpl_type}")
1415
+ end
1416
+ }
1417
+ )
1418
+ end