engine2 1.0.1 → 1.0.2
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 +4 -4
- data/lib/engine2.rb +0 -2
- data/lib/engine2/action.rb +14 -13
- data/lib/engine2/core.rb +68 -57
- data/lib/engine2/handler.rb +4 -1
- data/lib/engine2/meta.rb +58 -11
- data/lib/engine2/meta/delete_meta.rb +2 -15
- data/lib/engine2/meta/infra_meta.rb +1 -2
- data/lib/engine2/meta/link_meta.rb +1 -0
- data/lib/engine2/meta/list_meta.rb +4 -3
- data/lib/engine2/meta/save_meta.rb +2 -15
- data/lib/engine2/meta/view_meta.rb +0 -1
- data/lib/engine2/model.rb +32 -22
- data/lib/engine2/scheme.rb +7 -15
- data/lib/engine2/type_info.rb +10 -13
- data/lib/engine2/version.rb +1 -1
- data/views/engine2.coffee +11 -7
- data/views/engine2actions.coffee +4 -1
- data/views/infra/inspect.slim +1 -1
- data/views/modals/close_m.slim +1 -1
- data/views/modals/confirm_m.slim +1 -1
- data/views/modals/empty_m.slim +1 -1
- data/views/modals/menu_m.slim +1 -1
- data/views/modals/yes_no_m.slim +1 -1
- data/views/panels/menu_m.slim +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a2ad0107737d0e5096b9a72b31743f01b8107ba
|
4
|
+
data.tar.gz: 6557952f0c927307bb297d71afc2cbb8086d3318
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 247de7a7bd6535a169b713cd08d1c9034d4e9de87301530986d9eae7b4ef98b4ceb9bff5610daf7ee66cc168c7f110dd585dd7db5691e07112f372c3345792d4
|
7
|
+
data.tar.gz: b41f9ea4e158dd174b4f71566f93d4fcc0f5bff6270f3885cc5f13e52b9e7fae0ba2f696273611453222bec33aed9f34556cd5a63c46f3a3f3dc8787e4ea7917
|
data/lib/engine2.rb
CHANGED
data/lib/engine2/action.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
module Engine2
|
4
|
+
|
4
5
|
class Action < BasicObject
|
5
6
|
ACCESS_FORBIDDEN ||= ->h{false}
|
6
7
|
attr_reader :parent, :name, :number, :actions, :recheck_access
|
7
|
-
attr_reader :meta_proc
|
8
|
+
attr_reader :meta_proc
|
8
9
|
|
9
10
|
class << self
|
10
11
|
attr_accessor :count
|
@@ -20,17 +21,7 @@ module Engine2
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def * &blk
|
23
|
-
if blk
|
24
|
-
@meta_proc = if meta_proc = @meta_proc
|
25
|
-
@meta_proc_chained = true
|
26
|
-
::Kernel::lambda do |obj|
|
27
|
-
obj.instance_eval(&meta_proc)
|
28
|
-
obj.instance_eval(&blk)
|
29
|
-
end
|
30
|
-
else
|
31
|
-
blk
|
32
|
-
end
|
33
|
-
end
|
24
|
+
@meta_proc = @meta_proc ? @meta_proc.chain(&blk) : blk if blk
|
34
25
|
@meta
|
35
26
|
end
|
36
27
|
|
@@ -74,6 +65,12 @@ module Engine2
|
|
74
65
|
end
|
75
66
|
end
|
76
67
|
|
68
|
+
def define_action_invoke name, meta_class = DummyMeta, assets = {}, &blk
|
69
|
+
define_action name, meta_class, assets do
|
70
|
+
self.*.define_singleton_method(:invoke, &blk)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
77
74
|
def undefine_action name
|
78
75
|
::Kernel.raise E2Error.new("No action #{name} defined") unless @actions[name]
|
79
76
|
@actions.delete(name)
|
@@ -163,7 +160,7 @@ module Engine2
|
|
163
160
|
model_name = model.name.to_sym
|
164
161
|
model.synchronize_type_info
|
165
162
|
model_actions[model_name] = action.to_a_rec{|a| !a.*.assets[:assoc]}
|
166
|
-
action.run_scheme(model_name) if SCHEMES
|
163
|
+
action.run_scheme(model_name) if SCHEMES[model_name, false]
|
167
164
|
false
|
168
165
|
else
|
169
166
|
true
|
@@ -216,6 +213,10 @@ module Engine2
|
|
216
213
|
end
|
217
214
|
::Kernel::puts "VERIFY #{::Time.now - t}"
|
218
215
|
end
|
216
|
+
|
217
|
+
def p *args
|
218
|
+
::Kernel::p *args
|
219
|
+
end
|
219
220
|
end
|
220
221
|
|
221
222
|
|
data/lib/engine2/core.rb
CHANGED
@@ -27,6 +27,14 @@ class Proc
|
|
27
27
|
loc = source_location
|
28
28
|
"\"#<Proc:#{loc.first[/\w+.rb/]}:#{loc.last}>\""
|
29
29
|
end
|
30
|
+
|
31
|
+
def chain &blk
|
32
|
+
proc = self
|
33
|
+
lambda do |obj|
|
34
|
+
obj.instance_eval(&proc)
|
35
|
+
obj.instance_eval(&blk)
|
36
|
+
end
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
class Hash
|
@@ -123,7 +131,7 @@ class Sequel::Database
|
|
123
131
|
attr_accessor :models, :default_schema
|
124
132
|
|
125
133
|
def cache_file
|
126
|
-
"#{
|
134
|
+
"#{Engine2::app}/#{opts[:orig_opts][:name]}.dump"
|
127
135
|
end
|
128
136
|
|
129
137
|
def load_schema_cache_from_file
|
@@ -236,7 +244,7 @@ module E2Model
|
|
236
244
|
else
|
237
245
|
info[:validations].each_pair do |validation, args|
|
238
246
|
validation_proc = Engine2::Validations[validation] || args[:lambda] # swap ?
|
239
|
-
raise "Validation not found for field '#{name}' of type #{validation}" unless validation_proc
|
247
|
+
raise E2Error.new("Validation not found for field '#{name}' of type #{validation}") unless validation_proc
|
240
248
|
if result = validation_proc.(self, name, info)
|
241
249
|
errors.add(name, result)
|
242
250
|
break
|
@@ -345,11 +353,7 @@ module E2Model
|
|
345
353
|
fields << name
|
346
354
|
else
|
347
355
|
fields << :"#{table}__#{name}"
|
348
|
-
|
349
|
-
unless assoc
|
350
|
-
# fail
|
351
|
-
end
|
352
|
-
joins[table] = assoc
|
356
|
+
joins[table] = model.many_to_one_associations[table]
|
353
357
|
end
|
354
358
|
|
355
359
|
if f_info[:dummy]
|
@@ -375,11 +379,9 @@ module E2Model
|
|
375
379
|
@opts[:select].compact!
|
376
380
|
|
377
381
|
joins.reduce(self) do |joined, (table, assoc)|
|
378
|
-
assoc = model.many_to_one_associations[table] # || model.one_to_one_associations[table]
|
379
382
|
m = Object.const_get(assoc[:class_name])
|
380
383
|
keys = assoc[:qualified_key]
|
381
|
-
|
382
|
-
joined.left_join(table, m.primary_keys.zip(keys))
|
384
|
+
joined.left_join(table, m.primary_keys.zip(keys.is_a?(Array) ? keys : [keys]))
|
383
385
|
end
|
384
386
|
end
|
385
387
|
|
@@ -433,65 +435,74 @@ module Engine2
|
|
433
435
|
PATH ||= File.expand_path('../..', File.dirname(__FILE__))
|
434
436
|
|
435
437
|
class << self
|
436
|
-
|
437
|
-
|
438
|
+
attr_reader :app, :reloading
|
439
|
+
attr_reader :core_loaded
|
438
440
|
|
439
|
-
|
441
|
+
def database name
|
442
|
+
Object.const_set(name, yield) unless Object.const_defined?(name)
|
443
|
+
end
|
440
444
|
|
441
|
-
|
442
|
-
|
443
|
-
|
445
|
+
def connect *args
|
446
|
+
db = Sequel.connect *args
|
447
|
+
db
|
448
|
+
end
|
444
449
|
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
db
|
449
|
-
end
|
450
|
+
def boot &blk
|
451
|
+
@boot_blk = blk
|
452
|
+
end
|
450
453
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
def DUMMYDB.synchronize *args;end
|
454
|
+
def model_boot &blk
|
455
|
+
@model_boot_blk = blk
|
456
|
+
end
|
455
457
|
|
456
|
-
|
457
|
-
|
458
|
-
|
458
|
+
def bootstrap_e2db
|
459
|
+
e2_db_file = (defined? JRUBY_VERSION) ? "jdbc:sqlite:#{@app}/engine2.db" : "sqlite://#{@app}/engine2.db"
|
460
|
+
Engine2.const_set :E2DB, connect(e2_db_file, loggers: [Logger.new($stdout)], convert_types: false, name: :engine2)
|
461
|
+
Engine2.const_set :DUMMYDB, Sequel::Database.new(uri: 'dummy')
|
462
|
+
def DUMMYDB.synchronize *args;end
|
463
|
+
end
|
459
464
|
|
460
|
-
|
461
|
-
|
462
|
-
|
465
|
+
def reload
|
466
|
+
@core_loaded = true
|
467
|
+
t = Time.now
|
468
|
+
Action.count = 0
|
469
|
+
SCHEMES.user.clear
|
470
|
+
|
471
|
+
Sequel::DATABASES.each do |db|
|
472
|
+
db.models.each{|n, m| Object.send(:remove_const, n) if Object.const_defined?(n)} unless db == E2DB || db == DUMMYDB
|
473
|
+
end
|
463
474
|
|
464
|
-
|
465
|
-
require 'engine2/pre_bootstrap'
|
466
|
-
t = Time.now
|
467
|
-
Action.count = 0
|
468
|
-
SCHEMES.clear
|
475
|
+
load "#{app}/boot.rb"
|
469
476
|
|
470
|
-
|
471
|
-
|
472
|
-
|
477
|
+
Sequel::DATABASES.each &:load_schema_cache_from_file
|
478
|
+
@model_boot_blk.() if @model_boot_blk
|
479
|
+
load 'engine2/models/Files.rb'
|
480
|
+
load 'engine2/models/UserInfo.rb'
|
481
|
+
Dir["#{app}/models/*"].each{|m| load m}
|
482
|
+
puts "MODELS: #{Sequel::DATABASES.reduce(0){|s, d|s + d.models.size}}, Time: #{Time.now - t}"
|
483
|
+
Sequel::DATABASES.each &:dump_schema_cache_to_file
|
473
484
|
|
474
|
-
|
485
|
+
Engine2.send(:remove_const, :ROOT) if defined? ROOT
|
486
|
+
Engine2.const_set(:ROOT, Action.new(nil, :api, DummyMeta, {}))
|
475
487
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
Dir["#{app}/models/*"].each{|m| load m}
|
481
|
-
puts "MODELS, Time: #{Time.now - t}"
|
482
|
-
Sequel::DATABASES.each &:dump_schema_cache_to_file
|
488
|
+
@boot_blk.(ROOT)
|
489
|
+
ROOT.setup_action_tree
|
490
|
+
puts "BOOTSTRAP #{app}, Time: #{Time.new - t}"
|
491
|
+
end
|
483
492
|
|
484
|
-
|
485
|
-
|
486
|
-
|
493
|
+
def bootstrap app, opts = {}
|
494
|
+
@app = app
|
495
|
+
@reloading = opts[:reloading]
|
496
|
+
bootstrap_e2db
|
487
497
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
require 'engine2/post_bootstrap'
|
498
|
+
require 'engine2/pre_bootstrap'
|
499
|
+
reload
|
500
|
+
require 'engine2/post_bootstrap'
|
501
|
+
end
|
493
502
|
end
|
494
503
|
|
504
|
+
@core_loaded = false
|
505
|
+
|
495
506
|
class E2Error < RuntimeError
|
496
507
|
def initialize msg
|
497
508
|
super
|
@@ -534,9 +545,9 @@ module Engine2
|
|
534
545
|
option name, properties, index, &blk
|
535
546
|
end
|
536
547
|
|
537
|
-
def option_index iname
|
548
|
+
def option_index iname, raise = true
|
538
549
|
index = @entries.index{|e| (e.is_a?(MenuBuilder) ? e.name : e[:name]) == iname}
|
539
|
-
raise E2Error.new("No menu option #{iname} found")
|
550
|
+
raise E2Error.new("No menu option #{iname} found") if !index && raise
|
540
551
|
index
|
541
552
|
end
|
542
553
|
|
data/lib/engine2/handler.rb
CHANGED
@@ -113,7 +113,10 @@ module Engine2
|
|
113
113
|
get '/*' do |name|
|
114
114
|
headers 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'
|
115
115
|
if name.empty?
|
116
|
-
|
116
|
+
if settings.environment == :development
|
117
|
+
load('engine2.rb') if Engine2::reloading
|
118
|
+
Engine2::reload
|
119
|
+
end
|
117
120
|
name = 'index'
|
118
121
|
end
|
119
122
|
slim name.to_sym
|
data/lib/engine2/meta.rb
CHANGED
@@ -140,13 +140,13 @@ module Engine2
|
|
140
140
|
|
141
141
|
class DummyMeta < Meta
|
142
142
|
meta_type :dummy
|
143
|
-
|
144
|
-
# def invoke handler
|
145
|
-
# {}
|
146
|
-
# end
|
147
143
|
end
|
148
144
|
|
149
145
|
module MetaAPISupport
|
146
|
+
def reload_routes!
|
147
|
+
@meta[:reload_routes] = true
|
148
|
+
end
|
149
|
+
|
150
150
|
def info
|
151
151
|
@meta[:info] ||= {}
|
152
152
|
end
|
@@ -166,6 +166,10 @@ module Engine2
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
+
def loc! hash
|
170
|
+
hash.each{|k, v| info! k, loc: v}
|
171
|
+
end
|
172
|
+
|
169
173
|
def decorate list
|
170
174
|
list.each do |f|
|
171
175
|
m = (info[f] ||= {})
|
@@ -420,13 +424,10 @@ module Engine2
|
|
420
424
|
panel_panel_template 'menu_m' unless panel[:panel_template] == false
|
421
425
|
# modal_action false if panel[:panel_template] == false
|
422
426
|
panel_class '' unless panel[:class]
|
427
|
+
footer true unless panel[:footer] == false
|
423
428
|
end
|
424
429
|
end
|
425
430
|
|
426
|
-
def glyphicon name
|
427
|
-
"<span class='glyphicon glyphicon-#{name}'></span>"
|
428
|
-
end
|
429
|
-
|
430
431
|
def panel
|
431
432
|
@meta[:panel] ||= {}
|
432
433
|
end
|
@@ -450,6 +451,10 @@ module Engine2
|
|
450
451
|
def panel_title tle
|
451
452
|
panel[:title] = tle
|
452
453
|
end
|
454
|
+
|
455
|
+
def footer ftr
|
456
|
+
panel[:footer] = ftr
|
457
|
+
end
|
453
458
|
end
|
454
459
|
|
455
460
|
class MenuMeta < Meta
|
@@ -543,16 +548,14 @@ module Engine2
|
|
543
548
|
panel_template 'scaffold/list'
|
544
549
|
panel_panel_template 'panels/menu_m' unless action.parent.*.assets[:model]
|
545
550
|
search_template 'scaffold/search'
|
546
|
-
panel_title "#{
|
551
|
+
panel_title "#{:list.icon} #{LOCS[assets[:model].name.to_sym]}"
|
547
552
|
menu(:panel_menu).option :cancel, icon: "remove"
|
548
553
|
menu :menu do
|
549
554
|
properties break: 2, group_class: "btn-group-xs"
|
550
555
|
option :search_toggle, icon: "search", show: "action.meta.search_fields", class: "action.ui_state.search_active && 'active'", button_loc: false
|
551
|
-
|
552
556
|
# divider
|
553
557
|
option :refresh, icon: "refresh", button_loc: false
|
554
558
|
option :default_order, icon: "signal", button_loc: false
|
555
|
-
option :select_toggle, icon: "check", enabled: "action.meta.config.selectable", button_loc: false
|
556
559
|
divider
|
557
560
|
option :debug_info, icon: "list-alt" do
|
558
561
|
option :show_meta, icon: "eye-open"
|
@@ -566,6 +569,13 @@ module Engine2
|
|
566
569
|
@meta[:state] = [:query, :ui_state]
|
567
570
|
end
|
568
571
|
|
572
|
+
def select_toggle_menu
|
573
|
+
m = menu :menu
|
574
|
+
unless m.option_index(:select_toggle, false)
|
575
|
+
m.option_after :default_order, :select_toggle, icon: "check", enabled: "action.meta.config.selectable", button_loc: false
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
569
579
|
def post_run
|
570
580
|
unless panel[:class]
|
571
581
|
panel_class case @meta[:fields].size
|
@@ -666,6 +676,10 @@ module Engine2
|
|
666
676
|
include MetaModelSupport
|
667
677
|
attr_reader :validations
|
668
678
|
|
679
|
+
def self.included meta
|
680
|
+
meta.http_method :post
|
681
|
+
end
|
682
|
+
|
669
683
|
def validate_fields *fields
|
670
684
|
if fields.empty?
|
671
685
|
@validate_fields
|
@@ -736,6 +750,10 @@ module Engine2
|
|
736
750
|
module MetaViewSupport
|
737
751
|
include MetaModelSupport, MetaAPISupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport
|
738
752
|
|
753
|
+
def self.included meta
|
754
|
+
meta.meta_type :view
|
755
|
+
end
|
756
|
+
|
739
757
|
def pre_run
|
740
758
|
super
|
741
759
|
panel_template 'scaffold/view'
|
@@ -761,6 +779,35 @@ module Engine2
|
|
761
779
|
end
|
762
780
|
end
|
763
781
|
|
782
|
+
module MetaDeleteSupport
|
783
|
+
include MetaModelSupport
|
784
|
+
|
785
|
+
def self.included meta
|
786
|
+
meta.http_method :delete
|
787
|
+
meta.meta_type :delete
|
788
|
+
end
|
789
|
+
|
790
|
+
def pre_run
|
791
|
+
super
|
792
|
+
action.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
module MetaBulkDeleteSupport
|
797
|
+
include MetaModelSupport
|
798
|
+
|
799
|
+
def self.included meta
|
800
|
+
meta.http_method :delete
|
801
|
+
meta.meta_type :bulk_delete
|
802
|
+
end
|
803
|
+
|
804
|
+
def pre_run
|
805
|
+
super
|
806
|
+
action.parent.parent.*.select_toggle_menu
|
807
|
+
action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
764
811
|
(FormRendererPostProcessors ||= {}).merge!(
|
765
812
|
boolean: lambda{|meta, field, info|
|
766
813
|
meta.info[field][:render].merge! true_value: info[:true_value], false_value: info[:false_value]
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module Engine2
|
4
4
|
class DeleteMetaBase < Meta
|
5
|
-
include MetaModelSupport
|
6
5
|
|
7
6
|
def invoke_delete_db handler, ids
|
8
7
|
begin
|
@@ -40,13 +39,7 @@ module Engine2
|
|
40
39
|
end
|
41
40
|
|
42
41
|
class DeleteMeta < DeleteMetaBase
|
43
|
-
|
44
|
-
meta_type :delete
|
45
|
-
|
46
|
-
def pre_run
|
47
|
-
super
|
48
|
-
action.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
|
49
|
-
end
|
42
|
+
include MetaDeleteSupport
|
50
43
|
|
51
44
|
def invoke handler
|
52
45
|
handler.permit id = handler.params[:id]
|
@@ -55,13 +48,7 @@ module Engine2
|
|
55
48
|
end
|
56
49
|
|
57
50
|
class BulkDeleteMeta < DeleteMetaBase
|
58
|
-
|
59
|
-
meta_type :bulk_delete
|
60
|
-
|
61
|
-
def pre_run
|
62
|
-
super
|
63
|
-
action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
|
64
|
-
end
|
51
|
+
include MetaBulkDeleteSupport
|
65
52
|
|
66
53
|
def invoke handler
|
67
54
|
ids = handler.param_to_json(:ids)
|
@@ -12,7 +12,7 @@ module Engine2
|
|
12
12
|
option :inspect_modal, icon: :wrench, button_loc: false # , show: "action.logged_on"
|
13
13
|
|
14
14
|
option :logout_form, icon: :"log-out" # , show: "action.logged_on"
|
15
|
-
option :login_form, icon: :"log-in" # , show: "!action.logged_on"
|
15
|
+
option :login_form, icon: :"log-in", disabled: 'action.action_pending' # , show: "!action.logged_on"
|
16
16
|
end
|
17
17
|
|
18
18
|
@meta_type = :infra
|
@@ -249,7 +249,6 @@ module Engine2
|
|
249
249
|
|
250
250
|
class LoginMeta < Meta
|
251
251
|
include MetaApproveSupport
|
252
|
-
http_method :post
|
253
252
|
meta_type :login
|
254
253
|
|
255
254
|
def after_approve handler, record
|
@@ -113,6 +113,7 @@ module Engine2
|
|
113
113
|
|
114
114
|
def pre_run
|
115
115
|
super
|
116
|
+
action.parent.parent.*.select_toggle_menu
|
116
117
|
action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_unlink, icon: "minus", show: "action.selected_size() > 0", button_loc: false
|
117
118
|
end
|
118
119
|
|
@@ -155,7 +155,7 @@ module Engine2
|
|
155
155
|
meta_type :star_to_many_list
|
156
156
|
def pre_run
|
157
157
|
super
|
158
|
-
panel_title "#{
|
158
|
+
panel_title "#{:list.icon} #{LOCS[assets[:assoc][:name]]}"
|
159
159
|
end
|
160
160
|
|
161
161
|
# def decode_panel_title handler
|
@@ -177,10 +177,11 @@ module Engine2
|
|
177
177
|
handler.permit parent = handler.params[:parent_id]
|
178
178
|
model = assets[:model]
|
179
179
|
assoc = assets[:assoc]
|
180
|
+
parent_keys = split_keys(parent)
|
180
181
|
case assoc[:type]
|
181
182
|
when :one_to_many
|
182
183
|
keys = assoc[:keys]
|
183
|
-
condition =
|
184
|
+
condition = parent_keys.all?(&:empty?) ? false : Hash[keys.map{|k| k.qualify(model.table_name)}.zip(parent_keys)]
|
184
185
|
if handler.params[:negate]
|
185
186
|
query = query.exclude(condition)
|
186
187
|
query = query.or(Hash[keys.zip([nil])]) if keys.all?{|k|model.db_schema[k][:allow_null] == true} # type_info[:required] ?
|
@@ -194,7 +195,7 @@ module Engine2
|
|
194
195
|
l_keys = assoc[:left_keys].map{|k| k.qualify(j_table)}
|
195
196
|
r_keys = assoc[:right_keys].map{|k| k.qualify(j_table)}
|
196
197
|
r_keys_vals = Hash[r_keys.zip(q_pk)]
|
197
|
-
l_keys_vals =
|
198
|
+
l_keys_vals = parent_keys.all?(&:empty?) ? false : Hash[l_keys.zip(parent_keys)]
|
198
199
|
|
199
200
|
if handler.params[:negate]
|
200
201
|
query.exclude(model.db[j_table].select(nil).where(r_keys_vals, l_keys_vals).exists)
|
@@ -4,7 +4,6 @@ module Engine2
|
|
4
4
|
|
5
5
|
class SaveMeta < Meta
|
6
6
|
include MetaApproveSupport
|
7
|
-
http_method :post
|
8
7
|
|
9
8
|
def validate_and_approve handler, record, json
|
10
9
|
record.skip_save_refresh = true
|
@@ -28,7 +27,7 @@ module Engine2
|
|
28
27
|
end
|
29
28
|
|
30
29
|
class InsertMeta < SaveMeta
|
31
|
-
meta_type :
|
30
|
+
meta_type :approve
|
32
31
|
def allocate_record handler, json
|
33
32
|
record = super(handler, json)
|
34
33
|
record.instance_variable_set(:"@new", true)
|
@@ -40,7 +39,7 @@ module Engine2
|
|
40
39
|
end
|
41
40
|
|
42
41
|
class UpdateMeta < SaveMeta
|
43
|
-
meta_type :
|
42
|
+
meta_type :approve
|
44
43
|
def allocate_record handler, json
|
45
44
|
record = super(handler, json)
|
46
45
|
model = assets[:model]
|
@@ -48,16 +47,4 @@ module Engine2
|
|
48
47
|
record
|
49
48
|
end
|
50
49
|
end
|
51
|
-
|
52
|
-
module TimeStampMeta
|
53
|
-
def before_approve handler, record
|
54
|
-
super
|
55
|
-
puts "before approve"
|
56
|
-
end
|
57
|
-
|
58
|
-
def after_approve handler, record
|
59
|
-
super
|
60
|
-
puts "after approve"
|
61
|
-
end
|
62
|
-
end
|
63
50
|
end
|
data/lib/engine2/model.rb
CHANGED
@@ -7,28 +7,25 @@ module Engine2
|
|
7
7
|
attr_reader :before_save_processors, :after_save_processors, :before_destroy_processors, :after_destroy_processors
|
8
8
|
attr_reader :validation_in_transaction
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
@after_destroy_processors = nil
|
28
|
-
@type_info_synchronized = nil
|
29
|
-
end
|
30
|
-
cls.setup_schema
|
10
|
+
def self.extended cls
|
11
|
+
models = cls.db.models
|
12
|
+
raise E2Error.new("Model '#{cls.name}' already defined") if models[cls.name.to_sym]
|
13
|
+
models[cls.name.to_sym] = cls
|
14
|
+
|
15
|
+
cls.instance_eval do
|
16
|
+
@many_to_one_associations = association_reflections.select{|n, a| a[:type] == :many_to_one}
|
17
|
+
@one_to_many_associations = association_reflections.select{|n, a| a[:type] == :one_to_many}
|
18
|
+
@many_to_many_associations = association_reflections.select{|n, a| a[:type] == :many_to_many}
|
19
|
+
# @one_to_one_associations = association_reflections.select{|n, a| a[:type] == :one_to_one}
|
20
|
+
@validation_in_transaction = nil
|
21
|
+
@before_save_processors = nil
|
22
|
+
@after_save_processors = nil
|
23
|
+
@around_save_processors = nil
|
24
|
+
@before_destroy_processors = nil
|
25
|
+
@after_destroy_processors = nil
|
26
|
+
@type_info_synchronized = nil
|
31
27
|
end
|
28
|
+
cls.setup_schema
|
32
29
|
end
|
33
30
|
|
34
31
|
def install_processors processors
|
@@ -111,6 +108,7 @@ module Engine2
|
|
111
108
|
|
112
109
|
def synchronize_type_info
|
113
110
|
resolve_dependencies
|
111
|
+
verify_associations
|
114
112
|
@before_save_processors = install_processors(BeforeSaveProcessors)
|
115
113
|
@after_save_processors = install_processors(AfterSaveProcessors)
|
116
114
|
@around_save_processors = {}
|
@@ -119,6 +117,18 @@ module Engine2
|
|
119
117
|
@type_info_synchronized = true
|
120
118
|
end
|
121
119
|
|
120
|
+
def verify_associations
|
121
|
+
one_to_many_associations.each do |name, assoc|
|
122
|
+
other = Object.const_get(assoc[:class_name])
|
123
|
+
other_type_info = other.type_info
|
124
|
+
if other_keys = assoc[:keys]
|
125
|
+
other_keys.each do |key|
|
126
|
+
raise E2Error.new("No key '#{key}' found in model '#{other}' being related from #{self}") unless other_type_info[key]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
122
132
|
def resolve_dependencies
|
123
133
|
resolved = {}
|
124
134
|
@type_info.each_pair do |name, info|
|
@@ -133,7 +143,7 @@ module Engine2
|
|
133
143
|
deps = @type_info[name][:depends]
|
134
144
|
deps.each do |e|
|
135
145
|
if !resolved[e]
|
136
|
-
raise "Circular dependency for field '#{name}' in model '#{self}'" if seen.include?(e)
|
146
|
+
raise E2Error.new("Circular dependency for field '#{name}' in model '#{self}'") if seen.include?(e)
|
137
147
|
resolve_dependency(e, resolved, seen)
|
138
148
|
end
|
139
149
|
end if deps
|
data/lib/engine2/scheme.rb
CHANGED
@@ -6,31 +6,23 @@ module Engine2
|
|
6
6
|
VIEW ||= {view: true}.freeze
|
7
7
|
LINK ||= {star_to_many_link: true, view: true, star_to_many_unlink: true}.freeze # star_to_many_bulk_unlink: true
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :builtin, :user
|
10
10
|
def initialize
|
11
11
|
@builtin = {}
|
12
|
-
@
|
12
|
+
@user = {}
|
13
13
|
end
|
14
14
|
|
15
15
|
def define_scheme name, &blk
|
16
|
-
schemes = Engine2::
|
17
|
-
raise "Scheme '#{name}' already defined" if schemes[name]
|
16
|
+
schemes = Engine2::core_loaded ? @user : @builtin
|
17
|
+
raise E2Error.new("Scheme '#{name}' already defined") if schemes[name]
|
18
18
|
schemes[name] = blk
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def [] name
|
26
|
-
scheme = @schemes[name]
|
27
|
-
raise E2Error.new("Scheme #{name} not found") unless scheme
|
21
|
+
def [] name, raise = true
|
22
|
+
scheme = @builtin[name] || @user[name]
|
23
|
+
raise E2Error.new("Scheme #{name} not found") if !scheme && raise
|
28
24
|
scheme
|
29
25
|
end
|
30
|
-
|
31
|
-
def merge!
|
32
|
-
@schemes.merge!(@builtin){|n| raise E2Error.new("Scheme collision: #{n}")}
|
33
|
-
end
|
34
26
|
end
|
35
27
|
|
36
28
|
SCHEMES ||= Schemes.new
|
data/lib/engine2/type_info.rb
CHANGED
@@ -209,8 +209,8 @@ module Engine2
|
|
209
209
|
info[:multiple] = multiple
|
210
210
|
info[:table] = table
|
211
211
|
info[:store] = store
|
212
|
-
info[:store][:upload] ||= "#{
|
213
|
-
info[:store][:files] ||= "#{
|
212
|
+
info[:store][:upload] ||= "#{Engine2::app}/store/upload"
|
213
|
+
info[:store][:files] ||= "#{Engine2::app}/store/files"
|
214
214
|
info[:transaction] = true
|
215
215
|
end
|
216
216
|
end
|
@@ -265,9 +265,8 @@ module Engine2
|
|
265
265
|
end
|
266
266
|
|
267
267
|
def foreign_blob_store_field assoc_name, name, name_field, mime_field
|
268
|
-
assoc = @model.
|
269
|
-
raise E2Error.new("
|
270
|
-
raise E2Error.new("Association '#{assoc_name}' in model '#{@mode}' is not of type many_to_one") unless assoc[:type] == :many_to_one
|
268
|
+
assoc = @model.many_to_one_associations[assoc_name]
|
269
|
+
raise E2Error.new("'many_to_one' association '#{assoc_name}' not found for model '#{@model}'") unless assoc
|
271
270
|
define_field :"#{assoc[:key]}_blob", :foreign_blob_store do |info|
|
272
271
|
info[:assoc_name] = assoc_name
|
273
272
|
info[:bytes_field] = name
|
@@ -278,9 +277,8 @@ module Engine2
|
|
278
277
|
end
|
279
278
|
|
280
279
|
def many_to_one_field assoc_name
|
281
|
-
assoc = @model.
|
282
|
-
raise E2Error.new("
|
283
|
-
raise E2Error.new("Association '#{assoc_name}' in model '#{@mode}' is not of type many_to_one") unless assoc[:type] == :many_to_one
|
280
|
+
assoc = @model.many_to_one_associations[assoc_name]
|
281
|
+
raise E2Error.new("'many_to_one' association '#{assoc_name}' not found for model '#{@model}'") unless assoc
|
284
282
|
keys = assoc[:keys]
|
285
283
|
modify_field keys.first do |info|
|
286
284
|
info[:type] = :many_to_one
|
@@ -290,9 +288,8 @@ module Engine2
|
|
290
288
|
end
|
291
289
|
|
292
290
|
def star_to_many_field assoc_name
|
293
|
-
assoc = @model.
|
294
|
-
raise E2Error.new("
|
295
|
-
raise E2Error.new("Association '#{assoc_name}' in model '#{@model}' is not of type *_to_many") unless [:one_to_many, :many_to_many].include?(assoc[:type])
|
291
|
+
assoc = @model.one_to_many_associations[assoc_name] || @model.many_to_many_associations[assoc_name]
|
292
|
+
raise E2Error.new("'*_to_many' association '#{assoc_name}' not found for model '#{@model}'") unless assoc
|
296
293
|
define_field assoc_name, :string do |info|
|
297
294
|
info[:type] = :star_to_many_field
|
298
295
|
info[:keys] = assoc[:keys]
|
@@ -309,7 +306,7 @@ module Engine2
|
|
309
306
|
# list.map{|k, v| {id: k, value: v}}
|
310
307
|
list.to_a
|
311
308
|
else
|
312
|
-
raise E2Error.new("type not supported for list_select modifier for field #{name}")
|
309
|
+
raise E2Error.new("type '#{list.class}' not supported for list_select modifier for field #{name}")
|
313
310
|
end
|
314
311
|
info[:validations][:list_select] = true
|
315
312
|
end
|
@@ -317,7 +314,7 @@ module Engine2
|
|
317
314
|
|
318
315
|
def decode name, dinfo = {form: {scaffold: true}, search: {scaffold: true}}
|
319
316
|
modify_field name do |info|
|
320
|
-
raise E2Error.new("Field type of #{name} needs to be
|
317
|
+
raise E2Error.new("Field type of '#{name}' in model '#{@model}' needs to be 'many_to_one'") unless info[:type] == :many_to_one
|
321
318
|
dec = info[:decode] ||= {}
|
322
319
|
dec[:search].clear if dinfo[:search] && dec[:search]
|
323
320
|
dec[:form].clear if dinfo[:form] && dec[:form]
|
data/lib/engine2/version.rb
CHANGED
data/views/engine2.coffee
CHANGED
@@ -279,14 +279,14 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
|
|
279
279
|
error: (title, msg, html) ->
|
280
280
|
body = if html then msg else "<div class='alert alert-danger'>#{msg}</div>"
|
281
281
|
clazz = if html then "modal-huge" else "modal-large"
|
282
|
-
@show meta: panel: (panel_template: "close_m", template_string: body, title: title, class: clazz) # message: msg,
|
282
|
+
@show meta: panel: (panel_template: "close_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
|
283
283
|
|
284
284
|
confirm: (title, msg, action) ->
|
285
285
|
body = "<div class='alert alert-warning'>#{msg}</div>"
|
286
286
|
clazz = "modal-large"
|
287
287
|
@show
|
288
288
|
confirm: action,
|
289
|
-
meta: panel: (panel_template: "confirm_m", template_string: body, title: title, class: clazz) # message: msg,
|
289
|
+
meta: panel: (panel_template: "confirm_m", template_string: body, title: title, class: clazz, footer: true) # message: msg,
|
290
290
|
|
291
291
|
.directive 'e2Modal', ($e2Modal) ->
|
292
292
|
restrict: 'E'
|
@@ -301,7 +301,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
|
|
301
301
|
return if ev.defaultPrevented
|
302
302
|
ev.preventDefault()
|
303
303
|
|
304
|
-
panel = panel_template: attrs.panelTemplate, title: attrs.title, class: attrs.clazz
|
304
|
+
panel = panel_template: attrs.panelTemplate, title: attrs.title, class: attrs.clazz, footer: true
|
305
305
|
if obody then panel.template_string = obody.outerHTML else panel.template = attrs.template
|
306
306
|
action = meta: (panel: panel), scope: -> scope
|
307
307
|
_.assign(action, args)
|
@@ -386,12 +386,16 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
|
|
386
386
|
selection = scope.action.selection
|
387
387
|
out = ''
|
388
388
|
_.each scope.action.entries, (e, i) ->
|
389
|
-
out += if selection then "<tr ng-class='action.selected_class(#{i})' class='tr_hover' ng-click='action.select(#{i}, $event)'>" else
|
389
|
+
out += if selection then "<tr ng-class='action.selected_class(#{i})' class='tr_hover' ng-click='action.select(#{i}, $event)'>" else
|
390
|
+
row_cls = e.$row_info?.class
|
391
|
+
if row_cls then "<tr class=\"#{row_cls}\">" else "<tr>"
|
390
392
|
out += "<td>"
|
391
|
-
out += "<div e2-button-set='action.meta.menus.item_menu' index='#{i}'></div>" if meta.config.show_item_menu
|
393
|
+
out += "<div e2-button-set='action.meta.menus.item_menu' index='#{i}'></div>" if meta.config.show_item_menu
|
392
394
|
out += "</td>"
|
393
|
-
|
394
|
-
|
395
|
+
_.each meta.fields, (f) ->
|
396
|
+
out += if col_cls = meta.info[f].column_class then "<td class='#{col_cls}'>" else "<td>"
|
397
|
+
out += scope.action.list_cell(e, f) ? ''
|
398
|
+
out += "</td>"
|
395
399
|
out += "</tr>"
|
396
400
|
|
397
401
|
elem.empty()
|
data/views/engine2actions.coffee
CHANGED
@@ -128,9 +128,11 @@ angular.module('Engine2')
|
|
128
128
|
|
129
129
|
pre_invoke: ->
|
130
130
|
@parent().action_pending = true
|
131
|
+
@action_pending = true
|
131
132
|
# @parent().parent().action_pending = true if @parent().parent()
|
132
133
|
post_invoke: ->
|
133
134
|
delete @parent().action_pending # = false
|
135
|
+
delete @action_pending
|
134
136
|
# @parent().parent().action_pending = false if @parent().parent()
|
135
137
|
invoke: ->
|
136
138
|
args = arguments
|
@@ -282,7 +284,7 @@ angular.module('Engine2')
|
|
282
284
|
|
283
285
|
traverse: (routes) ->
|
284
286
|
menu_tmpl = _.template("<li><a href='{{href}}'>{{icon}}{{aicon}} {{loc}}</a></li>")
|
285
|
-
menu_sub_tmpl = _.template("<li e2-drop-down='{{dropdown}}'><a href='javascript://'>{{icon}}{{aicon}}{{loc}}<span class='caret'></span></a></li>")
|
287
|
+
menu_sub_tmpl = _.template("<li e2-drop-down='{{dropdown}}'><a href='javascript://'>{{icon}}{{aicon}} {{loc}}<span class='caret'></span></a></li>")
|
286
288
|
out = routes.map (route, i) ->
|
287
289
|
if route.menu
|
288
290
|
menu_sub_tmpl
|
@@ -565,6 +567,7 @@ angular.module('Engine2')
|
|
565
567
|
super()
|
566
568
|
@decode_field = @scope().f
|
567
569
|
@dinfo = @parentp().meta.info[@decode_field]
|
570
|
+
throw "Primary and foreign key list lengths dont match: [#{@meta.primary_fields}] and [#{@dinfo.fields}]" unless @meta.primary_fields.length == @dinfo.fields.length
|
568
571
|
@scope().$on "search_reset", => @clean()
|
569
572
|
|
570
573
|
if_fk_values: (f) ->
|
data/views/infra/inspect.slim
CHANGED
data/views/modals/close_m.slim
CHANGED
data/views/modals/confirm_m.slim
CHANGED
data/views/modals/empty_m.slim
CHANGED
data/views/modals/menu_m.slim
CHANGED
data/views/modals/yes_no_m.slim
CHANGED
data/views/panels/menu_m.slim
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
h4.modal-title ng-bind-html="action.meta.panel.title"
|
4
4
|
.panel-body
|
5
5
|
| modal-content-to-replace
|
6
|
-
.panel-footer
|
6
|
+
.panel-footer ng-if="::action.meta.panel.footer"
|
7
7
|
// .loader.pull-left: img src="img/ajax-loader.gif"
|
8
8
|
div e2-button-set="action.meta.menus.panel_menu"
|
9
9
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: engine2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lopex
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|