simple_master 1.0.0
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 +7 -0
- data/lib/simple_master/active_record/belongs_to_master_polymorphic_reflection.rb +11 -0
- data/lib/simple_master/active_record/belongs_to_polymorphic_association.rb +17 -0
- data/lib/simple_master/active_record/belongs_to_polymorphic_builder.rb +21 -0
- data/lib/simple_master/active_record/extension.rb +183 -0
- data/lib/simple_master/active_record/preloader_association_extension.rb +11 -0
- data/lib/simple_master/active_record.rb +12 -0
- data/lib/simple_master/loader/dataset_loader.rb +20 -0
- data/lib/simple_master/loader/marshal_loader.rb +15 -0
- data/lib/simple_master/loader/query_loader.rb +63 -0
- data/lib/simple_master/loader.rb +55 -0
- data/lib/simple_master/master/association/belongs_to_association.rb +79 -0
- data/lib/simple_master/master/association/belongs_to_polymorphic_association.rb +79 -0
- data/lib/simple_master/master/association/has_many_association.rb +53 -0
- data/lib/simple_master/master/association/has_many_through_association.rb +64 -0
- data/lib/simple_master/master/association/has_one_association.rb +57 -0
- data/lib/simple_master/master/association.rb +50 -0
- data/lib/simple_master/master/column/bitmask_column.rb +74 -0
- data/lib/simple_master/master/column/boolean_column.rb +51 -0
- data/lib/simple_master/master/column/enum_column.rb +96 -0
- data/lib/simple_master/master/column/float_column.rb +21 -0
- data/lib/simple_master/master/column/id_column.rb +31 -0
- data/lib/simple_master/master/column/integer_column.rb +21 -0
- data/lib/simple_master/master/column/json_column.rb +27 -0
- data/lib/simple_master/master/column/polymorphic_type_column.rb +44 -0
- data/lib/simple_master/master/column/sti_type_column.rb +21 -0
- data/lib/simple_master/master/column/string_column.rb +17 -0
- data/lib/simple_master/master/column/symbol_column.rb +23 -0
- data/lib/simple_master/master/column/time_column.rb +38 -0
- data/lib/simple_master/master/column.rb +138 -0
- data/lib/simple_master/master/dsl.rb +239 -0
- data/lib/simple_master/master/editable.rb +155 -0
- data/lib/simple_master/master/filterable.rb +47 -0
- data/lib/simple_master/master/queryable.rb +75 -0
- data/lib/simple_master/master/storable.rb +20 -0
- data/lib/simple_master/master/validatable.rb +216 -0
- data/lib/simple_master/master.rb +417 -0
- data/lib/simple_master/schema.rb +49 -0
- data/lib/simple_master/storage/dataset.rb +172 -0
- data/lib/simple_master/storage/ondemand_table.rb +68 -0
- data/lib/simple_master/storage/table.rb +197 -0
- data/lib/simple_master/storage/test_table.rb +69 -0
- data/lib/simple_master/storage.rb +11 -0
- data/lib/simple_master/version.rb +5 -0
- data/lib/simple_master.rb +62 -0
- metadata +128 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "simple_master/storage/table"
|
|
4
|
+
require "simple_master/storage/test_table"
|
|
5
|
+
|
|
6
|
+
module SimpleMaster
|
|
7
|
+
module Storage
|
|
8
|
+
class Dataset
|
|
9
|
+
require "objspace"
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def after_load_procs
|
|
13
|
+
@after_load_procs ||= []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def after_load(&proc)
|
|
17
|
+
after_load_procs << proc
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run_after_load
|
|
21
|
+
after_load_procs.each(&:call)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
DEFAULT_TABLE = Table
|
|
26
|
+
DEFAULT_LOADER = Loader::QueryLoader
|
|
27
|
+
|
|
28
|
+
attr_reader :table_class
|
|
29
|
+
attr_reader :loader
|
|
30
|
+
attr_reader :diff
|
|
31
|
+
attr_accessor :tables
|
|
32
|
+
attr_accessor :cache
|
|
33
|
+
attr_accessor :load_targets
|
|
34
|
+
|
|
35
|
+
def initialize(table_class: DEFAULT_TABLE, loader: nil)
|
|
36
|
+
@table_class = table_class
|
|
37
|
+
@loader = loader || DEFAULT_LOADER.new
|
|
38
|
+
@diff = {}
|
|
39
|
+
|
|
40
|
+
initialize_cache
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def initialize_cache
|
|
44
|
+
self.tables = Hash.new { |hash, klass| hash[klass] = table_class.new(klass, self, loader) }.compare_by_identity
|
|
45
|
+
self.cache = {}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def table(klass)
|
|
49
|
+
@tables[klass]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def reload
|
|
53
|
+
if table_class <= SimpleMaster::Storage::OndemandTable
|
|
54
|
+
unload
|
|
55
|
+
else
|
|
56
|
+
load
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def load
|
|
61
|
+
memsize do
|
|
62
|
+
cache.clear
|
|
63
|
+
targets = @load_targets || SimpleMaster.targets
|
|
64
|
+
|
|
65
|
+
tables = targets.map(&:base_class).uniq.map { table(_1) }
|
|
66
|
+
|
|
67
|
+
timer("MasterData load") do
|
|
68
|
+
tables.each(&:load_records)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
timer("MasterData cache update") do
|
|
72
|
+
SimpleMaster.use_dataset(self) do
|
|
73
|
+
tables.each(&:update_class_method_cache)
|
|
74
|
+
tables.each(&:tap_instance_methods)
|
|
75
|
+
tables.each(&:freeze_all)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
timer("Cache update") do
|
|
80
|
+
SimpleMaster.use_dataset(self) do
|
|
81
|
+
self.class.run_after_load
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def unload
|
|
89
|
+
cache.clear
|
|
90
|
+
tables.clear
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# NOTE: Pass a empty hash to duplicate with empty diff.
|
|
94
|
+
def duplicate(diff: nil)
|
|
95
|
+
diff ||= @diff
|
|
96
|
+
new_dataset = self.class.new(table_class: table_class, loader: loader)
|
|
97
|
+
new_dataset.diff = diff.deep_dup
|
|
98
|
+
tables.each do |klass, table|
|
|
99
|
+
new_dataset.tables[klass] = table.duplicate_for(new_dataset)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
new_dataset
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def reload_klass(klass)
|
|
106
|
+
fail NotImplementedError unless table_class == SimpleMaster::Storage::OndemandTable
|
|
107
|
+
|
|
108
|
+
tables.delete(klass)
|
|
109
|
+
end
|
|
110
|
+
alias reload_class reload_klass
|
|
111
|
+
|
|
112
|
+
# Cache helper for other data sources.
|
|
113
|
+
def cache_read(key)
|
|
114
|
+
cache[key]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def cache_fetch(key)
|
|
118
|
+
cache.fetch(key) do
|
|
119
|
+
cache[key] = yield
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def cache_write(key, value)
|
|
124
|
+
cache[key] = value
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def cache_delete(key)
|
|
128
|
+
cache.delete(key)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# NOTE: set diff before records loaded.
|
|
132
|
+
def diff=(diff_json)
|
|
133
|
+
@diff = if diff_json.is_a?(String)
|
|
134
|
+
JSON.parse(diff_json)
|
|
135
|
+
else
|
|
136
|
+
diff_json || {}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def memsize
|
|
143
|
+
GC.compact
|
|
144
|
+
before = ObjectSpace.memsize_of_all
|
|
145
|
+
|
|
146
|
+
res = yield
|
|
147
|
+
|
|
148
|
+
GC.compact
|
|
149
|
+
after = ObjectSpace.memsize_of_all
|
|
150
|
+
|
|
151
|
+
SimpleMaster.logger.info { "Consumed memory size: #{after - before}" }
|
|
152
|
+
|
|
153
|
+
res
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def timer(text)
|
|
157
|
+
t1 = Time.zone.now
|
|
158
|
+
@timer_index ||= 0
|
|
159
|
+
@timer_index += 1
|
|
160
|
+
|
|
161
|
+
res = yield
|
|
162
|
+
|
|
163
|
+
t2 = Time.zone.now
|
|
164
|
+
@timer_index -= 1
|
|
165
|
+
|
|
166
|
+
SimpleMaster.logger.info { "#{' ' * @timer_index}#{text}: #{t2 - t1}s" }
|
|
167
|
+
|
|
168
|
+
res
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "simple_master/storage/table"
|
|
4
|
+
|
|
5
|
+
module SimpleMaster
|
|
6
|
+
module Storage
|
|
7
|
+
class OndemandTable < Table
|
|
8
|
+
def sub_table(sub_klass)
|
|
9
|
+
(@sub_tables ||= klass.descendants.reject(&:abstract_class).index_with { |k| self.class.new(k, dataset, loader) })[sub_klass]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def load_records
|
|
13
|
+
@class_method_cache = nil
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def all
|
|
18
|
+
return @all if @all
|
|
19
|
+
|
|
20
|
+
if klass.sti_sub_class?
|
|
21
|
+
klass.sti_base_class.all
|
|
22
|
+
@all
|
|
23
|
+
else
|
|
24
|
+
load_records
|
|
25
|
+
SimpleMaster.use_dataset(dataset) do
|
|
26
|
+
tap_instance_methods
|
|
27
|
+
freeze_all
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def id_hash
|
|
33
|
+
return @id_hash if @id_hash
|
|
34
|
+
|
|
35
|
+
update_id_hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def grouped_hash
|
|
39
|
+
return @grouped_hash if @grouped_hash
|
|
40
|
+
|
|
41
|
+
update_grouped_hash
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def class_method_cache
|
|
45
|
+
return @class_method_cache if @class_method_cache
|
|
46
|
+
|
|
47
|
+
# make sure @all is loaded to prevent errors
|
|
48
|
+
all
|
|
49
|
+
|
|
50
|
+
update_class_method_cache
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def update_sub_tables
|
|
54
|
+
sub_klasses = klass.descendants.reject(&:abstract_class)
|
|
55
|
+
return if sub_klasses.empty?
|
|
56
|
+
|
|
57
|
+
grouped = all.group_by(&:class)
|
|
58
|
+
sub_klasses.each do |sub_klass|
|
|
59
|
+
sub_sub_klasses = [sub_klass, *sub_klass.descendants].reject(&:abstract_class)
|
|
60
|
+
|
|
61
|
+
sub_table = self.sub_table(sub_klass)
|
|
62
|
+
sub_table.all = sub_sub_klasses.flat_map { grouped[_1] || EMPTY_ARRAY }.freeze
|
|
63
|
+
sub_table.digest = digest
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleMaster
|
|
4
|
+
module Storage
|
|
5
|
+
class Table
|
|
6
|
+
METADATA_PREFIX = "__"
|
|
7
|
+
|
|
8
|
+
def initialize(klass, dataset, loader)
|
|
9
|
+
@klass = klass
|
|
10
|
+
@dataset = dataset
|
|
11
|
+
@loader = loader
|
|
12
|
+
@method_cache = Hash.new { |k, v| k[v] = {}.compare_by_identity }.compare_by_identity
|
|
13
|
+
|
|
14
|
+
@sub_tables = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sub_table(klass)
|
|
18
|
+
@sub_tables[klass]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def duplicate_for(dataset, table_klass = self.class)
|
|
22
|
+
table = table_klass.new(klass, dataset, loader)
|
|
23
|
+
|
|
24
|
+
if table.diff == applied_diff
|
|
25
|
+
table.all = all
|
|
26
|
+
table.applied_diff = applied_diff
|
|
27
|
+
table.digest = digest
|
|
28
|
+
|
|
29
|
+
# If already loaded, copy existing id_hash and grouped_hash
|
|
30
|
+
table.id_hash = id_hash if @id_hash
|
|
31
|
+
table.grouped_hash = grouped_hash if @grouped_hash
|
|
32
|
+
table.sub_tables = sub_tables&.transform_values { |sub_table|
|
|
33
|
+
sub_table.duplicate_for(dataset)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
table
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_records
|
|
41
|
+
@method_cache.clear
|
|
42
|
+
|
|
43
|
+
loader.load_records(self)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
attr_accessor :klass
|
|
47
|
+
attr_accessor :dataset
|
|
48
|
+
attr_accessor :loader
|
|
49
|
+
attr_accessor :sub_tables
|
|
50
|
+
attr_accessor :all
|
|
51
|
+
attr_accessor :id_hash
|
|
52
|
+
attr_accessor :grouped_hash
|
|
53
|
+
attr_accessor :class_method_cache
|
|
54
|
+
attr_accessor :method_cache
|
|
55
|
+
attr_accessor :applied_diff
|
|
56
|
+
attr_accessor :digest
|
|
57
|
+
|
|
58
|
+
def freeze_all
|
|
59
|
+
all.each(&:freeze).tap { run_on_sub_tables(:freeze_all) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update_id_hash
|
|
63
|
+
self.id_hash = all.index_by(&:id).freeze
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def update_grouped_hash
|
|
67
|
+
grouped_hash = {}.compare_by_identity
|
|
68
|
+
|
|
69
|
+
klass.group_keys.each do |group_key|
|
|
70
|
+
grouped_hash[group_key] = all.group_by(&group_key).freeze.each_value(&:freeze)
|
|
71
|
+
end
|
|
72
|
+
grouped_hash.freeze
|
|
73
|
+
|
|
74
|
+
self.grouped_hash = grouped_hash
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_class_method_cache
|
|
78
|
+
self.class_method_cache = class_method_cache = {}.compare_by_identity
|
|
79
|
+
# Use instance_eval so a block runs in the declaring class context
|
|
80
|
+
klass.all_class_method_cache_info.each do |args, initializer|
|
|
81
|
+
result = klass.instance_eval(&initializer)
|
|
82
|
+
if args.length == 1
|
|
83
|
+
result = [result]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
args.zip(result).each do |arg, value|
|
|
87
|
+
class_method_cache[arg] = value
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
class_method_cache.freeze.tap {
|
|
91
|
+
run_on_sub_tables(:update_class_method_cache)
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def tap_instance_methods
|
|
96
|
+
klass.instance_methods_need_tap&.each do |method_name|
|
|
97
|
+
all.each(&method_name)
|
|
98
|
+
end
|
|
99
|
+
run_on_sub_tables(:tap_instance_methods)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def update_sub_tables
|
|
103
|
+
sub_klasses = klass.descendants.reject(&:abstract_class)
|
|
104
|
+
return if sub_klasses.empty?
|
|
105
|
+
|
|
106
|
+
@sub_tables = sub_klasses.index_with { |sub_klass| self.class.new(sub_klass, dataset, loader) }
|
|
107
|
+
|
|
108
|
+
grouped = all.group_by(&:class)
|
|
109
|
+
|
|
110
|
+
sub_klasses.each do |sub_klass|
|
|
111
|
+
sub_sub_klasses = [sub_klass, *sub_klass.descendants].reject(&:abstract_class)
|
|
112
|
+
|
|
113
|
+
sub_table = self.sub_table(sub_klass)
|
|
114
|
+
sub_table.all = sub_sub_klasses.flat_map { grouped[_1] || EMPTY_ARRAY }.freeze
|
|
115
|
+
sub_table.digest = digest
|
|
116
|
+
sub_table.applied_diff = applied_diff
|
|
117
|
+
sub_table.update_id_hash
|
|
118
|
+
sub_table.update_grouped_hash
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def run_on_sub_tables(method)
|
|
123
|
+
sub_tables&.each_value(&method)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def diff
|
|
127
|
+
dataset.diff[klass.table_name]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def apply_diff
|
|
131
|
+
update_id_hash
|
|
132
|
+
if diff.present?
|
|
133
|
+
apply_diff_to_id_hash
|
|
134
|
+
self.all = id_hash.values.freeze
|
|
135
|
+
end
|
|
136
|
+
update_grouped_hash
|
|
137
|
+
|
|
138
|
+
self
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def apply_diff_to_id_hash
|
|
142
|
+
id_hash = self.id_hash.dup
|
|
143
|
+
diff&.each do |key, record_diff|
|
|
144
|
+
next if metadata_key?(key)
|
|
145
|
+
id = key.to_i
|
|
146
|
+
|
|
147
|
+
if record_diff.nil?
|
|
148
|
+
id_hash.delete(id)
|
|
149
|
+
next
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
original_record = id_hash[id]
|
|
153
|
+
|
|
154
|
+
record =
|
|
155
|
+
if original_record.nil?
|
|
156
|
+
new_klass = klass.sti_column && record_diff[klass.sti_column.to_s]&.constantize || klass
|
|
157
|
+
new_klass.new(id: id)
|
|
158
|
+
elsif klass.sti_column && record_diff[klass.sti_column.to_s]
|
|
159
|
+
record_diff[klass.sti_column.to_s].constantize.new(original_record.attributes)
|
|
160
|
+
else
|
|
161
|
+
original_record.create_copy
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
record_diff.each do |k, v|
|
|
165
|
+
next if metadata_key?(k)
|
|
166
|
+
# Handle expanded JSON columns by converting keys back to JSON strings before assignment
|
|
167
|
+
if v.is_a?(Hash) || v.is_a?(Array)
|
|
168
|
+
v = v.to_json
|
|
169
|
+
end
|
|
170
|
+
record.send(:"#{k}=", v)
|
|
171
|
+
rescue NoMethodError => _e
|
|
172
|
+
raise ColumnNotExist, "Column #{k} does not exist on #{klass}."
|
|
173
|
+
rescue => e
|
|
174
|
+
raise AssignmentError, "Failed to assign data: #{klass}.#{k} = #{v.inspect}. Message: #{e.message}."
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
id_hash[id] = record
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
self.applied_diff = diff
|
|
181
|
+
self.id_hash = id_hash.freeze
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
class ColumnNotExist < StandardError
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class AssignmentError < StandardError
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
|
|
192
|
+
def metadata_key?(key)
|
|
193
|
+
key.to_s.start_with?(METADATA_PREFIX)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "simple_master/storage/table"
|
|
4
|
+
|
|
5
|
+
module SimpleMaster
|
|
6
|
+
module Storage
|
|
7
|
+
# Ondemand table based on updated id_hash.
|
|
8
|
+
class TestTable < Table
|
|
9
|
+
def initialize(_klass, _dataset, _loader)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
@all = []
|
|
13
|
+
@id_hash = {}
|
|
14
|
+
@grouped_hash = {}
|
|
15
|
+
@class_method_cache = {}
|
|
16
|
+
@method_cache = Hash.new { |k, v| k[v] = {}.compare_by_identity }.compare_by_identity
|
|
17
|
+
|
|
18
|
+
@all_need_update = true
|
|
19
|
+
@grouped_hash_need_update = true
|
|
20
|
+
@class_method_cache_need_update = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def sub_table(sub_klass)
|
|
24
|
+
(@sub_tables ||= klass.descendants.reject(&:abstract_class).index_with { |k| self.class.new(k, dataset, loader) })[sub_klass]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update(id, record)
|
|
28
|
+
id_hash[id] = record
|
|
29
|
+
@all_need_update = true
|
|
30
|
+
@grouped_hash_need_update = true
|
|
31
|
+
@class_method_cache_need_update = true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def record_updated
|
|
35
|
+
@grouped_hash_need_update = true
|
|
36
|
+
@class_method_cache_need_update = true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def all=(records)
|
|
40
|
+
@all_need_update = false
|
|
41
|
+
super.tap { update_id_hash }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def all
|
|
45
|
+
if @all_need_update
|
|
46
|
+
@all_need_update = false
|
|
47
|
+
@all = id_hash.values
|
|
48
|
+
end
|
|
49
|
+
@all
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def grouped_hash
|
|
53
|
+
if @grouped_hash_need_update
|
|
54
|
+
@grouped_hash_need_update = false
|
|
55
|
+
update_grouped_hash
|
|
56
|
+
end
|
|
57
|
+
@grouped_hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def class_method_cache
|
|
61
|
+
if @class_method_cache_need_update
|
|
62
|
+
@class_method_cache_need_update = false
|
|
63
|
+
update_class_method_cache
|
|
64
|
+
end
|
|
65
|
+
@class_method_cache
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleMaster
|
|
4
|
+
module Storage
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require "simple_master/storage/table"
|
|
9
|
+
require "simple_master/storage/ondemand_table"
|
|
10
|
+
require "simple_master/storage/test_table"
|
|
11
|
+
require "simple_master/storage/dataset"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/dependencies"
|
|
4
|
+
require "active_record"
|
|
5
|
+
require "request_store"
|
|
6
|
+
require "logger"
|
|
7
|
+
require "simple_master/version"
|
|
8
|
+
|
|
9
|
+
module SimpleMaster
|
|
10
|
+
EMPTY_ARRAY = [].freeze
|
|
11
|
+
EMPTY_HASH = {}.freeze
|
|
12
|
+
|
|
13
|
+
def self.logger
|
|
14
|
+
if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
|
15
|
+
Rails.logger
|
|
16
|
+
else
|
|
17
|
+
@logger ||= Logger.new($stdout)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.init(for_test: false)
|
|
22
|
+
is_database_available = database_available?
|
|
23
|
+
unless is_database_available
|
|
24
|
+
SimpleMaster.logger.warn "DB not connected. SimpleMaster will not initialize associations to ActiveRecord."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
yield if block_given?
|
|
28
|
+
|
|
29
|
+
targets.each { |klass| klass.init(is_database_available, for_test: for_test) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.targets
|
|
33
|
+
Master.descendants.reject(&:abstract_class)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.database_available?
|
|
37
|
+
# Raises an error if the DB is missing
|
|
38
|
+
::ActiveRecord::Base.connection.verify!
|
|
39
|
+
|
|
40
|
+
true
|
|
41
|
+
rescue
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.use_dataset(dataset)
|
|
46
|
+
former_dataset = $current_dataset
|
|
47
|
+
$current_dataset = dataset
|
|
48
|
+
yield
|
|
49
|
+
ensure
|
|
50
|
+
$current_dataset = former_dataset
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
require "simple_master/active_record"
|
|
55
|
+
require "simple_master/loader"
|
|
56
|
+
require "simple_master/schema"
|
|
57
|
+
require "simple_master/storage"
|
|
58
|
+
require "simple_master/master"
|
|
59
|
+
|
|
60
|
+
class ActiveRecord::Associations::Preloader::Association
|
|
61
|
+
prepend SimpleMaster::ActiveRecord::PreloaderAssociationExtension
|
|
62
|
+
end
|