engine2 1.0.4 → 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 (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,8 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Engine2
5
+ class ViewAction < Action
6
+ include ActionViewSupport, ActionQuerySupport
7
+ end
8
+ end
@@ -0,0 +1,221 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Engine2
5
+ class ActionNode < BasicObject
6
+ ACCESS_FORBIDDEN ||= ->h{false}
7
+ attr_reader :parent, :name, :number, :nodes, :recheck_access
8
+ attr_reader :meta_proc, :access_block
9
+
10
+ class << self
11
+ attr_accessor :count
12
+ end
13
+
14
+ def initialize parent, name, action_class, assets
15
+ ActionNode.count += 1
16
+ @number = ActionNode.count
17
+ @parent = parent
18
+ @name = name
19
+ @action = action_class.new(self, assets)
20
+ @nodes = {}
21
+ end
22
+
23
+ def * &blk
24
+ @meta_proc = @meta_proc ? @meta_proc.chain(&blk) : blk if blk
25
+ @action
26
+ end
27
+
28
+ alias :action :*
29
+
30
+ def access! &blk
31
+ ::Kernel.raise E2Error.new("Access for node #{name} already defined") if @access_block
32
+ @access_block = blk
33
+ end
34
+
35
+ def access_forbidden!
36
+ access! &ACCESS_FORBIDDEN
37
+ end
38
+
39
+ def check_access! handler
40
+ !@access_block || @access_block.(handler)
41
+ end
42
+
43
+ def run_scheme name, *args, &blk
44
+ result = instance_exec(*args, &SCHEMES[name])
45
+ result.instance_eval(&blk) if blk
46
+ result
47
+ end
48
+
49
+ def define_node name, action_class = InlineAction.inherit, assets = {}, &blk
50
+ ::Kernel.raise E2Error.new("ActionNode #{name} already defined") if @nodes[name]
51
+ node = @nodes[name] = ActionNode.new(self, name, action_class, assets)
52
+ node.*.pre_run
53
+ define_singleton_method! name do |&ablk| # forbidden list
54
+ node.instance_eval(&ablk) if ablk
55
+ node
56
+ end
57
+ node.instance_eval(&blk) if blk
58
+ node.*.node_defined
59
+ node
60
+ end
61
+
62
+ def define_action name, action_class = InlineAction.inherit, assets = {}, &blk
63
+ define_node name, action_class, assets do
64
+ self.* &blk
65
+ end
66
+ end
67
+
68
+ def define_invoke name, action_class = InlineAction.inherit, assets = {}, &blk
69
+ define_node name, action_class, assets do
70
+ self.*.define_invoke &blk
71
+ end
72
+ end
73
+
74
+ def define_node_bundle name, *nodes
75
+ define_singleton_method!(name) do |&blk|
76
+ if blk
77
+ nodes.each{|a|__send__(a, &blk)} # if @nodes[node] ?
78
+ else
79
+ ActionNodeBundle.new(self, nodes)
80
+ end
81
+ end
82
+ end
83
+
84
+ def define_singleton_method! name, &blk
85
+ class << self;self;end.instance_eval do # __realclass__
86
+ define_method name, &blk
87
+ end
88
+ end
89
+
90
+ def [] name
91
+ @nodes[name]
92
+ end
93
+
94
+ def nodes_info handler
95
+ info = nodes.reduce({}) do |h, (name, a)|
96
+ action = a.*
97
+ act = {
98
+ action_type: action.action_type,
99
+ method: action.http_method,
100
+ number: a.number,
101
+ terminal: a.nodes.empty?,
102
+ meta: !action.meta.empty?
103
+ }
104
+
105
+ act[:access] = true if !recheck_access && a.check_access!(handler)
106
+ act[:recheck_access] = true if a.recheck_access
107
+
108
+ if Handler::development?
109
+ act[:action_class] = action.class
110
+ act[:access_block] = a.access_block if a.access_block
111
+ act[:model] = action.assets[:model]
112
+ end
113
+
114
+ h[name] = act
115
+ h
116
+ end
117
+
118
+ info.first[1][:default] = true unless nodes.empty?
119
+ info
120
+ end
121
+
122
+ def access_info handler
123
+ @nodes.reduce({}) do |h, (name, a)|
124
+ h[name] = a.check_access!(handler)
125
+ h
126
+ end
127
+ end
128
+
129
+ def recheck_access!
130
+ @recheck_access = true
131
+ end
132
+
133
+ def each_node &blk
134
+ # no self
135
+ @nodes.each_pair do |n, a|
136
+ a.each_node(&blk) if yield a
137
+ end
138
+ end
139
+
140
+ def to_a_rec root = true, result = [], &blk # optimize
141
+ if root && (yield self)
142
+ result << self
143
+ @nodes.each_pair do |n, a|
144
+ if yield a
145
+ result << a
146
+ a.to_a_rec(false, result, &blk)
147
+ end
148
+ end
149
+ end
150
+ result
151
+ end
152
+
153
+ def inspect
154
+ "ActionNode: #{@name}, action: #{@action.class}, action_type: #{@action.action_type}"
155
+ end
156
+
157
+ def setup_node_tree
158
+ time = ::Time.now
159
+
160
+ model_nodes = {}
161
+ each_node do |node|
162
+ if model = node.*.assets[:model]
163
+ model_name = model.name.to_sym
164
+ model_nodes[model_name] = node.to_a_rec{|a| !a.*.assets[:assoc]}
165
+ node.run_scheme(model_name) if SCHEMES[model_name, false]
166
+ false
167
+ else
168
+ true
169
+ end
170
+ end
171
+
172
+ thefts = 0
173
+ panels = 0
174
+ each_node do |node|
175
+ action = node.*
176
+ model = action.assets[:model]
177
+ assoc = action.assets[:assoc]
178
+ if model && assoc
179
+ if source_nodes = model_nodes[model.name.to_sym]
180
+ source_node = source_nodes.select{|sa| sa.meta_proc && sa.*.class >= action.class}
181
+ # source_node = source_nodes.select{|sa| sa.meta_proc && action.class <= sa.*.class}
182
+ unless source_node.empty?
183
+ raise E2Error.new("Multiple action candidates for #{node.inspect} found in '#{source_node.inspect}'") if source_node.size > 1
184
+ # puts "#{node.inspect} => #{source_node.inspect}\n"
185
+ action.instance_eval(&source_node.first.meta_proc)
186
+ thefts += 1
187
+ end
188
+ end
189
+ end
190
+
191
+ action.instance_eval(&node.meta_proc) if node.meta_proc
192
+ true
193
+ end
194
+
195
+ each_node do |node|
196
+ meta = node.*
197
+ meta.post_run
198
+ meta.freeze_action
199
+ panels += 1 if node.*.is_a? ActionPanelSupport
200
+ true
201
+ end
202
+
203
+ ::Kernel::puts "ACTION NODES: #{ActionNode.count}, Panels: #{panels}, Thefts: #{thefts}, Time: #{::Time.now - time}"
204
+ end
205
+
206
+ def p *args
207
+ ::Kernel::p *args
208
+ end
209
+ end
210
+
211
+ class ActionNodeBundle
212
+ def initialize node, node_names
213
+ @node = node
214
+ @node_names = node_names
215
+ end
216
+
217
+ def method_missing name, *args, &blk
218
+ @node_names.each{|an| @node[an].__send__(name, *args, &blk)}
219
+ end
220
+ end
221
+ end
@@ -1,4 +1,5 @@
1
- #coding: utf-8
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PrettyJSON
4
5
  def to_json_pretty
@@ -13,9 +14,19 @@ class BigDecimal
13
14
  end
14
15
  end
15
16
 
17
+ class Sequel::SQL::QualifiedIdentifier
18
+ def to_json(*)
19
+ "\"#{table}__#{column}\""
20
+ end
21
+
22
+ def to_sym
23
+ :"#{table}__#{column}"
24
+ end
25
+ end
26
+
16
27
  class Object
17
28
  def instance_variables_hash
18
- instance_variables.inject({}) do |h, i|
29
+ instance_variables.reduce({}) do |h, i|
19
30
  h[i] = instance_variable_get(i)
20
31
  h
21
32
  end
@@ -25,16 +36,24 @@ end
25
36
  class Proc
26
37
  def to_json(*)
27
38
  loc = source_location
28
- "\"#<Proc:#{loc.first[/\w+.rb/]}:#{loc.last}>\""
39
+ loc ? "\"#<Proc:#{loc.first[/\w+.rb/]}:#{loc.last}>\"" : '"source unknown"'
29
40
  end
30
41
 
31
42
  def chain &blk
32
- proc = self
43
+ prc = self
33
44
  lambda do |obj|
34
- obj.instance_eval(&proc)
45
+ obj.instance_eval(&prc)
35
46
  obj.instance_eval(&blk)
36
47
  end
37
48
  end
49
+
50
+ def chain_args &blk
51
+ prc = self
52
+ lambda do |*args|
53
+ instance_exec(*args, &prc)
54
+ instance_exec(*args, &blk)
55
+ end
56
+ end
38
57
  end
39
58
 
40
59
  class Hash
@@ -107,15 +126,45 @@ class String
107
126
  s
108
127
  end
109
128
  end
129
+
130
+ def escape_html
131
+ Rack::Utils.escape_html(self)
132
+ end
110
133
  end
111
134
 
112
135
  class Symbol
113
136
  def icon
114
- "<span class='glyphicon glyphicon-#{self}'></span>"
137
+ s = self
138
+ if s[0, 3] == 'fa_'
139
+ "<i class='fa fa-#{s[3 .. -1]}'></i>"
140
+ else
141
+ "<span class='glyphicon glyphicon-#{s}'></span>"
142
+ end
143
+ end
144
+
145
+ def loc
146
+ Engine2::LOCS[self]
147
+ end
148
+
149
+ def q col
150
+ col.qualify self
151
+ end
152
+
153
+ def html body = '', attrs = {}
154
+ element = self.to_s
155
+ attrs = attrs.map{|k, v| "#{k}=\"#{v}\""}.join(" ")
156
+ "<#{element} #{attrs}>#{body}</#{element}>"
115
157
  end
158
+ end
116
159
 
117
- def aicon
118
- "<i class='fa fa-#{self}'></i>"
160
+ module Faye
161
+ class WebSocket
162
+ module API
163
+ def send! msg
164
+ msg = msg.to_json if msg.is_a? Hash
165
+ send msg
166
+ end
167
+ end
119
168
  end
120
169
  end
121
170
 
@@ -125,13 +174,17 @@ class << Sequel
125
174
  def split_keys id
126
175
  id.split(Engine2::SETTINGS[:key_separator])
127
176
  end
177
+
178
+ def join_keys keys
179
+ keys.join(Engine2::SETTINGS[:key_separator])
180
+ end
128
181
  end
129
182
 
130
183
  class Sequel::Database
131
184
  attr_accessor :models, :default_schema
132
185
 
133
186
  def cache_file
134
- "#{Engine2::app}/#{opts[:orig_opts][:name]}.dump"
187
+ "#{Engine2::SETTINGS.path_for(:db_path)}/#{opts[:orig_opts][:name]}.schema_cache"
135
188
  end
136
189
 
137
190
  def load_schema_cache_from_file
@@ -144,7 +197,6 @@ class Sequel::Database
144
197
  end
145
198
  end
146
199
 
147
- Sequel.quote_identifiers = false
148
200
  Sequel.extension :core_extensions
149
201
  Sequel::Inflections.clear
150
202
  Sequel.alias_columns_in_joins = true
@@ -161,6 +213,10 @@ module E2Model
161
213
  module InstanceMethods
162
214
  attr_accessor :skip_save_refresh, :validate_fields
163
215
 
216
+ def key? key
217
+ @values.key? key
218
+ end
219
+
164
220
  def has_primary_key?
165
221
  pk = self.pk
166
222
  pk.is_a?(Array) ? !pk.all?{|k|k.nil?} : !pk.nil?
@@ -236,7 +292,7 @@ module E2Model
236
292
  next if info[:primary_key] && !model.natural_key
237
293
 
238
294
  value = values[name].to_s
239
- value.strip! unless info[:dont_strip]
295
+ value.strip! unless value.frozen? || info[:dont_strip]
240
296
  if value.empty?
241
297
  if req = info[:required]
242
298
  errors.add(name, req[:message]) if !req[:if] || req[:if].(self)
@@ -277,7 +333,7 @@ module E2Model
277
333
 
278
334
  def primary_keys_qualified
279
335
  # cache it ?
280
- primary_keys.map{|k|k.qualify(table_name)}
336
+ primary_keys.map{|k|table_name.q(k)}
281
337
  end
282
338
 
283
339
  def primary_keys_hash id
@@ -290,9 +346,35 @@ module E2Model
290
346
  end
291
347
 
292
348
  module DatasetMethods
349
+ def load *args
350
+ if entry = self[*args]
351
+ model.after_load_processors.each do |name, proc|
352
+ type_info = model.find_type_info(name)
353
+ name_sym = name.to_sym
354
+ proc.(entry, name_sym, type_info) if entry.key?(name_sym)
355
+ end if model.after_load_processors
356
+ entry
357
+ end
358
+ end
359
+
360
+ def load_all
361
+ entries = self.all
362
+ apply_after_load_processors(model, entries) if model.after_load_processors
363
+ entries
364
+ end
365
+
366
+ def apply_after_load_processors model, entries
367
+ model.after_load_processors.each do |name, proc|
368
+ type_info = model.find_type_info(name)
369
+ name_sym = name.to_sym
370
+ entries.each do |entry|
371
+ proc.(entry, name_sym, type_info) if entry.key?(name_sym)
372
+ end
373
+ end
374
+ end
293
375
 
294
376
  def ensure_primary_key
295
- pk = @model.primary_keys
377
+ pk = model.primary_keys
296
378
  raise Engine2::E2Error.new("No primary key defined for model #{model}") unless pk && pk.all?
297
379
 
298
380
  if opts_select = @opts[:select]
@@ -300,7 +382,7 @@ module E2Model
300
382
  opts_select.each do |sel|
301
383
  name = case sel
302
384
  when Symbol
303
- sel.to_s =~ /\w+__(\w+)/ ? $1.to_sym : sel
385
+ sel
304
386
  when Sequel::SQL::QualifiedIdentifier
305
387
  sel.column
306
388
  when Sequel::SQL::AliasedExpression
@@ -314,94 +396,82 @@ module E2Model
314
396
  if pk.length == sel_pk.length
315
397
  self
316
398
  else
317
- sels = (pk - sel_pk).map{|k| k.qualify(@model.table_name)}
399
+ sels = (pk - sel_pk).map{|k| model.table_name.q(k)}
318
400
  select_more(*sels)
319
401
  end
320
402
  else
321
- select(*pk.map{|k| k.qualify(@model.table_name)})
403
+ select(*pk.map{|k| model.table_name.q(k)})
322
404
  end
405
+ end
323
406
 
407
+ def extract_select sel, al = nil, &blk
408
+ case sel
409
+ when Symbol
410
+ yield nil, sel, nil
411
+ when Sequel::SQL::QualifiedIdentifier
412
+ yield sel.table, sel.column, al
413
+ when Sequel::SQL::AliasedExpression, Sequel::SQL::Function
414
+ sel
415
+ # extract_select sel.expression, sel.aliaz, &blk
416
+ # expr = sel.expression
417
+ # yield expr.table, expr.column
418
+ else
419
+ raise Engine2::E2Error.new("Unknown selection #{sel}")
420
+ end
324
421
  end
325
422
 
326
- def setup! fields
423
+ def setup_query fields
327
424
  joins = {}
328
- type_info = model.type_info
329
425
  model_table_name = model.table_name
330
426
 
331
- @opts[:select].map! do |sel|
427
+ select = @opts[:select].map do |sel|
332
428
  extract_select sel do |table, name, aliaz|
333
- if table
429
+ mdl = if table
334
430
  if table == model_table_name
335
- m = model
431
+ model
336
432
  else
337
- a = model.many_to_one_associations[table] # || model.one_to_one_associations[table]
338
- raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless a
339
- m = a.associated_class
433
+ assoc = model.many_to_one_associations[table] || model.many_to_many_associations[table]
434
+ raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless assoc
435
+ assoc.associated_class
340
436
  end
341
- # raise Engine2::E2Error.new("Model not found for table #{table} in model #{model}") unless m
342
- info = m.type_info
343
437
  else
344
- info = type_info
438
+ model
345
439
  end
346
440
 
347
- f_info = info[name]
348
- raise Engine2::E2Error.new("Column #{name} not found for table #{table || model_table_name}") unless f_info
349
-
350
- table ||= model_table_name
351
-
352
- if table == model_table_name
441
+ mdl_table_name = mdl.table_name
442
+ table ||= mdl_table_name
443
+ if mdl_table_name == model_table_name
353
444
  fields << name
354
445
  else
355
- fields << :"#{table}__#{name}"
356
- joins[table] = model.many_to_one_associations[table]
446
+ fields << table.q(name)
447
+ joins[mdl_table_name] ||= model.many_to_one_associations[table] || model.many_to_many_associations[table]
357
448
  end
358
449
 
450
+ f_info = mdl.type_info[name]
451
+ raise Engine2::E2Error.new("Column #{name} not found for table #{table}") unless f_info
359
452
  if f_info[:dummy]
360
453
  nil
361
- # elsif f_info[:type] == :blob_store
362
- # # (~{name => nil}).as :name
363
- # # Sequel.char_length(name).as name
364
- # nil
365
454
  else
366
- if table != model_table_name
367
- if Sequel.alias_columns_in_joins
368
- name.qualify(table).as(:"#{table}__#{name}")
369
- else
370
- name.qualify(table)
371
- end
455
+ qname = mdl_table_name.q(name)
456
+ if table == model_table_name
457
+ qname
372
458
  else
373
- name.qualify(table)
459
+ Sequel.alias_columns_in_joins ? qname.as(:"#{table}__#{name}") : qname
374
460
  end
375
461
  end
376
462
  end
377
- end
378
-
379
- @opts[:select].compact!
463
+ end.compact
380
464
 
381
- joins.reduce(self) do |joined, (table, assoc)|
465
+ joins.reduce(clone(select: select)) do |joined, (table, assoc)|
382
466
  m = assoc.associated_class
383
- keys = assoc[:qualified_key]
384
- joined.left_join(table, m.primary_keys.zip(keys.is_a?(Array) ? keys : [keys]))
385
- end
386
- end
387
-
388
- def extract_select sel, al = nil, &blk
389
- case sel
390
- when Symbol
391
- if sel.to_s =~ /^(\w+)__(\w+?)(?:___(\w+))?$/
392
- yield $1.to_sym, $2.to_sym, $3 ? $3.to_sym : nil
393
- else
394
- yield nil, sel, al
467
+ case assoc[:type]
468
+ when :many_to_one
469
+ keys = assoc[:qualified_key]
470
+ joined.left_join(table, m.primary_keys.zip(keys.is_a?(Array) ? keys : [keys]))
471
+ when :many_to_many
472
+ joined.left_join(assoc[:join_table], assoc[:left_keys].zip(model.primary_keys)).left_join(m.table_name, m.primary_keys.zip(assoc[:right_keys]))
473
+ else unsupported_association
395
474
  end
396
- when Sequel::SQL::QualifiedIdentifier
397
- yield sel.table, sel.column, al
398
- when Sequel::SQL::AliasedExpression, Sequel::SQL::Function
399
- sel
400
- # extract_select sel.expression, sel.aliaz, &blk
401
- # expr = sel.expression
402
- # yield expr.table, expr.column
403
- else
404
- raise Engine2::E2Error.new("Unknown selection #{sel}")
405
475
  end
406
476
  end
407
477
 
@@ -427,16 +497,27 @@ module Sequel
427
497
  @error = error
428
498
  end
429
499
  end
430
-
431
500
  end
432
501
 
433
502
  module Engine2
434
503
  LOCS ||= Hash.new{|h, k| ":#{k}:"}
435
504
  PATH ||= File.expand_path('../..', File.dirname(__FILE__))
436
- SETTINGS ||= {key_separator: '|'}
505
+ SETTINGS ||= {
506
+ key_separator: '|',
507
+ app_path: 'app',
508
+ db_path: 'db',
509
+ model_path: 'models',
510
+ view_path: 'views',
511
+ asset_path: 'assets',
512
+ conf_path: 'conf',
513
+ log_path: 'log'
514
+ }
515
+
516
+ def SETTINGS.path_for path
517
+ "#{self[:app_path]}/#{self[path]}"
518
+ end unless SETTINGS.frozen?
437
519
 
438
520
  class << self
439
- attr_reader :app
440
521
  attr_reader :core_loaded
441
522
 
442
523
  def database name
@@ -457,8 +538,9 @@ module Engine2
457
538
  end
458
539
 
459
540
  def bootstrap_e2db
460
- e2_db_file = (defined? JRUBY_VERSION) ? "jdbc:sqlite:#{@app}/engine2.db" : "sqlite://#{@app}/engine2.db"
461
- const_set :E2DB, connect(e2_db_file, loggers: [Logger.new($stdout)], convert_types: false, name: :engine2)
541
+ e2_db_path = "#{Engine2::SETTINGS.path_for(:db_path)}/engine2.db"
542
+ e2_db_url = (defined? JRUBY_VERSION) ? "jdbc:sqlite:#{e2_db_path}" : "sqlite://#{e2_db_path}"
543
+ const_set :E2DB, connect(e2_db_url, loggers: [Logger.new($stdout)], convert_types: false, name: :engine2)
462
544
  const_set :DUMMYDB, Sequel::Database.new(uri: 'dummy')
463
545
  def DUMMYDB.synchronize *args;end
464
546
  end
@@ -466,35 +548,41 @@ module Engine2
466
548
  def reload
467
549
  @core_loaded = true
468
550
  t = Time.now
469
- Action.count = 0
551
+ ActionNode.count = 0
470
552
  SCHEMES.user.clear
471
553
 
472
554
  Sequel::DATABASES.each do |db|
473
555
  db.models.each{|n, m| Object.send(:remove_const, n) if Object.const_defined?(n)} unless db == E2DB || db == DUMMYDB
474
556
  end
475
557
 
476
- load "#{app}/boot.rb"
558
+ load "#{Engine2::SETTINGS[:app_path]}/boot.rb"
477
559
 
478
560
  Sequel::DATABASES.each &:load_schema_cache_from_file
479
- @model_boot_blk.() if @model_boot_blk
480
561
  load 'engine2/models/Files.rb'
481
562
  load 'engine2/models/UserInfo.rb'
482
- Dir["#{app}/models/*"].each{|m| load m}
563
+ Dir["#{Engine2::SETTINGS.path_for(:model_path)}/*"].each{|m| load m}
564
+ @model_boot_blk.() if @model_boot_blk
483
565
  puts "MODELS: #{Sequel::DATABASES.reduce(0){|s, d|s + d.models.size}}, Time: #{Time.now - t}"
484
- Sequel::DATABASES.each &:dump_schema_cache_to_file
566
+ Sequel::DATABASES.each do |db|
567
+ db.dump_schema_cache_to_file
568
+ db.models.each{|n, m|m.synchronize_type_info}
569
+ end
485
570
 
486
571
  send(:remove_const, :ROOT) if defined? ROOT
487
- const_set(:ROOT, Action.new(nil, :api, RootMeta, {}))
572
+ const_set(:ROOT, ActionNode.new(nil, :api, RootAction, {}))
488
573
 
489
574
  @boot_blk.(ROOT)
490
- ROOT.setup_action_tree
491
- puts "BOOTSTRAP #{app}, Time: #{Time.new - t}"
575
+ ROOT.setup_node_tree
576
+ puts "BOOTSTRAP #{Engine2::SETTINGS[:name]}, Time: #{Time.new - t}"
492
577
  end
493
578
 
494
- def bootstrap app, settings = {}
495
- @app = app
579
+ def bootstrap path, settings = {}
496
580
  SETTINGS.merge! settings
497
- SETTINGS[:name] ||= File::basename(app)
581
+ SETTINGS[:path] = path
582
+ SETTINGS[:name] ||= File::basename(path)
583
+ SETTINGS.freeze
584
+ Handler.set :public_folder, "public"
585
+ Handler.set :views, [SETTINGS.path_for(:view_path), "#{Engine2::PATH}/views"]
498
586
  bootstrap_e2db
499
587
 
500
588
  require 'engine2/pre_bootstrap'