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,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'