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.
- checksums.yaml +5 -5
- data/Gemfile +0 -0
- data/Rakefile +4 -4
- data/app/{engine2actions.coffee → actions.coffee} +341 -215
- data/app/app.coffee +0 -0
- data/app/app.css +17 -0
- data/app/engine2.coffee +158 -208
- data/app/modal.coffee +138 -0
- data/bower.json +4 -2
- data/conf/message.yaml +5 -0
- data/conf/message_pl.yaml +7 -2
- data/config.coffee +24 -12
- data/engine2.gemspec +8 -8
- data/lib/engine2.rb +12 -10
- data/lib/engine2/action.rb +1338 -133
- data/lib/engine2/action/array.rb +189 -0
- data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
- data/lib/engine2/action/delete.rb +64 -0
- data/lib/engine2/action/form.rb +16 -0
- data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
- data/lib/engine2/action/link.rb +117 -0
- data/lib/engine2/action/list.rb +333 -0
- data/lib/engine2/action/save.rb +28 -0
- data/lib/engine2/action/view.rb +8 -0
- data/lib/engine2/action_node.rb +221 -0
- data/lib/engine2/core.rb +175 -87
- data/lib/engine2/handler.rb +14 -13
- data/lib/engine2/model.rb +85 -43
- data/lib/engine2/models/Files.rb +4 -1
- data/lib/engine2/models/UserInfo.rb +6 -3
- data/lib/engine2/post_bootstrap.rb +4 -3
- data/lib/engine2/pre_bootstrap.rb +10 -6
- data/lib/engine2/scheme.rb +107 -65
- data/lib/engine2/templates.rb +41 -6
- data/lib/engine2/type_info.rb +51 -23
- data/lib/engine2/version.rb +2 -1
- data/package.json +22 -16
- data/public/favicon.ico +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/views/fields/blob.slim +1 -1
- data/views/fields/bs_select.slim +2 -2
- data/views/fields/bsselect_picker.slim +4 -4
- data/views/fields/bsselect_picker_opt.slim +5 -5
- data/views/fields/checkbox.slim +4 -4
- data/views/fields/checkbox_button.slim +6 -0
- data/views/fields/checkbox_buttons.slim +3 -3
- data/views/fields/checkbox_buttons_opt.slim +3 -3
- data/views/fields/currency.slim +2 -2
- data/views/fields/date.slim +4 -4
- data/views/fields/date_range.slim +9 -9
- data/views/fields/date_time.slim +9 -9
- data/views/fields/datetime.slim +8 -8
- data/views/fields/decimal.slim +1 -1
- data/views/fields/decimal_date.slim +3 -3
- data/views/fields/decimal_time.slim +3 -3
- data/views/fields/email.slim +3 -3
- data/views/fields/file_store.slim +11 -11
- data/views/fields/input_text.slim +4 -4
- data/views/fields/integer.slim +1 -1
- data/views/fields/list_bsmselect.slim +20 -0
- data/views/fields/list_bsselect.slim +5 -5
- data/views/fields/list_bsselect_opt.slim +6 -6
- data/views/fields/list_buttons.slim +1 -1
- data/views/fields/list_buttons_opt.slim +2 -2
- data/views/fields/list_mbuttons.slim +9 -0
- data/views/fields/list_mbuttons_opt.slim +11 -0
- data/views/fields/list_mselect.slim +12 -0
- data/views/fields/list_select.slim +4 -4
- data/views/fields/list_select_opt.slim +5 -5
- data/views/fields/password.slim +4 -4
- data/views/fields/radio_checkbox.slim +3 -3
- data/views/fields/scaffold.slim +2 -2
- data/views/fields/scaffold_picker.slim +5 -5
- data/views/fields/select_picker.slim +3 -3
- data/views/fields/select_picker_opt.slim +4 -4
- data/views/fields/text_area.slim +4 -3
- data/views/fields/time.slim +5 -4
- data/views/fields/typeahead_picker.slim +12 -9
- data/views/index.slim +3 -3
- data/views/infra/index.slim +0 -0
- data/views/infra/inspect.slim +41 -10
- data/views/modals/close_m.slim +0 -0
- data/views/modals/confirm_m.slim +0 -0
- data/views/modals/empty_m.slim +0 -0
- data/views/modals/menu_m.slim +1 -1
- data/views/modals/yes_no_m.slim +0 -0
- data/views/panels/menu_m.slim +1 -1
- data/views/scaffold/confirm.slim +0 -0
- data/views/scaffold/fields.slim +6 -4
- data/views/scaffold/form.slim +1 -1
- data/views/scaffold/form_collapse.slim +4 -3
- data/views/scaffold/form_tabs.slim +3 -2
- data/views/scaffold/list.slim +0 -0
- data/views/scaffold/message.slim +0 -0
- data/views/scaffold/search.slim +4 -4
- data/views/scaffold/search_collapse.slim +8 -7
- data/views/scaffold/search_tabs.slim +6 -5
- data/views/scaffold/view.slim +2 -2
- data/views/scaffold/view_collapse.slim +5 -4
- data/views/scaffold/view_tabs.slim +4 -3
- data/views/search_fields/bsmselect_picker.slim +4 -4
- data/views/search_fields/bsselect_picker.slim +4 -4
- data/views/search_fields/checkbox.slim +3 -3
- data/views/search_fields/checkbox2.slim +5 -5
- data/views/search_fields/checkbox_buttons.slim +3 -3
- data/views/search_fields/date.slim +20 -0
- data/views/search_fields/date_range.slim +8 -8
- data/views/search_fields/decimal_date_range.slim +5 -5
- data/views/search_fields/input_text.slim +2 -2
- data/views/search_fields/integer.slim +1 -1
- data/views/search_fields/integer_range.slim +2 -2
- data/views/search_fields/list_bsmselect.slim +4 -4
- data/views/search_fields/list_bsselect.slim +4 -4
- data/views/search_fields/list_buttons.slim +2 -2
- data/views/search_fields/list_mbuttons.slim +12 -0
- data/views/search_fields/list_select.slim +3 -3
- data/views/search_fields/scaffold_picker.slim +2 -2
- data/views/search_fields/select_picker.slim +3 -3
- data/views/search_fields/typeahead_picker.slim +8 -7
- metadata +53 -48
- data/lib/engine2/meta.rb +0 -1216
- data/lib/engine2/meta/array_meta.rb +0 -82
- data/lib/engine2/meta/delete_meta.rb +0 -60
- data/lib/engine2/meta/form_meta.rb +0 -15
- data/lib/engine2/meta/link_meta.rb +0 -134
- data/lib/engine2/meta/list_meta.rb +0 -281
- data/lib/engine2/meta/save_meta.rb +0 -50
- data/lib/engine2/meta/view_meta.rb +0 -7
- data/public/__sinatra__/404.png +0 -0
- data/public/__sinatra__/500.png +0 -0
|
@@ -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
|
data/lib/engine2/core.rb
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
43
|
+
prc = self
|
|
33
44
|
lambda do |obj|
|
|
34
|
-
obj.instance_eval(&
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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::
|
|
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|
|
|
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 =
|
|
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
|
|
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|
|
|
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|
|
|
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
|
|
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
|
|
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
|
-
|
|
431
|
+
model
|
|
336
432
|
else
|
|
337
|
-
|
|
338
|
-
raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless
|
|
339
|
-
|
|
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
|
-
|
|
438
|
+
model
|
|
345
439
|
end
|
|
346
440
|
|
|
347
|
-
|
|
348
|
-
|
|
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 <<
|
|
356
|
-
joins[
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
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(
|
|
465
|
+
joins.reduce(clone(select: select)) do |joined, (table, assoc)|
|
|
382
466
|
m = assoc.associated_class
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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 ||= {
|
|
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
|
-
|
|
461
|
-
|
|
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
|
-
|
|
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 "#{
|
|
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["#{
|
|
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
|
|
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,
|
|
572
|
+
const_set(:ROOT, ActionNode.new(nil, :api, RootAction, {}))
|
|
488
573
|
|
|
489
574
|
@boot_blk.(ROOT)
|
|
490
|
-
ROOT.
|
|
491
|
-
puts "BOOTSTRAP #{
|
|
575
|
+
ROOT.setup_node_tree
|
|
576
|
+
puts "BOOTSTRAP #{Engine2::SETTINGS[:name]}, Time: #{Time.new - t}"
|
|
492
577
|
end
|
|
493
578
|
|
|
494
|
-
def bootstrap
|
|
495
|
-
@app = app
|
|
579
|
+
def bootstrap path, settings = {}
|
|
496
580
|
SETTINGS.merge! settings
|
|
497
|
-
SETTINGS[:
|
|
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'
|