engine2 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Rakefile +138 -0
- data/conf/message.yaml +93 -0
- data/conf/message_pl.yaml +93 -0
- data/engine2.gemspec +34 -0
- data/lib/engine2.rb +34 -0
- data/lib/engine2/action.rb +217 -0
- data/lib/engine2/core.rb +572 -0
- data/lib/engine2/handler.rb +134 -0
- data/lib/engine2/meta.rb +969 -0
- data/lib/engine2/meta/decode_meta.rb +110 -0
- data/lib/engine2/meta/delete_meta.rb +73 -0
- data/lib/engine2/meta/form_meta.rb +144 -0
- data/lib/engine2/meta/infra_meta.rb +292 -0
- data/lib/engine2/meta/link_meta.rb +133 -0
- data/lib/engine2/meta/list_meta.rb +284 -0
- data/lib/engine2/meta/save_meta.rb +63 -0
- data/lib/engine2/meta/view_meta.rb +22 -0
- data/lib/engine2/model.rb +390 -0
- data/lib/engine2/models/Files.rb +38 -0
- data/lib/engine2/models/UserInfo.rb +24 -0
- data/lib/engine2/post_bootstrap.rb +83 -0
- data/lib/engine2/pre_bootstrap.rb +27 -0
- data/lib/engine2/scheme.rb +202 -0
- data/lib/engine2/templates.rb +229 -0
- data/lib/engine2/type_info.rb +342 -0
- data/lib/engine2/version.rb +9 -0
- data/public/assets/javascripts.js +13 -0
- data/public/assets/styles.css +4 -0
- data/public/css/angular-motion.css +1022 -0
- data/public/css/angular-ui-tree.min.css +1 -0
- data/public/css/app.css +196 -0
- data/public/css/bootstrap-additions.css +1560 -0
- data/public/css/bootstrap.min.css +11 -0
- data/public/css/font-awesome.min.css +4 -0
- data/public/favicon.ico +0 -0
- data/public/fonts/FontAwesome.otf +0 -0
- data/public/fonts/fontawesome-webfont.eot +0 -0
- data/public/fonts/fontawesome-webfont.svg +655 -0
- data/public/fonts/fontawesome-webfont.ttf +0 -0
- data/public/fonts/fontawesome-webfont.woff +0 -0
- data/public/fonts/fontawesome-webfont.woff2 +0 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +288 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/public/images/file.png +0 -0
- data/public/images/folder-closed.png +0 -0
- data/public/images/folder.png +0 -0
- data/public/images/node-closed-2.png +0 -0
- data/public/images/node-closed-light.png +0 -0
- data/public/images/node-closed.png +0 -0
- data/public/images/node-opened-2.png +0 -0
- data/public/images/node-opened-light.png +0 -0
- data/public/images/node-opened.png +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader-light.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/public/js/angular-animate.js +4115 -0
- data/public/js/angular-cookies.js +322 -0
- data/public/js/angular-local-storage.js +455 -0
- data/public/js/angular-route.js +1022 -0
- data/public/js/angular-sanitize.js +717 -0
- data/public/js/angular-strap.js +4339 -0
- data/public/js/angular-strap.tpl.js +43 -0
- data/public/js/angular-ui-tree.js +1569 -0
- data/public/js/angular.js +30714 -0
- data/public/js/i18n/angular-locale_pl.js +115 -0
- data/public/js/lodash.custom.min.js +97 -0
- data/public/js/ng-file-upload-shim.min.js +2 -0
- data/public/js/ng-file-upload.min.js +3 -0
- data/views/app.coffee +3 -0
- data/views/engine2.coffee +557 -0
- data/views/engine2actions.coffee +849 -0
- data/views/engine2templates.coffee +0 -0
- data/views/fields/blob.slim +22 -0
- data/views/fields/bs_select.slim +10 -0
- data/views/fields/bsselect_picker.slim +18 -0
- data/views/fields/bsselect_picker_opt.slim +22 -0
- data/views/fields/checkbox.slim +11 -0
- data/views/fields/checkbox_buttons.slim +6 -0
- data/views/fields/checkbox_buttons_opt.slim +8 -0
- data/views/fields/currency.slim +10 -0
- data/views/fields/date.slim +21 -0
- data/views/fields/date_range.slim +44 -0
- data/views/fields/date_time.slim +42 -0
- data/views/fields/datetime.slim +42 -0
- data/views/fields/decimal.slim +11 -0
- data/views/fields/decimal_date.slim +22 -0
- data/views/fields/decimal_time.slim +26 -0
- data/views/fields/email.slim +13 -0
- data/views/fields/file_store.slim +61 -0
- data/views/fields/input_text.slim +14 -0
- data/views/fields/integer.slim +11 -0
- data/views/fields/list_bsselect.slim +18 -0
- data/views/fields/list_bsselect_opt.slim +21 -0
- data/views/fields/list_buttons.slim +3 -0
- data/views/fields/list_buttons_opt.slim +5 -0
- data/views/fields/list_select.slim +11 -0
- data/views/fields/list_select_opt.slim +15 -0
- data/views/fields/password.slim +14 -0
- data/views/fields/radio_checkbox.slim +10 -0
- data/views/fields/scaffold.slim +2 -0
- data/views/fields/scaffold_picker.slim +20 -0
- data/views/fields/select_picker.slim +12 -0
- data/views/fields/select_picker_opt.slim +16 -0
- data/views/fields/text_area.slim +10 -0
- data/views/fields/time.slim +22 -0
- data/views/fields/typeahead_picker.slim +25 -0
- data/views/index.slim +44 -0
- data/views/infra/index.slim +5 -0
- data/views/infra/inspect.slim +81 -0
- data/views/modals/close_m.slim +15 -0
- data/views/modals/confirm_m.slim +19 -0
- data/views/modals/empty_m.slim +12 -0
- data/views/modals/menu_m.slim +13 -0
- data/views/modals/yes_no_m.slim +19 -0
- data/views/panels/menu_m.slim +9 -0
- data/views/scaffold/confirm.slim +3 -0
- data/views/scaffold/fields.slim +10 -0
- data/views/scaffold/form.slim +11 -0
- data/views/scaffold/list.slim +42 -0
- data/views/scaffold/message.slim +3 -0
- data/views/scaffold/search.slim +20 -0
- data/views/scaffold/view.slim +18 -0
- data/views/search_fields/bsmselect_picker.slim +25 -0
- data/views/search_fields/bsselect_picker.slim +24 -0
- data/views/search_fields/checkbox.slim +11 -0
- data/views/search_fields/checkbox2.slim +14 -0
- data/views/search_fields/checkbox_buttons.slim +10 -0
- data/views/search_fields/date_range.slim +46 -0
- data/views/search_fields/decimal_date_range.slim +47 -0
- data/views/search_fields/input_text.slim +18 -0
- data/views/search_fields/integer.slim +18 -0
- data/views/search_fields/integer_range.slim +27 -0
- data/views/search_fields/list_bsmselect.slim +24 -0
- data/views/search_fields/list_bsselect.slim +22 -0
- data/views/search_fields/list_buttons.slim +8 -0
- data/views/search_fields/list_select.slim +17 -0
- data/views/search_fields/scaffold_picker.slim +19 -0
- data/views/search_fields/select_picker.slim +17 -0
- data/views/search_fields/typeahead_picker.slim +25 -0
- metadata +327 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Engine2
|
4
|
+
class ViewMeta < Meta
|
5
|
+
meta_type :view
|
6
|
+
include MetaViewSupport, MetaQuerySupport
|
7
|
+
|
8
|
+
def record handler, record
|
9
|
+
end
|
10
|
+
|
11
|
+
def invoke handler
|
12
|
+
handler.permit id = handler.params[:id]
|
13
|
+
record = get_query[assets[:model].primary_keys_hash_qualified(split_keys(id))]
|
14
|
+
if record
|
15
|
+
static.record(handler, record)
|
16
|
+
{record: record}
|
17
|
+
else
|
18
|
+
handler.halt_not_found LOCS[:no_entry]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,390 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Engine2
|
4
|
+
module Model
|
5
|
+
attr_reader :dummies
|
6
|
+
attr_reader :many_to_one_associations, :one_to_many_associations, :many_to_many_associations #, :one_to_one_associations
|
7
|
+
attr_reader :before_save_processors, :after_save_processors, :before_destroy_processors, :after_destroy_processors
|
8
|
+
attr_reader :validation_in_transaction
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def extended cls
|
12
|
+
# cls.dataset.row_proc = nil
|
13
|
+
models = cls.db.models ||= {}
|
14
|
+
raise E2Error.new("Model '#{cls.name}' already defined") if models[cls.name.to_sym]
|
15
|
+
models[cls.name.to_sym] = cls
|
16
|
+
|
17
|
+
cls.instance_eval do
|
18
|
+
@many_to_one_associations = association_reflections.select{|n, a| a[:type] == :many_to_one}
|
19
|
+
@one_to_many_associations = association_reflections.select{|n, a| a[:type] == :one_to_many}
|
20
|
+
@many_to_many_associations = association_reflections.select{|n, a| a[:type] == :many_to_many}
|
21
|
+
# @one_to_one_associations = association_reflections.select{|n, a| a[:type] == :one_to_one}
|
22
|
+
@validation_in_transaction = nil
|
23
|
+
@before_save_processors = nil
|
24
|
+
@after_save_processors = nil
|
25
|
+
@around_save_processors = nil
|
26
|
+
@before_destroy_processors = nil
|
27
|
+
@after_destroy_processors = nil
|
28
|
+
@type_info_synchronized = nil
|
29
|
+
end
|
30
|
+
cls.setup_schema
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def install_processors processors
|
35
|
+
hash = {}
|
36
|
+
type_info.each_pair do |name, info|
|
37
|
+
proc = processors[info[:type]]
|
38
|
+
hash[name] = proc if proc
|
39
|
+
end
|
40
|
+
hash.empty? ? nil : hash
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup_schema
|
44
|
+
@type_info = {}
|
45
|
+
@dummies = []
|
46
|
+
|
47
|
+
type_info do
|
48
|
+
schema = @model.db_schema
|
49
|
+
schema.each_pair do |name, db_info|
|
50
|
+
@info[name] = {otype: db_info[:type]}
|
51
|
+
|
52
|
+
case db_info[:type]
|
53
|
+
when :integer
|
54
|
+
integer_field name
|
55
|
+
when :string
|
56
|
+
if db_info[:db_type] == 'text'
|
57
|
+
string_field name, 10
|
58
|
+
else
|
59
|
+
string_field name, Integer(db_info[:column_size] || db_info[:db_type][/\((\d+)\)/, 1])
|
60
|
+
end
|
61
|
+
|
62
|
+
when :time
|
63
|
+
time_field name, LOCS[:default_time_format], LOCS[:default_time_model_format]
|
64
|
+
when :date
|
65
|
+
date_field name, LOCS[:default_date_format], LOCS[:default_date_model_format]
|
66
|
+
when :datetime
|
67
|
+
datetime_field name, LOCS[:default_date_format], LOCS[:default_time_format], LOCS[:default_date_model_format], LOCS[:default_time_model_format]
|
68
|
+
when :decimal
|
69
|
+
size, scale = db_info[:column_size], db_info[:scale]
|
70
|
+
unless size && scale
|
71
|
+
db_info[:db_type] =~ /decimal\((\d+),(\d+)\)/i
|
72
|
+
size, scale = $1.to_i, $2.to_i
|
73
|
+
raise E2Error.new("Cannot parse decimal type for #{db_info}") unless size || scale
|
74
|
+
end
|
75
|
+
decimal_field name, size, scale
|
76
|
+
when :blob
|
77
|
+
blob_field name, 100000
|
78
|
+
else
|
79
|
+
p db_info
|
80
|
+
raise E2Error.new("Unknown column type: #{db_info[:type].inspect} for #{name}")
|
81
|
+
end
|
82
|
+
|
83
|
+
required name if !db_info[:allow_null]
|
84
|
+
primary_key name if db_info[:primary_key]
|
85
|
+
sequence name, "SEQ_#{@model.table_name}.nextVal" if db_info[:primary_key] && !db_info[:allow_null] && !db_info[:auto_increment] && !@model.natural_key
|
86
|
+
default name, db_info[:ruby_default] if db_info[:ruby_default]
|
87
|
+
end
|
88
|
+
|
89
|
+
unique *@model.primary_keys if @model.natural_key && @model.db.adapter_scheme # uri ?
|
90
|
+
|
91
|
+
@model.many_to_one_associations.each do |aname, assoc|
|
92
|
+
many_to_one_field aname
|
93
|
+
decode assoc[:keys].first
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def type_info &blk
|
99
|
+
if blk
|
100
|
+
raise E2Error.new("type_info already called for model #{self}") if @type_info_synchronized
|
101
|
+
TypeInfo.new(self).instance_eval(&blk)
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
@type_info
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def synchronize_type_info
|
109
|
+
resolve_dependencies
|
110
|
+
@before_save_processors = install_processors(BeforeSaveProcessors)
|
111
|
+
@after_save_processors = install_processors(AfterSaveProcessors)
|
112
|
+
@around_save_processors = {}
|
113
|
+
@before_destroy_processors = install_processors(BeforeDestroyProcessors)
|
114
|
+
@after_destroy_processors = install_processors(AfterDestroyProcessors)
|
115
|
+
@type_info_synchronized = true
|
116
|
+
end
|
117
|
+
|
118
|
+
def resolve_dependencies
|
119
|
+
resolved = {}
|
120
|
+
@type_info.each_pair do |name, info|
|
121
|
+
@validation_in_transaction ||= info[:transaction]
|
122
|
+
resolve_dependency(name, resolved)
|
123
|
+
end
|
124
|
+
@type_info = resolved
|
125
|
+
end
|
126
|
+
|
127
|
+
def resolve_dependency name, resolved, seen = []
|
128
|
+
seen << name
|
129
|
+
deps = @type_info[name][:depends]
|
130
|
+
deps.each do |e|
|
131
|
+
if !resolved[e]
|
132
|
+
raise "Circular dependency for field '#{name}' in model '#{self}'" if seen.include?(e)
|
133
|
+
resolve_dependency(e, resolved, seen)
|
134
|
+
end
|
135
|
+
end if deps
|
136
|
+
resolved[name] = @type_info[name]
|
137
|
+
end
|
138
|
+
|
139
|
+
attr_reader :scheme_name, :scheme_args
|
140
|
+
|
141
|
+
def scheme s_name = :default, opts = nil, &blk
|
142
|
+
@scheme_name = s_name
|
143
|
+
@scheme_args = [name.to_sym, self, opts]
|
144
|
+
SCHEMES::define_scheme name.to_sym, &blk
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# def define_dummy_model
|
150
|
+
# end
|
151
|
+
|
152
|
+
module MemoryModel
|
153
|
+
def self.extended cls
|
154
|
+
cls.extend Engine2::Model
|
155
|
+
cls.class_eval do
|
156
|
+
def save
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def cls.type_info &blk
|
161
|
+
if blk
|
162
|
+
super(&blk)
|
163
|
+
@columns = @type_info.keys
|
164
|
+
nil
|
165
|
+
else
|
166
|
+
@type_info
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
(Validations ||= {}).merge!(
|
174
|
+
boolean: lambda{|record, field, info|
|
175
|
+
value = record.values[field]
|
176
|
+
LOCS[:wrong_boolean_value] if value != info[:true_value] && value != info[:false_value]
|
177
|
+
},
|
178
|
+
string_length: lambda{|record, field, info|
|
179
|
+
value = record.values[field]
|
180
|
+
LOCS[:value_exceeds_maximum_length] if value.to_s.length > info[:length]
|
181
|
+
},
|
182
|
+
date: lambda{|record, field, info|
|
183
|
+
value = record.values[field]
|
184
|
+
begin
|
185
|
+
Sequel.string_to_date(value.to_s)
|
186
|
+
nil
|
187
|
+
end rescue LOCS[:invalid_date_format]
|
188
|
+
},
|
189
|
+
time: lambda{|record, field, info|
|
190
|
+
value = record.values[field]
|
191
|
+
begin
|
192
|
+
Sequel.string_to_time(value)
|
193
|
+
nil
|
194
|
+
end rescue LOCS[:invalid_time_format] unless value.is_a? Integer
|
195
|
+
},
|
196
|
+
decimal_date: lambda{|record, field, info|
|
197
|
+
value = record.values[field].to_s
|
198
|
+
if value == '0' && info[:required]
|
199
|
+
info[:required][:message]
|
200
|
+
else
|
201
|
+
Validations[:date].(record, field, info)
|
202
|
+
end
|
203
|
+
},
|
204
|
+
decimal_time: lambda{|record, field, info|
|
205
|
+
value = record.values[field].to_s
|
206
|
+
|
207
|
+
if value == '0' && info[:required]
|
208
|
+
info[:required][:message]
|
209
|
+
else
|
210
|
+
LOCS[:invalid_time_format] unless value.rjust(6, '0') =~ info[:model_regexp]
|
211
|
+
end
|
212
|
+
|
213
|
+
# value = record.values[field]
|
214
|
+
# begin
|
215
|
+
# Sequel.string_to_time("010101 #{value}")
|
216
|
+
# nil
|
217
|
+
# end rescue LOCS[:invalid_time_format]
|
218
|
+
},
|
219
|
+
|
220
|
+
datetime: lambda{|record, field, info|
|
221
|
+
begin
|
222
|
+
Sequel.string_to_datetime(record.values[field])
|
223
|
+
nil
|
224
|
+
end rescue LOCS[:invalid_datetime_format]
|
225
|
+
},
|
226
|
+
date_range: lambda{|record, field, info|
|
227
|
+
to_errors = record.errors[info[:other_date]]
|
228
|
+
if to_errors
|
229
|
+
record.errors.add(field, *to_errors)
|
230
|
+
nil
|
231
|
+
else
|
232
|
+
from = record.values[field].to_s
|
233
|
+
to = record.values[info[:other_date]].to_s
|
234
|
+
LOCS[:value_from_gt_to] if Sequel.string_to_date(from) > Sequel.string_to_date(to)
|
235
|
+
end
|
236
|
+
},
|
237
|
+
date_time: lambda{|record, field, info|
|
238
|
+
to_errors = record.errors[info[:other_time]]
|
239
|
+
if to_errors
|
240
|
+
record.errors.add(field, *to_errors)
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
},
|
244
|
+
format: lambda{|record, field, info|
|
245
|
+
value = record.values[field]
|
246
|
+
args = info[:validations][:format]
|
247
|
+
args[:message] if value !~ args[:pattern]
|
248
|
+
},
|
249
|
+
integer: lambda{|record, field, info|
|
250
|
+
value = record.values[field]
|
251
|
+
LOCS[:invalid_number_value] unless value.is_a?(Integer) || value.to_s =~ /^\-?\d+$/
|
252
|
+
},
|
253
|
+
positive_integer: lambda{|record, field, info|
|
254
|
+
LOCS[:number_negative] if record.values[field] < 0
|
255
|
+
},
|
256
|
+
list_select: lambda{|record, field, info|
|
257
|
+
value = record.values[field]
|
258
|
+
LOCS[:invalid_list_value] unless info[:list].any?{|a|a.first == value}
|
259
|
+
},
|
260
|
+
decimal: lambda{|record, field, info|
|
261
|
+
value = record.values[field]
|
262
|
+
LOCS[:invalid_decimal_value] unless value.to_s =~ info[:validations][:decimal][:regexp]
|
263
|
+
},
|
264
|
+
currency: lambda{|record, field, info|
|
265
|
+
value = record.values[field]
|
266
|
+
LOCS[:invalid_currency_value] unless value.to_s =~ /^\d+(?:\.\d{,2})?$/
|
267
|
+
},
|
268
|
+
unique: lambda{|record, field, info|
|
269
|
+
with = info[:validations][:unique][:with]
|
270
|
+
with_errors = with.map{|w|record.errors[w]}
|
271
|
+
if with_errors.compact.empty?
|
272
|
+
all_fields = [field] + with
|
273
|
+
query = record.model.dataset.where(*all_fields.map{|f|{f => record[f]}})
|
274
|
+
query = query.exclude(record.model.primary_keys_hash(record.primary_key_values)) unless record.new?
|
275
|
+
unless query.empty?
|
276
|
+
msg = LOCS[:required_unique_value]
|
277
|
+
with.each{|w| record.errors.add(w, msg)}
|
278
|
+
msg
|
279
|
+
end
|
280
|
+
else
|
281
|
+
nil
|
282
|
+
end
|
283
|
+
}
|
284
|
+
)
|
285
|
+
|
286
|
+
(BeforeSaveProcessors ||= {}).merge!(
|
287
|
+
blob_store: lambda{|record, field, info|
|
288
|
+
if value = record.values[field] # attachment info
|
289
|
+
record.values[info[:name_field]] = value[:name]
|
290
|
+
record.values[info[:mime_field]] = value[:mime]
|
291
|
+
end
|
292
|
+
},
|
293
|
+
foreign_blob_store: lambda{|record, field, info|
|
294
|
+
if value = record.values[field] # attachment info
|
295
|
+
assoc = record.model.association_reflections[info[:assoc_name]]
|
296
|
+
blob_model = Object.const_get(assoc[:class_name])
|
297
|
+
file_fields = {info[:bytes_field] => :$data, info[:name_field] => :$name_field, info[:mime_field] => :$mime_field}
|
298
|
+
upload = info[:store][:upload]
|
299
|
+
file_data = {data: Sequel.blob(open("#{upload}/#{value[:rackname]}", "rb"){|f|f.read}), name_field: value[:name], mime_field: value[:mime]}
|
300
|
+
|
301
|
+
if record.new?
|
302
|
+
statement = blob_model.dataset.prepare(:insert, :insert_blob, file_fields)
|
303
|
+
id = statement.call(file_data)
|
304
|
+
record.values[assoc[:key]] = id
|
305
|
+
else
|
306
|
+
key = record.model.naked.select(assoc[:key]).where(record.model.primary_keys_hash(record.primary_key_values)).first
|
307
|
+
statement = blob_model.dataset.where(blob_model.primary_key => :$id_field).prepare(:update, :update_blob, file_fields)
|
308
|
+
statement.call(file_data.merge(id_field: key[assoc[:key]]))
|
309
|
+
end
|
310
|
+
File.delete("#{upload}/#{value[:rackname]}")
|
311
|
+
end
|
312
|
+
}
|
313
|
+
)
|
314
|
+
|
315
|
+
(AfterSaveProcessors ||= {}).merge!(
|
316
|
+
star_to_many_field: lambda{|record, field, info|
|
317
|
+
value = record.values[field]
|
318
|
+
if value && value.is_a?(Hash)
|
319
|
+
assoc = record.model.association_reflections[info[:assoc_name]]
|
320
|
+
other_model = Object.const_get(assoc[:class_name])
|
321
|
+
unlinked = value[:unlinked]
|
322
|
+
linked = value[:linked]
|
323
|
+
parent_key = record.primary_key_values
|
324
|
+
case assoc[:type]
|
325
|
+
when :one_to_many
|
326
|
+
StarToManyUnlinkMetaBase.one_to_many_unlink_db(other_model, assoc, unlinked) if unlinked
|
327
|
+
StarToManyLinkMeta.one_to_many_link_db(other_model, assoc, parent_key, linked) if linked
|
328
|
+
when :many_to_many
|
329
|
+
StarToManyUnlinkMetaBase.many_to_many_unlink_db(other_model, assoc, parent_key, unlinked) if unlinked
|
330
|
+
StarToManyLinkMeta.many_to_many_link_db(other_model, assoc, parent_key, linked) if linked
|
331
|
+
else unsupported_association
|
332
|
+
end
|
333
|
+
end
|
334
|
+
},
|
335
|
+
file_store: lambda{|m, v, info|
|
336
|
+
value = m.values[v]
|
337
|
+
files = E2Files.db[:files]
|
338
|
+
owner = m.primary_key_values.join('|')
|
339
|
+
upload = info[:store][:upload]
|
340
|
+
files_dir = info[:store][:files]
|
341
|
+
value.each do |entry|
|
342
|
+
name = entry[:name]
|
343
|
+
if (rackname = entry[:rackname])
|
344
|
+
unless entry[:deleted]
|
345
|
+
file_id = files.insert(name: name, mime: entry[:mime], owner: owner, model: m.model.name, field: v.to_s, uploaded: Sequel.datetime_class.now)
|
346
|
+
File.rename("#{upload}/#{rackname}", "#{files_dir}/#{name}_#{file_id}")
|
347
|
+
end
|
348
|
+
elsif entry[:deleted]
|
349
|
+
File.delete("#{files_dir}/#{name}_#{entry[:id]}")
|
350
|
+
files.where(id: entry[:id]).delete #, model: m.model.table_name.to_s, field: v.to_s
|
351
|
+
end
|
352
|
+
end if value # .is_a?(Array)
|
353
|
+
},
|
354
|
+
blob_store: lambda{|record, field, info|
|
355
|
+
if value = record.values[field] # attachment info
|
356
|
+
upload = info[:store][:upload]
|
357
|
+
id = record.model.primary_keys_hash(record.primary_key_values)
|
358
|
+
id_n = Hash[record.model.primary_keys.map{|k| [k, :"$#{k}"]}]
|
359
|
+
statement = record.model.dataset.where(id_n).prepare(:update, :update_blob, info[:bytes_field] => :$data)
|
360
|
+
statement.call(id.merge(data: Sequel.blob(open("#{upload}/#{value[:rackname]}", "rb"){|f|f.read})))
|
361
|
+
# record.model.where(id).update(info[:field] => Sequel.blob(open("#{upload}/#{value[:rackname]}", "rb"){|f|f.read}))
|
362
|
+
File.delete("#{upload}/#{value[:rackname]}")
|
363
|
+
end
|
364
|
+
}
|
365
|
+
)
|
366
|
+
|
367
|
+
(BeforeDestroyProcessors ||= {}).merge!(
|
368
|
+
foreign_blob_store: lambda{|record, field, info|
|
369
|
+
assoc = record.model.association_reflections[info[:assoc_name]]
|
370
|
+
key = record.model.naked.select(assoc[:key]).where(record.model.primary_keys_hash(record.primary_key_values)).first
|
371
|
+
if key
|
372
|
+
blob_model = Object.const_get(assoc[:class_name])
|
373
|
+
blob_model.where(blob_model.primary_key => key[assoc[:key]]).delete
|
374
|
+
end
|
375
|
+
}
|
376
|
+
)
|
377
|
+
|
378
|
+
(AfterDestroyProcessors ||= {}).merge!(
|
379
|
+
file_store: lambda{|m, v, info|
|
380
|
+
files = E2Files.db[:files]
|
381
|
+
files_dir = info[:store][:files]
|
382
|
+
owner = m.primary_key_values.join('|')
|
383
|
+
files.select(:id, :name).where(owner: owner, model: m.model.name, field: v.to_s).all.each do |entry|
|
384
|
+
File.delete("#{files_dir}/#{entry[:name]}_#{entry[:id]}")
|
385
|
+
end
|
386
|
+
files.where(owner: owner, model: m.model.name, field: v.to_s).delete
|
387
|
+
}
|
388
|
+
)
|
389
|
+
|
390
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Engine2
|
4
|
+
E2DB.create_table :files do
|
5
|
+
primary_key :id
|
6
|
+
String :name, size: 100, null: false
|
7
|
+
String :mime, fixed: true, size: 40, null: false
|
8
|
+
String :owner, fixed: true, size: 20, null: false
|
9
|
+
String :model, fixed: true, size: 20, null: false
|
10
|
+
String :field, fixed: true, size: 20, null: false
|
11
|
+
DateTime :uploaded, null: false
|
12
|
+
end unless E2DB.table_exists?(:files)
|
13
|
+
|
14
|
+
class E2Files < Sequel::Model(E2DB[:files])
|
15
|
+
extend Engine2::Model
|
16
|
+
|
17
|
+
type_info do
|
18
|
+
# list_select :model, Hash[@model.db.models.keys.map{|m| [m, m]}]
|
19
|
+
# list_select :field, {}
|
20
|
+
end
|
21
|
+
|
22
|
+
scheme :default, Schemes::CRUD.merge(create: false, bulk_delete: true) do
|
23
|
+
self.* do
|
24
|
+
hide_pk
|
25
|
+
query select(:name, :mime, :owner, :model, :field, :uploaded)
|
26
|
+
sortable
|
27
|
+
searchable :name, :owner, :model, :field
|
28
|
+
search_live
|
29
|
+
|
30
|
+
on_change :model do |req, value|
|
31
|
+
# action.parent.*.assets[:model].select(:field).where(model: value).all.map{|rec|f = rec.values[:field]; [f, f]}
|
32
|
+
# render :field, list: {a: 1, b: 2}.to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|