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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/simple_master/active_record/belongs_to_master_polymorphic_reflection.rb +11 -0
  3. data/lib/simple_master/active_record/belongs_to_polymorphic_association.rb +17 -0
  4. data/lib/simple_master/active_record/belongs_to_polymorphic_builder.rb +21 -0
  5. data/lib/simple_master/active_record/extension.rb +183 -0
  6. data/lib/simple_master/active_record/preloader_association_extension.rb +11 -0
  7. data/lib/simple_master/active_record.rb +12 -0
  8. data/lib/simple_master/loader/dataset_loader.rb +20 -0
  9. data/lib/simple_master/loader/marshal_loader.rb +15 -0
  10. data/lib/simple_master/loader/query_loader.rb +63 -0
  11. data/lib/simple_master/loader.rb +55 -0
  12. data/lib/simple_master/master/association/belongs_to_association.rb +79 -0
  13. data/lib/simple_master/master/association/belongs_to_polymorphic_association.rb +79 -0
  14. data/lib/simple_master/master/association/has_many_association.rb +53 -0
  15. data/lib/simple_master/master/association/has_many_through_association.rb +64 -0
  16. data/lib/simple_master/master/association/has_one_association.rb +57 -0
  17. data/lib/simple_master/master/association.rb +50 -0
  18. data/lib/simple_master/master/column/bitmask_column.rb +74 -0
  19. data/lib/simple_master/master/column/boolean_column.rb +51 -0
  20. data/lib/simple_master/master/column/enum_column.rb +96 -0
  21. data/lib/simple_master/master/column/float_column.rb +21 -0
  22. data/lib/simple_master/master/column/id_column.rb +31 -0
  23. data/lib/simple_master/master/column/integer_column.rb +21 -0
  24. data/lib/simple_master/master/column/json_column.rb +27 -0
  25. data/lib/simple_master/master/column/polymorphic_type_column.rb +44 -0
  26. data/lib/simple_master/master/column/sti_type_column.rb +21 -0
  27. data/lib/simple_master/master/column/string_column.rb +17 -0
  28. data/lib/simple_master/master/column/symbol_column.rb +23 -0
  29. data/lib/simple_master/master/column/time_column.rb +38 -0
  30. data/lib/simple_master/master/column.rb +138 -0
  31. data/lib/simple_master/master/dsl.rb +239 -0
  32. data/lib/simple_master/master/editable.rb +155 -0
  33. data/lib/simple_master/master/filterable.rb +47 -0
  34. data/lib/simple_master/master/queryable.rb +75 -0
  35. data/lib/simple_master/master/storable.rb +20 -0
  36. data/lib/simple_master/master/validatable.rb +216 -0
  37. data/lib/simple_master/master.rb +417 -0
  38. data/lib/simple_master/schema.rb +49 -0
  39. data/lib/simple_master/storage/dataset.rb +172 -0
  40. data/lib/simple_master/storage/ondemand_table.rb +68 -0
  41. data/lib/simple_master/storage/table.rb +197 -0
  42. data/lib/simple_master/storage/test_table.rb +69 -0
  43. data/lib/simple_master/storage.rb +11 -0
  44. data/lib/simple_master/version.rb +5 -0
  45. data/lib/simple_master.rb +62 -0
  46. metadata +128 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3767dce67967051739f25da6dc14604e647a65bca3d6a68579b54cc0bad58009
4
+ data.tar.gz: ad1ffecd0e636e7072d8f1d928717ca42090a1e01426a1e619fca6a056a1720f
5
+ SHA512:
6
+ metadata.gz: 28d28d28165fe704e8fa38b99a2a409b8c505c03b31366f2bb5ad7ad9bc033ca10c8e095ce725fe1b67e35cfc28da06f88d10c2dcb81545462f6cce911e2e21c
7
+ data.tar.gz: f92e380bbf3ff68fbd9686427d42c32f961342b73e4722f1a2634f6c2f2b72bcd97c919c33c76fdfa1f6afe208a8270b748b246eddedb6f6ee51d8e9baadd178
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ class BelongsToMasterPolymorphicReflection < ::ActiveRecord::Reflection::BelongsToReflection
6
+ def association_class
7
+ BelongsToPolymorphicAssociation
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ class BelongsToPolymorphicAssociation < ::ActiveRecord::Associations::BelongsToPolymorphicAssociation
6
+ def find_target(async: false)
7
+ klass = klass()
8
+ if klass < Master
9
+ foreign_key = owner.send(reflection.foreign_key)
10
+ klass.find(foreign_key)
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ class BelongsToPolymorphicBuilder < ::ActiveRecord::Associations::Builder::BelongsTo
6
+ def self.create_reflection(model, name, scope, options, &)
7
+ fail ArgumentError, "association names must be a Symbol" unless name.is_a?(Symbol)
8
+
9
+ validate_options(options)
10
+
11
+ extension = define_extensions(model, name, &)
12
+ options[:extend] = [*options[:extend], extension] if extension
13
+
14
+ scope = build_scope(scope)
15
+
16
+ fail "not implemented" if options[:through]
17
+ BelongsToMasterPolymorphicReflection.new(name, scope, options, model)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ module Extension
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ def master_dirty
10
+ @master_dirty ||= {}
11
+ end
12
+
13
+ def initialize_dup(*_)
14
+ @master_dirty = {}
15
+ super
16
+ end
17
+
18
+ class << self
19
+ def simple_master_connection
20
+ self
21
+ end
22
+
23
+ def simple_master_association_options
24
+ @simple_master_association_options ||= {}
25
+ end
26
+
27
+ def belongs_to(name, scope = nil, **options)
28
+ unless options[:polymorphic]
29
+ class_name = options[:class_name] || ActiveSupport::Inflector.classify(name)
30
+
31
+ klass = compute_type(class_name.to_s)
32
+ if klass < SimpleMaster::Master
33
+ return belongs_to_master(name, options)
34
+ else
35
+ return super
36
+ end
37
+ end
38
+
39
+ reflection = BelongsToPolymorphicBuilder.build(self, name, scope, options)
40
+ ::ActiveRecord::Reflection.add_reflection self, name, reflection
41
+ end
42
+
43
+ def belongs_to_master(name, options = EMPTY_HASH)
44
+ simple_master_association_options[name] = options.dup
45
+
46
+ class_name = options.delete(:class_name) || ActiveSupport::Inflector.classify(name)
47
+ foreign_key = options.delete(:foreign_key) || ActiveSupport::Inflector.foreign_key(name)
48
+ primary_key = options.delete(:primary_key) || :id
49
+
50
+ warn "Options not supported! #{options}" unless options.empty?
51
+
52
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
53
+ Module.new {
54
+ def #{name}
55
+ master_dirty[:#{name}] || #{class_name}.find_by_#{primary_key}(#{foreign_key})
56
+ end
57
+
58
+ def #{name}=(value)
59
+ if value == nil
60
+ self.#{foreign_key} = nil
61
+ master_dirty.delete(:#{name})
62
+ else
63
+ @_association_#{name}_source = self.#{foreign_key} = value.#{primary_key}
64
+
65
+ master_dirty[:#{name}] = value
66
+ end
67
+ end
68
+
69
+ def #{foreign_key}=(value)
70
+ master_dirty.delete(:#{name})
71
+ super
72
+ end
73
+ }.tap { |mod| include mod }
74
+
75
+ before_save do
76
+ if master_dirty[:#{name}]
77
+ master = master_dirty.delete(:#{name})
78
+ # Skip saving the association if the key was changed after assignment
79
+ if @_association_#{name}_source != #{foreign_key}
80
+ next
81
+ end
82
+ master.save
83
+ self.#{name} = master
84
+ end
85
+ end
86
+ RUBY
87
+ end
88
+
89
+ def has_one(name, scope = nil, **options)
90
+ return super if options[:through]
91
+
92
+ class_name = options[:class_name] || ActiveSupport::Inflector.classify(name)
93
+
94
+ klass = compute_type(class_name.to_s)
95
+ if klass < SimpleMaster::Master
96
+ has_one_master(name, options)
97
+ else
98
+ super
99
+ end
100
+ end
101
+
102
+ def has_one_master(name, options = EMPTY_HASH)
103
+ simple_master_association_options[name] = options.dup
104
+
105
+ class_name = options.delete(:class_name) || ActiveSupport::Inflector.classify(name)
106
+ foreign_key = options.delete(:foreign_key) || ActiveSupport::Inflector.foreign_key(to_s)
107
+ primary_key = options.delete(:primary_key) || :id
108
+
109
+ warn "Options not supported! #{options}" unless options.empty?
110
+
111
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
112
+ Module.new {
113
+ def #{name}
114
+ #{class_name}.find_by(:#{foreign_key}, #{primary_key})
115
+ end
116
+ }.tap { |mod| include mod }
117
+ RUBY
118
+ end
119
+
120
+ def has_many(name, scope = nil, **options)
121
+ return super if options[:through]
122
+
123
+ class_name = options[:class_name] || ActiveSupport::Inflector.classify(name)
124
+
125
+ klass = compute_type(class_name.to_s)
126
+ if klass < SimpleMaster::Master
127
+ has_many_master(name, options)
128
+ else
129
+ super
130
+ end
131
+ end
132
+
133
+ def has_many_master(name, options = EMPTY_HASH)
134
+ simple_master_association_options[name] = options.dup
135
+
136
+ class_name = options.delete(:class_name) || ActiveSupport::Inflector.classify(name)
137
+ foreign_key = options.delete(:foreign_key) || ActiveSupport::Inflector.foreign_key(to_s)
138
+ primary_key = options.delete(:primary_key) || :id
139
+ inverse_of = options.delete(:inverse_of)
140
+
141
+ warn "Options not supported! #{options}" unless options.empty?
142
+
143
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
144
+ Module.new {
145
+ def #{name}
146
+ master_dirty[:#{name}] || #{class_name}.all_by(:#{foreign_key}, #{primary_key})
147
+ end
148
+
149
+ def #{name}=(values)
150
+ master_dirty[:#{name}] = values
151
+ end
152
+ }.tap { |mod| include mod }
153
+ RUBY
154
+
155
+ if inverse_of
156
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
157
+ after_save do
158
+ if master_dirty[:#{name}]
159
+ master_dirty.delete(:#{name}).each do |master|
160
+ master.#{inverse_of} = self
161
+ master.save
162
+ end
163
+ end
164
+ end
165
+ RUBY
166
+ else
167
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
168
+ after_save do
169
+ if master_dirty[:#{name}]
170
+ master_dirty.delete(:#{name}).each do |master|
171
+ master.#{foreign_key} = self.id
172
+ master.save
173
+ end
174
+ end
175
+ end
176
+ RUBY
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ module PreloaderAssociationExtension
6
+ def run?
7
+ super || klass < SimpleMaster::Master
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ module ActiveRecord
5
+ end
6
+ end
7
+
8
+ require "simple_master/active_record/belongs_to_master_polymorphic_reflection"
9
+ require "simple_master/active_record/belongs_to_polymorphic_association"
10
+ require "simple_master/active_record/belongs_to_polymorphic_builder"
11
+ require "simple_master/active_record/extension"
12
+ require "simple_master/active_record/preloader_association_extension"
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Loader
5
+ class DatasetLoader < Loader
6
+ # options: dataset: ...
7
+ def read_raw(table)
8
+ dataset.table(table.klass)
9
+ end
10
+
11
+ def build_records(_klass, raw)
12
+ raw.all
13
+ end
14
+
15
+ def dataset
16
+ @options[:dataset]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Loader
5
+ class MarshalLoader < Loader
6
+ def read_raw(table)
7
+ File.read("#{@options[:path]}/#{table.klass.table_name}.marshal")
8
+ end
9
+
10
+ def build_records(_klass, raw)
11
+ Marshal.load(raw) # rubocop:disable Security/MarshalLoad
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Loader
5
+ class QueryLoader < Loader
6
+ def read_raw(table)
7
+ klass = table.klass
8
+ unless klass.table_available?
9
+ return { columns: EMPTY_ARRAY, rows: EMPTY_ARRAY }
10
+ end
11
+
12
+ result = klass.query_select_all
13
+
14
+ { columns: result.columns, rows: result.rows }
15
+ end
16
+
17
+ def build_records(klass, raw)
18
+ columns = raw[:columns].map(&:to_sym)
19
+
20
+ column_assign_methods = columns.map { :"#{_1}=" }
21
+ columns_hash = klass.columns_hash
22
+ sti_column_index = columns.find_index { columns_hash[_1].is_a?(Master::Column::StiTypeColumn) }
23
+
24
+ columns.each do |column_name|
25
+ unless klass.method_defined?(:"#{column_name}=")
26
+ if ENV["RAILS_ENV"] == "development"
27
+ # In local/dev, define a no-op setter so loading does not raise
28
+ klass.define_method(:"#{column_name}=", &:itself)
29
+ warn "#{klass}.#{column_name} column is not defined!"
30
+ else
31
+ fail "#{klass}.#{column_name} column is not defined!"
32
+ end
33
+ end
34
+ end
35
+
36
+ raw[:rows].filter_map { |row|
37
+ begin
38
+ record =
39
+ if sti_column_index
40
+ child_klass = ActiveSupport::Inflector.constantize(row[sti_column_index])
41
+ if child_klass == klass || child_klass.sti_base_class == klass
42
+ child_klass.new
43
+ else
44
+ warn "[#{klass}] Invalid value in the type column: #{row}"
45
+ next
46
+ end
47
+ else
48
+ klass.new
49
+ end
50
+
51
+ column_assign_methods.zip(row) do |assign_method, value|
52
+ record.send(assign_method, value)
53
+ end
54
+
55
+ record
56
+ rescue
57
+ nil
58
+ end
59
+ }.freeze
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Loader
5
+ attr_reader :options
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ end
10
+
11
+ # Returns if records are updated in table.
12
+ def load_records(table)
13
+ klass = table.klass
14
+ fail "Load target not available." unless klass.base_class?
15
+
16
+ raw = read_raw(table)
17
+
18
+ new_digest = table.diff.nil? ? raw.hash : "#{raw.hash}/#{table.diff.hash}"
19
+ if new_digest == table.digest
20
+ return false
21
+ end
22
+
23
+ table.digest = new_digest
24
+ table.all = build_records(klass, raw).freeze
25
+ globalize(table)
26
+
27
+ table.apply_diff
28
+
29
+ table.update_sub_tables
30
+
31
+ klass.reset_object_cache
32
+
33
+ true
34
+ end
35
+
36
+ # Interface for loader loading raw data, for building records and table digest.
37
+ def read_raw(table)
38
+ fail NotImplementedError
39
+ end
40
+
41
+ # Interface for building records from raw data.
42
+ def build_records(klass, raw)
43
+ fail NotImplementedError
44
+ end
45
+
46
+ def globalize(table)
47
+ return unless options[:globalize_proc]
48
+ table.all = options[:globalize_proc].call(table.klass, table.all)
49
+ end
50
+ end
51
+ end
52
+
53
+ require "simple_master/loader/dataset_loader"
54
+ require "simple_master/loader/marshal_loader"
55
+ require "simple_master/loader/query_loader"
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Master
5
+ class Association
6
+ class BelongsToAssociation < self
7
+ def foreign_key
8
+ @foreign_key ||= (options[:foreign_key] || ActiveSupport::Inflector.foreign_key(name)).to_sym
9
+ end
10
+
11
+ def primary_key
12
+ @primary_key ||= options[:primary_key] || target_class.primary_key || :id
13
+ end
14
+
15
+ def init(master_class)
16
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
17
+ if #{target_class} < SimpleMaster::Master
18
+ def #{name}
19
+ return nil unless #{foreign_key}
20
+
21
+ #{search_code}
22
+ end
23
+ else
24
+ def #{name}
25
+ return nil unless #{foreign_key}
26
+
27
+ belongs_to_store[:#{name}] ||= #{target_class}.simple_master_connection.find_by_#{primary_key}(#{foreign_key})
28
+ end
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ def init_for_test(master_class)
34
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
35
+ if #{target_class} < SimpleMaster::Master
36
+ def #{name}
37
+ return belongs_to_store[:#{name}] if belongs_to_store[:#{name}]
38
+ return nil unless #{foreign_key}
39
+
40
+ #{search_code}
41
+ end
42
+ else
43
+ def #{name}
44
+ return nil unless #{foreign_key}
45
+
46
+ belongs_to_store[:#{name}] ||= #{target_class}.simple_master_connection.find_by_#{primary_key}(#{foreign_key})
47
+ end
48
+ end
49
+
50
+ def #{name}=(value)
51
+ @_association_#{name}_source = self.#{foreign_key} = value&.#{primary_key}
52
+ belongs_to_store[:#{name}] = value
53
+ end
54
+
55
+ def _#{name}_target_save?
56
+ # Skip saving the association if the key changed after assignment
57
+ return false if @_association_#{name}_source != #{foreign_key}
58
+
59
+ target = belongs_to_store[:#{name}]
60
+ return false if target.nil?
61
+
62
+ true
63
+ end
64
+ RUBY
65
+ end
66
+
67
+ private
68
+
69
+ def search_code
70
+ if primary_key == :id
71
+ "#{target_class}.find_by_id(#{foreign_key})"
72
+ else
73
+ "#{target_class}.find_by(:#{primary_key}, #{foreign_key})"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Master
5
+ class Association
6
+ class BelongsToPolymorphicAssociation < self
7
+ def foreign_key
8
+ @foreign_key ||= (options[:foreign_key] || ActiveSupport::Inflector.foreign_key(name)).to_sym
9
+ end
10
+
11
+ def foreign_type
12
+ @foreign_type ||= options[:foreign_type] || :"#{name}_type"
13
+ end
14
+
15
+ def foreign_type_class
16
+ @foreign_type_class ||= :"#{foreign_type}_class"
17
+ end
18
+
19
+ def is_active_record?
20
+ # target_class will be dynamically loaded.
21
+ false
22
+ end
23
+
24
+ def target_class
25
+ fail
26
+ end
27
+
28
+ def init(master_class)
29
+ unless master_class.method_defined?(foreign_type_class)
30
+ fail "[#{master_class}] Please specify `polymorphic_type: true` on polymorphic type column <#{name}>."
31
+ end
32
+
33
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
34
+ def #{name}
35
+ return nil if #{foreign_type_class}.nil?
36
+
37
+ if #{foreign_type_class} < SimpleMaster::Master
38
+ #{foreign_type_class}.find_by_id(#{foreign_key})
39
+ else
40
+ belongs_to_store[:#{name}] ||= #{foreign_type_class}.simple_master_connection.find_by(id: #{foreign_key})
41
+ end
42
+ end
43
+ RUBY
44
+ end
45
+
46
+ def init_for_test(master_class)
47
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
48
+ def #{name}
49
+ return nil if #{foreign_type_class}.nil?
50
+
51
+ if #{foreign_type_class} < SimpleMaster::Master
52
+ belongs_to_store[:#{name}] || #{foreign_type_class}.find_by_id(#{foreign_key})
53
+ else
54
+ belongs_to_store[:#{name}] ||= #{foreign_type_class}.simple_master_connection.find_by(id: #{foreign_key})
55
+ end
56
+ end
57
+
58
+ def #{name}=(value)
59
+ @_association_#{name}_source = self.#{foreign_key} = value&.id
60
+ @_association_#{name}_class_source = self.#{foreign_type_class} = value&.class
61
+ belongs_to_store[:#{name}] = value
62
+ end
63
+
64
+ def _#{name}_target_save?
65
+ # Skip saving the association if the key changed after assignment
66
+ return false if @_association_#{name}_source != #{foreign_key}
67
+ return false if @_association_#{name}_class_source != #{foreign_type_class}
68
+
69
+ target = belongs_to_store[:#{name}]
70
+ return false if target.nil?
71
+
72
+ true
73
+ end
74
+ RUBY
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleMaster
4
+ class Master
5
+ class Association
6
+ class HasManyAssociation < self
7
+ def foreign_key
8
+ @foreign_key ||= (options[:foreign_key] || ActiveSupport::Inflector.foreign_key(defined_at.to_s)).to_sym
9
+ end
10
+
11
+ def inverse_of
12
+ @inverse_of ||= options[:inverse_of] || ActiveSupport::Inflector.underscore(defined_at.to_s)
13
+ end
14
+
15
+ def primary_key
16
+ @primary_key ||= options[:primary_key] || defined_at.primary_key || :id
17
+ end
18
+
19
+ def init(master_class)
20
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
+ if #{target_class} < SimpleMaster::Master
22
+ def #{name}
23
+ #{target_class}.all_by(:#{foreign_key}, #{primary_key})
24
+ end
25
+ else
26
+ def #{name}
27
+ has_many_store[:#{name}] ||= #{target_class}.simple_master_connection.where(:#{foreign_key} => #{primary_key}).to_a
28
+ end
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ def init_for_test(master_class)
34
+ master_class.simple_master_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
35
+ if #{target_class} < SimpleMaster::Master
36
+ def #{name}
37
+ has_many_store[:#{name}] || #{target_class}.all_by(:#{foreign_key}, #{primary_key})
38
+ end
39
+ else
40
+ def #{name}
41
+ has_many_store[:#{name}] ||= #{target_class}.simple_master_connection.where(:#{foreign_key} => #{primary_key}).to_a
42
+ end
43
+ end
44
+
45
+ def #{name}=(values)
46
+ has_many_store[:#{name}] = values
47
+ end
48
+ RUBY
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end