mr 0.35.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 +7 -0
- data/.gitignore +19 -0
- data/Gemfile +13 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/bench/all.rb +4 -0
- data/bench/factory.rb +68 -0
- data/bench/fake_record.rb +174 -0
- data/bench/model.rb +201 -0
- data/bench/read_model.rb +191 -0
- data/bench/results/factory.txt +21 -0
- data/bench/results/fake_record.txt +37 -0
- data/bench/results/model.txt +44 -0
- data/bench/results/read_model.txt +46 -0
- data/bench/setup.rb +132 -0
- data/lib/mr.rb +11 -0
- data/lib/mr/after_commit.rb +49 -0
- data/lib/mr/after_commit/fake_record.rb +39 -0
- data/lib/mr/after_commit/record.rb +48 -0
- data/lib/mr/after_commit/record_procs_methods.rb +82 -0
- data/lib/mr/factory.rb +82 -0
- data/lib/mr/factory/config.rb +240 -0
- data/lib/mr/factory/model_factory.rb +103 -0
- data/lib/mr/factory/model_stack.rb +28 -0
- data/lib/mr/factory/read_model_factory.rb +104 -0
- data/lib/mr/factory/record_factory.rb +130 -0
- data/lib/mr/factory/record_stack.rb +219 -0
- data/lib/mr/fake_query.rb +53 -0
- data/lib/mr/fake_record.rb +58 -0
- data/lib/mr/fake_record/associations.rb +257 -0
- data/lib/mr/fake_record/attributes.rb +168 -0
- data/lib/mr/fake_record/persistence.rb +116 -0
- data/lib/mr/json_field.rb +180 -0
- data/lib/mr/json_field/fake_record.rb +31 -0
- data/lib/mr/json_field/record.rb +38 -0
- data/lib/mr/model.rb +67 -0
- data/lib/mr/model/associations.rb +161 -0
- data/lib/mr/model/configuration.rb +67 -0
- data/lib/mr/model/fields.rb +177 -0
- data/lib/mr/model/persistence.rb +79 -0
- data/lib/mr/query.rb +126 -0
- data/lib/mr/read_model.rb +83 -0
- data/lib/mr/read_model/data.rb +38 -0
- data/lib/mr/read_model/fields.rb +218 -0
- data/lib/mr/read_model/query_expression.rb +188 -0
- data/lib/mr/read_model/querying.rb +214 -0
- data/lib/mr/read_model/set_querying.rb +82 -0
- data/lib/mr/read_model/subquery.rb +98 -0
- data/lib/mr/record.rb +35 -0
- data/lib/mr/test_helpers.rb +229 -0
- data/lib/mr/type_converter.rb +85 -0
- data/lib/mr/version.rb +3 -0
- data/log/.gitkeep +0 -0
- data/mr.gemspec +29 -0
- data/test/helper.rb +21 -0
- data/test/support/db.rb +10 -0
- data/test/support/factory.rb +13 -0
- data/test/support/factory/area.rb +6 -0
- data/test/support/factory/comment.rb +14 -0
- data/test/support/factory/image.rb +6 -0
- data/test/support/factory/user.rb +6 -0
- data/test/support/models/area.rb +58 -0
- data/test/support/models/comment.rb +60 -0
- data/test/support/models/image.rb +53 -0
- data/test/support/models/user.rb +96 -0
- data/test/support/read_model/querying.rb +150 -0
- data/test/support/read_models/comment_with_user_data.rb +27 -0
- data/test/support/read_models/set_data.rb +49 -0
- data/test/support/read_models/subquery_data.rb +41 -0
- data/test/support/read_models/user_with_area_data.rb +15 -0
- data/test/support/schema.rb +39 -0
- data/test/support/setup_test_db.rb +10 -0
- data/test/system/factory/model_factory_tests.rb +87 -0
- data/test/system/factory/model_stack_tests.rb +30 -0
- data/test/system/factory/record_factory_tests.rb +84 -0
- data/test/system/factory/record_stack_tests.rb +51 -0
- data/test/system/factory_tests.rb +32 -0
- data/test/system/read_model_tests.rb +199 -0
- data/test/system/with_model_tests.rb +275 -0
- data/test/unit/after_commit/fake_record_tests.rb +110 -0
- data/test/unit/after_commit/record_procs_methods_tests.rb +177 -0
- data/test/unit/after_commit/record_tests.rb +134 -0
- data/test/unit/after_commit_tests.rb +113 -0
- data/test/unit/factory/config_tests.rb +651 -0
- data/test/unit/factory/model_factory_tests.rb +473 -0
- data/test/unit/factory/model_stack_tests.rb +97 -0
- data/test/unit/factory/read_model_factory_tests.rb +195 -0
- data/test/unit/factory/record_factory_tests.rb +446 -0
- data/test/unit/factory/record_stack_tests.rb +549 -0
- data/test/unit/factory_tests.rb +213 -0
- data/test/unit/fake_query_tests.rb +137 -0
- data/test/unit/fake_record/associations_tests.rb +585 -0
- data/test/unit/fake_record/attributes_tests.rb +265 -0
- data/test/unit/fake_record/persistence_tests.rb +239 -0
- data/test/unit/fake_record_tests.rb +106 -0
- data/test/unit/json_field/fake_record_tests.rb +75 -0
- data/test/unit/json_field/record_tests.rb +80 -0
- data/test/unit/json_field_tests.rb +302 -0
- data/test/unit/model/associations_tests.rb +346 -0
- data/test/unit/model/configuration_tests.rb +92 -0
- data/test/unit/model/fields_tests.rb +278 -0
- data/test/unit/model/persistence_tests.rb +114 -0
- data/test/unit/model_tests.rb +137 -0
- data/test/unit/query_tests.rb +300 -0
- data/test/unit/read_model/data_tests.rb +56 -0
- data/test/unit/read_model/fields_tests.rb +416 -0
- data/test/unit/read_model/query_expression_tests.rb +381 -0
- data/test/unit/read_model/querying_tests.rb +613 -0
- data/test/unit/read_model/set_querying_tests.rb +149 -0
- data/test/unit/read_model/subquery_tests.rb +242 -0
- data/test/unit/read_model_tests.rb +187 -0
- data/test/unit/record_tests.rb +45 -0
- data/test/unit/test_helpers_tests.rb +431 -0
- data/test/unit/type_converter_tests.rb +207 -0
- metadata +285 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'mr/factory'
|
2
|
+
require 'mr/factory/config'
|
3
|
+
require 'mr/factory/model_stack'
|
4
|
+
require 'mr/factory/record_factory'
|
5
|
+
require 'mr/fake_record'
|
6
|
+
|
7
|
+
module MR; end
|
8
|
+
module MR::Factory
|
9
|
+
|
10
|
+
class ModelFactory
|
11
|
+
|
12
|
+
attr_reader :model_class, :record_class, :config
|
13
|
+
|
14
|
+
def initialize(model_class, record_class, &block)
|
15
|
+
@model_class = model_class
|
16
|
+
@record_class = record_class
|
17
|
+
@config = Config.new(@record_class)
|
18
|
+
self.instance_eval(&block) if block
|
19
|
+
|
20
|
+
@record_factory = MR::Factory::RecordFactory.new(record_class)
|
21
|
+
end
|
22
|
+
|
23
|
+
def model(args = nil)
|
24
|
+
@model_class.new(@record_factory.record).tap do |model|
|
25
|
+
self.config.apply_args(model, args || {})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias :instance :model
|
29
|
+
|
30
|
+
def saved_model(args = nil)
|
31
|
+
model = self.model(args).tap(&:save)
|
32
|
+
model.record.reset_save_called if model.record.kind_of?(MR::FakeRecord)
|
33
|
+
model
|
34
|
+
end
|
35
|
+
alias :saved_instance :saved_model
|
36
|
+
|
37
|
+
def stack(args = nil)
|
38
|
+
MR::Factory::ModelStack.new(self.model(args), self.config)
|
39
|
+
end
|
40
|
+
alias :instance_stack :stack
|
41
|
+
|
42
|
+
def stack_model(args = nil)
|
43
|
+
self.stack(args).model
|
44
|
+
end
|
45
|
+
|
46
|
+
def saved_stack(args = nil)
|
47
|
+
self.stack(args).tap(&:create)
|
48
|
+
end
|
49
|
+
|
50
|
+
def saved_stack_model(args = nil)
|
51
|
+
self.saved_stack(args).model
|
52
|
+
end
|
53
|
+
|
54
|
+
def saved_dependencies_stack(args = nil)
|
55
|
+
self.stack(args).tap(&:create_deps)
|
56
|
+
end
|
57
|
+
alias :saved_deps_stack :saved_dependencies_stack
|
58
|
+
|
59
|
+
def saved_dependencies_stack_model(args = nil)
|
60
|
+
self.saved_deps_stack(args).model
|
61
|
+
end
|
62
|
+
alias :saved_deps_stack_model :saved_dependencies_stack_model
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def default_association(association_name, factory, options = nil)
|
67
|
+
self.config.add_association_factory(association_name, factory, options)
|
68
|
+
rescue NoAssociationError => exception
|
69
|
+
raise exception.class, exception.message, caller
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_args(&block)
|
73
|
+
self.config.set_default_args(&block)
|
74
|
+
end
|
75
|
+
|
76
|
+
class Config
|
77
|
+
include MR::Factory::WithAssociationsConfig
|
78
|
+
|
79
|
+
def build_associated_model(association_name, record_class)
|
80
|
+
self.factory_for(association_name, record_class).model
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_associated_record(association_name, record_class)
|
84
|
+
self.build_associated_model(association_name, record_class).record
|
85
|
+
end
|
86
|
+
|
87
|
+
def ar_association_for(model, name)
|
88
|
+
if (reflection = model.record_class.reflect_on_association(name))
|
89
|
+
model.record.association(reflection.name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def build_factory_for_record_class(record_class)
|
96
|
+
MR::Factory::ModelFactory.new(record_class.model_class, record_class)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mr/factory/record_factory'
|
2
|
+
require 'mr/factory/record_stack'
|
3
|
+
|
4
|
+
module MR; end
|
5
|
+
module MR::Factory
|
6
|
+
|
7
|
+
class ModelStack
|
8
|
+
|
9
|
+
attr_reader :model
|
10
|
+
|
11
|
+
def initialize(model, factory_config)
|
12
|
+
@model = model
|
13
|
+
@record_stack = MR::Factory::RecordStack.for_record(model.record, factory_config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def factory_config; @record_stack.factory_config; end
|
17
|
+
|
18
|
+
def create; @record_stack.create; end
|
19
|
+
def destroy; @record_stack.destroy; end
|
20
|
+
|
21
|
+
def create_dependencies; @record_stack.create_dependencies; end
|
22
|
+
alias :create_deps :create_dependencies
|
23
|
+
|
24
|
+
def destroy_dependencies; @record_stack.destroy_dependencies; end
|
25
|
+
alias :destroy_deps :destroy_dependencies
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'mr/factory'
|
2
|
+
require 'mr/factory/config'
|
3
|
+
require 'mr/read_model'
|
4
|
+
|
5
|
+
module MR; end
|
6
|
+
module MR::Factory
|
7
|
+
|
8
|
+
class ReadModelFactory
|
9
|
+
|
10
|
+
attr_reader :read_model_class, :config
|
11
|
+
|
12
|
+
def initialize(read_model_class, &block)
|
13
|
+
unless read_model_class < MR::ReadModelStruct
|
14
|
+
raise ArgumentError, "takes a read model or read model struct"
|
15
|
+
end
|
16
|
+
@read_model_class = read_model_class
|
17
|
+
@config = Config.new(read_model_class)
|
18
|
+
self.instance_eval(&block) if block
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_model_data(args = nil)
|
22
|
+
Data.new.tap do |read_model_data|
|
23
|
+
self.config.apply_args(read_model_data, args || {})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_model(args = nil)
|
28
|
+
@read_model_class.new(self.read_model_data(args))
|
29
|
+
end
|
30
|
+
alias :instance :read_model
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def default_args(&block)
|
35
|
+
self.config.set_default_args(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
class Config
|
39
|
+
include MR::Factory::Config
|
40
|
+
|
41
|
+
def read_model_class
|
42
|
+
self.object_class
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def apply_default_args(data)
|
48
|
+
field_defaults.each do |field_name, field_type|
|
49
|
+
data[field_name] = MR::Factory.send(field_type)
|
50
|
+
end
|
51
|
+
json_struct_list_defaults.each do |field_name, factory|
|
52
|
+
data[field_name] = Factory.integer(3).times.map{ factory.read_model_data }
|
53
|
+
end
|
54
|
+
json_struct_obj_defaults.each do |field_name, factory|
|
55
|
+
data[field_name] = factory.read_model_data
|
56
|
+
end
|
57
|
+
super(data)
|
58
|
+
end
|
59
|
+
|
60
|
+
def field_defaults
|
61
|
+
@field_defaults ||= self.read_model_class.fields.inject({}) do |h, field|
|
62
|
+
h.merge!(field.name.to_s => field.type)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def json_struct_list_defaults
|
67
|
+
@json_struct_list_defaults ||= build_defaults_for_json_struct_fields(
|
68
|
+
self.read_model_class.json_struct_lists
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def json_struct_obj_defaults
|
73
|
+
@json_struct_obj_defaults ||= build_defaults_for_json_struct_fields(
|
74
|
+
self.read_model_class.json_struct_objs
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_defaults_for_json_struct_fields(fields)
|
79
|
+
fields.inject({}) do |h, field|
|
80
|
+
factory = MR::Factory::ReadModelFactory.new(field.struct_class)
|
81
|
+
h.merge!(field.name.to_s => factory)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Data
|
87
|
+
def initialize; @hash = {}; end
|
88
|
+
|
89
|
+
def [](key); @hash[key]; end
|
90
|
+
def []=(key, value); @hash[key] = value; end
|
91
|
+
|
92
|
+
def method_missing(method, *args, &block)
|
93
|
+
method_string = method.to_s
|
94
|
+
if method_string =~ /=\z/ && args.size == 1
|
95
|
+
@hash[method_string.gsub('=', '')] = args.first
|
96
|
+
else
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'mr/factory'
|
2
|
+
require 'mr/factory/config'
|
3
|
+
require 'mr/factory/record_stack'
|
4
|
+
require 'mr/fake_record'
|
5
|
+
|
6
|
+
module MR; end
|
7
|
+
module MR::Factory
|
8
|
+
|
9
|
+
class RecordFactory
|
10
|
+
|
11
|
+
attr_reader :record_class, :config
|
12
|
+
|
13
|
+
def initialize(record_class, &block)
|
14
|
+
@record_class = record_class
|
15
|
+
@config = Config.new(record_class)
|
16
|
+
self.instance_eval(&block) if block
|
17
|
+
end
|
18
|
+
|
19
|
+
def record(args = nil)
|
20
|
+
@record_class.new.tap do |record|
|
21
|
+
self.config.apply_args(record, args || {})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
alias :instance :record
|
25
|
+
|
26
|
+
def saved_record(args = nil)
|
27
|
+
record = self.record(args).tap(&:save!)
|
28
|
+
record.reset_save_called if record.kind_of?(MR::FakeRecord)
|
29
|
+
record
|
30
|
+
end
|
31
|
+
alias :saved_instance :saved_record
|
32
|
+
|
33
|
+
def stack(args = nil)
|
34
|
+
MR::Factory::RecordStack.for_record(self.record(args), self.config)
|
35
|
+
end
|
36
|
+
alias :instance_stack :stack
|
37
|
+
|
38
|
+
def stack_record(args = nil)
|
39
|
+
self.stack(args).record
|
40
|
+
end
|
41
|
+
|
42
|
+
def saved_stack(args = nil)
|
43
|
+
self.stack(args).tap(&:create)
|
44
|
+
end
|
45
|
+
|
46
|
+
def saved_stack_record(args = nil)
|
47
|
+
self.saved_stack(args).record
|
48
|
+
end
|
49
|
+
|
50
|
+
def saved_dependencies_stack(args = nil)
|
51
|
+
self.stack(args).tap(&:create_deps)
|
52
|
+
end
|
53
|
+
alias :saved_deps_stack :saved_dependencies_stack
|
54
|
+
|
55
|
+
def saved_dependencies_stack_record(args = nil)
|
56
|
+
self.saved_deps_stack(args).record
|
57
|
+
end
|
58
|
+
alias :saved_deps_stack_record :saved_dependencies_stack_record
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def default_association(association_name, factory, options = nil)
|
63
|
+
self.config.add_association_factory(association_name, factory, options)
|
64
|
+
rescue NoAssociationError => exception
|
65
|
+
raise exception.class, exception.message, caller
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_args(&block)
|
69
|
+
self.config.set_default_args(&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
class Config
|
73
|
+
include MR::Factory::WithAssociationsConfig
|
74
|
+
|
75
|
+
def build_associated_record(association_name, record_class)
|
76
|
+
self.factory_for(association_name, record_class).record
|
77
|
+
end
|
78
|
+
|
79
|
+
def ar_association_for(record, name)
|
80
|
+
if (reflection = record.class.reflect_on_association(name))
|
81
|
+
record.association(reflection.name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def apply_default_args(record)
|
88
|
+
column_defaults.each do |column_name, column_type|
|
89
|
+
record.send("#{column_name}=", MR::Factory.send(column_type))
|
90
|
+
end
|
91
|
+
super(record)
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_factory_for_record_class(record_class)
|
95
|
+
MR::Factory::RecordFactory.new(record_class)
|
96
|
+
end
|
97
|
+
|
98
|
+
def column_defaults
|
99
|
+
@column_defaults ||= begin
|
100
|
+
association_column_names = association_column_names(self.record_class)
|
101
|
+
self.record_class.columns.inject({}) do |args, column|
|
102
|
+
column_type = column.type || column.sql_type
|
103
|
+
if should_default_column?(column, association_column_names, column_type)
|
104
|
+
args[column.name.to_s] = column_type
|
105
|
+
end
|
106
|
+
args
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def should_default_column?(column, association_column_names, column_type)
|
112
|
+
!column.primary &&
|
113
|
+
!association_column_names.include?(column.name) &&
|
114
|
+
!column_type.nil? &&
|
115
|
+
MR::Factory.respond_to?(column_type)
|
116
|
+
end
|
117
|
+
|
118
|
+
def association_column_names(record_class)
|
119
|
+
names = []
|
120
|
+
record_class.reflect_on_all_associations(:belongs_to).each do |reflection|
|
121
|
+
names << reflection.foreign_type if reflection.options[:polymorphic]
|
122
|
+
names << reflection.foreign_key
|
123
|
+
end
|
124
|
+
names
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'mr/factory'
|
2
|
+
require 'mr/fake_record'
|
3
|
+
|
4
|
+
module MR; end
|
5
|
+
module MR::Factory
|
6
|
+
|
7
|
+
class RecordStack
|
8
|
+
|
9
|
+
def self.for_record(record, factory_config)
|
10
|
+
self.new(RecordData.new(record), factory_config)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :record_data, :factory_config, :record_lookup
|
14
|
+
attr_reader :record_stacks
|
15
|
+
|
16
|
+
def initialize(record_data, factory_config, record_lookup = nil)
|
17
|
+
@record_data = record_data
|
18
|
+
@factory_config = factory_config
|
19
|
+
@record_lookup = record_lookup || build_lookup(@record_data)
|
20
|
+
|
21
|
+
@record_stacks = @record_data.association_datas.map do |association_data|
|
22
|
+
next unless association_data.required?(factory_config)
|
23
|
+
|
24
|
+
associated_record_data = association_data.get_record_data(
|
25
|
+
@factory_config,
|
26
|
+
@record_lookup
|
27
|
+
)
|
28
|
+
associated_factory_config = @factory_config.factory_config_for(
|
29
|
+
association_data.name,
|
30
|
+
associated_record_data.record_class
|
31
|
+
)
|
32
|
+
@record_lookup[associated_record_data.record_class] ||= associated_record_data
|
33
|
+
@record_data.set_association(association_data.name, associated_record_data)
|
34
|
+
MR::Factory::RecordStack.new(
|
35
|
+
associated_record_data,
|
36
|
+
associated_factory_config,
|
37
|
+
@record_lookup
|
38
|
+
)
|
39
|
+
end.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
def record
|
43
|
+
self.record_data.record
|
44
|
+
end
|
45
|
+
|
46
|
+
def create
|
47
|
+
self.create_dependencies
|
48
|
+
self.record_data.create_record
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy
|
53
|
+
self.record_data.destroy_record
|
54
|
+
self.destroy_dependencies
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_dependencies
|
59
|
+
self.record_stacks.each(&:create)
|
60
|
+
self.record_data.refresh_record_associations
|
61
|
+
true
|
62
|
+
end
|
63
|
+
alias :create_deps :create_dependencies
|
64
|
+
|
65
|
+
def destroy_dependencies
|
66
|
+
self.record_stacks.each(&:destroy)
|
67
|
+
true
|
68
|
+
end
|
69
|
+
alias :destroy_deps :destroy_dependencies
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def build_lookup(record_data)
|
74
|
+
load_preset_associations_into_lookup({}, record_data)
|
75
|
+
end
|
76
|
+
|
77
|
+
def load_preset_associations_into_lookup(lookup, record_data)
|
78
|
+
record_data.association_datas.select(&:preset?).each do |association_data|
|
79
|
+
preset_record_data = association_data.preset_record_data
|
80
|
+
lookup[preset_record_data.record_class] ||= preset_record_data
|
81
|
+
load_preset_associations_into_lookup(lookup, preset_record_data)
|
82
|
+
end
|
83
|
+
lookup
|
84
|
+
end
|
85
|
+
|
86
|
+
class RecordData
|
87
|
+
|
88
|
+
attr_reader :record, :association_datas
|
89
|
+
|
90
|
+
def initialize(record)
|
91
|
+
@record = record
|
92
|
+
@association_datas = build_association_datas(@record)
|
93
|
+
end
|
94
|
+
|
95
|
+
def record_class
|
96
|
+
@record.class
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_association(name, record_data)
|
100
|
+
@record.send("#{name}=", record_data.record)
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_record
|
104
|
+
if @record.new_record?
|
105
|
+
@record.save!
|
106
|
+
@record.reset_save_called if @record.kind_of?(MR::FakeRecord)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def destroy_record
|
111
|
+
@record.destroy unless @record.destroyed?
|
112
|
+
end
|
113
|
+
|
114
|
+
# ensures records have their associations foreign type/key attributes set
|
115
|
+
# by re-setting the association to its current value
|
116
|
+
def refresh_record_associations
|
117
|
+
@association_datas.each do |association_data|
|
118
|
+
associated_record = @record.send(association_data.name)
|
119
|
+
@record.send("#{association_data.name}=", associated_record)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def build_association_datas(record)
|
126
|
+
record.class.reflect_on_all_associations(:belongs_to).map do |reflection|
|
127
|
+
AssociationData.new(record.association(reflection.name))
|
128
|
+
end.compact.sort
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
class AssociationData
|
134
|
+
|
135
|
+
attr_reader :association, :preset_record_data
|
136
|
+
|
137
|
+
def initialize(association)
|
138
|
+
@association = association
|
139
|
+
|
140
|
+
preset_record = association.owner.send("#{self.name}")
|
141
|
+
@preset_record_data = RecordData.new(preset_record) if preset_record
|
142
|
+
end
|
143
|
+
|
144
|
+
def name
|
145
|
+
self.association.reflection.name
|
146
|
+
end
|
147
|
+
|
148
|
+
def record_class
|
149
|
+
self.association.klass
|
150
|
+
end
|
151
|
+
|
152
|
+
def preset?
|
153
|
+
!self.preset_record_data.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
def required?(factory_config)
|
157
|
+
self.preset? ||
|
158
|
+
factory_config.force_in_stack_association?(self.name) ||
|
159
|
+
columns_required?(self.association)
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_record_data(factory_config, lookup)
|
163
|
+
if self.preset?
|
164
|
+
self.preset_record_data
|
165
|
+
elsif (lookup_record_data = get_lookup_record_data(factory_config, lookup))
|
166
|
+
lookup_record_data
|
167
|
+
else
|
168
|
+
RecordData.new(build_record(factory_config))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def <=>(other)
|
173
|
+
if other.kind_of?(self.class)
|
174
|
+
self.name.to_s <=> other.name.to_s
|
175
|
+
else
|
176
|
+
super
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
# get the intersection of the record classes and use a random match to
|
183
|
+
# get a record data from the lookup; if their are no matching record
|
184
|
+
# classes the intersection returns an empty array which will return a
|
185
|
+
# `nil` value from the lookup
|
186
|
+
def get_lookup_record_data(factory_config, lookup)
|
187
|
+
record_classes = lookup_record_classes(factory_config) & lookup.keys
|
188
|
+
lookup[record_classes.sample]
|
189
|
+
end
|
190
|
+
|
191
|
+
def lookup_record_classes(factory_config)
|
192
|
+
if self.record_class
|
193
|
+
[self.record_class]
|
194
|
+
else
|
195
|
+
factory_config.record_classes_for(self.name)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def build_record(factory_config)
|
200
|
+
factory_config.build_associated_record(self.name, self.record_class)
|
201
|
+
rescue NoRecordClassError
|
202
|
+
raise NoRecordClassError.for_association(self.association)
|
203
|
+
end
|
204
|
+
|
205
|
+
def columns_required?(association)
|
206
|
+
column_required?(association.owner, association.reflection.foreign_key) ||
|
207
|
+
column_required?(association.owner, association.reflection.foreign_type)
|
208
|
+
end
|
209
|
+
|
210
|
+
def column_required?(record, column_name)
|
211
|
+
column = record.column_for_attribute(column_name)
|
212
|
+
!(column.nil? || column.null)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|