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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/bench/all.rb +4 -0
  7. data/bench/factory.rb +68 -0
  8. data/bench/fake_record.rb +174 -0
  9. data/bench/model.rb +201 -0
  10. data/bench/read_model.rb +191 -0
  11. data/bench/results/factory.txt +21 -0
  12. data/bench/results/fake_record.txt +37 -0
  13. data/bench/results/model.txt +44 -0
  14. data/bench/results/read_model.txt +46 -0
  15. data/bench/setup.rb +132 -0
  16. data/lib/mr.rb +11 -0
  17. data/lib/mr/after_commit.rb +49 -0
  18. data/lib/mr/after_commit/fake_record.rb +39 -0
  19. data/lib/mr/after_commit/record.rb +48 -0
  20. data/lib/mr/after_commit/record_procs_methods.rb +82 -0
  21. data/lib/mr/factory.rb +82 -0
  22. data/lib/mr/factory/config.rb +240 -0
  23. data/lib/mr/factory/model_factory.rb +103 -0
  24. data/lib/mr/factory/model_stack.rb +28 -0
  25. data/lib/mr/factory/read_model_factory.rb +104 -0
  26. data/lib/mr/factory/record_factory.rb +130 -0
  27. data/lib/mr/factory/record_stack.rb +219 -0
  28. data/lib/mr/fake_query.rb +53 -0
  29. data/lib/mr/fake_record.rb +58 -0
  30. data/lib/mr/fake_record/associations.rb +257 -0
  31. data/lib/mr/fake_record/attributes.rb +168 -0
  32. data/lib/mr/fake_record/persistence.rb +116 -0
  33. data/lib/mr/json_field.rb +180 -0
  34. data/lib/mr/json_field/fake_record.rb +31 -0
  35. data/lib/mr/json_field/record.rb +38 -0
  36. data/lib/mr/model.rb +67 -0
  37. data/lib/mr/model/associations.rb +161 -0
  38. data/lib/mr/model/configuration.rb +67 -0
  39. data/lib/mr/model/fields.rb +177 -0
  40. data/lib/mr/model/persistence.rb +79 -0
  41. data/lib/mr/query.rb +126 -0
  42. data/lib/mr/read_model.rb +83 -0
  43. data/lib/mr/read_model/data.rb +38 -0
  44. data/lib/mr/read_model/fields.rb +218 -0
  45. data/lib/mr/read_model/query_expression.rb +188 -0
  46. data/lib/mr/read_model/querying.rb +214 -0
  47. data/lib/mr/read_model/set_querying.rb +82 -0
  48. data/lib/mr/read_model/subquery.rb +98 -0
  49. data/lib/mr/record.rb +35 -0
  50. data/lib/mr/test_helpers.rb +229 -0
  51. data/lib/mr/type_converter.rb +85 -0
  52. data/lib/mr/version.rb +3 -0
  53. data/log/.gitkeep +0 -0
  54. data/mr.gemspec +29 -0
  55. data/test/helper.rb +21 -0
  56. data/test/support/db.rb +10 -0
  57. data/test/support/factory.rb +13 -0
  58. data/test/support/factory/area.rb +6 -0
  59. data/test/support/factory/comment.rb +14 -0
  60. data/test/support/factory/image.rb +6 -0
  61. data/test/support/factory/user.rb +6 -0
  62. data/test/support/models/area.rb +58 -0
  63. data/test/support/models/comment.rb +60 -0
  64. data/test/support/models/image.rb +53 -0
  65. data/test/support/models/user.rb +96 -0
  66. data/test/support/read_model/querying.rb +150 -0
  67. data/test/support/read_models/comment_with_user_data.rb +27 -0
  68. data/test/support/read_models/set_data.rb +49 -0
  69. data/test/support/read_models/subquery_data.rb +41 -0
  70. data/test/support/read_models/user_with_area_data.rb +15 -0
  71. data/test/support/schema.rb +39 -0
  72. data/test/support/setup_test_db.rb +10 -0
  73. data/test/system/factory/model_factory_tests.rb +87 -0
  74. data/test/system/factory/model_stack_tests.rb +30 -0
  75. data/test/system/factory/record_factory_tests.rb +84 -0
  76. data/test/system/factory/record_stack_tests.rb +51 -0
  77. data/test/system/factory_tests.rb +32 -0
  78. data/test/system/read_model_tests.rb +199 -0
  79. data/test/system/with_model_tests.rb +275 -0
  80. data/test/unit/after_commit/fake_record_tests.rb +110 -0
  81. data/test/unit/after_commit/record_procs_methods_tests.rb +177 -0
  82. data/test/unit/after_commit/record_tests.rb +134 -0
  83. data/test/unit/after_commit_tests.rb +113 -0
  84. data/test/unit/factory/config_tests.rb +651 -0
  85. data/test/unit/factory/model_factory_tests.rb +473 -0
  86. data/test/unit/factory/model_stack_tests.rb +97 -0
  87. data/test/unit/factory/read_model_factory_tests.rb +195 -0
  88. data/test/unit/factory/record_factory_tests.rb +446 -0
  89. data/test/unit/factory/record_stack_tests.rb +549 -0
  90. data/test/unit/factory_tests.rb +213 -0
  91. data/test/unit/fake_query_tests.rb +137 -0
  92. data/test/unit/fake_record/associations_tests.rb +585 -0
  93. data/test/unit/fake_record/attributes_tests.rb +265 -0
  94. data/test/unit/fake_record/persistence_tests.rb +239 -0
  95. data/test/unit/fake_record_tests.rb +106 -0
  96. data/test/unit/json_field/fake_record_tests.rb +75 -0
  97. data/test/unit/json_field/record_tests.rb +80 -0
  98. data/test/unit/json_field_tests.rb +302 -0
  99. data/test/unit/model/associations_tests.rb +346 -0
  100. data/test/unit/model/configuration_tests.rb +92 -0
  101. data/test/unit/model/fields_tests.rb +278 -0
  102. data/test/unit/model/persistence_tests.rb +114 -0
  103. data/test/unit/model_tests.rb +137 -0
  104. data/test/unit/query_tests.rb +300 -0
  105. data/test/unit/read_model/data_tests.rb +56 -0
  106. data/test/unit/read_model/fields_tests.rb +416 -0
  107. data/test/unit/read_model/query_expression_tests.rb +381 -0
  108. data/test/unit/read_model/querying_tests.rb +613 -0
  109. data/test/unit/read_model/set_querying_tests.rb +149 -0
  110. data/test/unit/read_model/subquery_tests.rb +242 -0
  111. data/test/unit/read_model_tests.rb +187 -0
  112. data/test/unit/record_tests.rb +45 -0
  113. data/test/unit/test_helpers_tests.rb +431 -0
  114. data/test/unit/type_converter_tests.rb +207 -0
  115. metadata +285 -0
@@ -0,0 +1,213 @@
1
+ require 'assert'
2
+ require 'mr/factory'
3
+
4
+ require 'thread'
5
+ require 'mr/fake_record'
6
+ require 'mr/model'
7
+ require 'mr/read_model'
8
+ require 'mr/type_converter'
9
+
10
+ module MR::Factory
11
+
12
+ class UnitTests < Assert::Context
13
+ desc "MR::Factory"
14
+ setup do
15
+ @type_converter = MR::TypeConverter.new
16
+ end
17
+ subject{ MR::Factory }
18
+
19
+ should have_imeths :new
20
+ should have_imeths :primary_key, :decimal, :timestamp
21
+ should have_imeths :type_converter
22
+
23
+ should "extend `Assert::Factory`" do
24
+ assert_respond_to :integer, subject
25
+ assert_respond_to :float, subject
26
+ assert_respond_to :date, subject
27
+ assert_respond_to :datetime, subject
28
+ assert_respond_to :time, subject
29
+ assert_respond_to :string, subject
30
+ assert_respond_to :text, subject
31
+ assert_respond_to :slug, subject
32
+ assert_respond_to :hex, subject
33
+ assert_respond_to :binary, subject
34
+ assert_respond_to :file_name, subject
35
+ assert_respond_to :dir_path, subject
36
+ assert_respond_to :file_path, subject
37
+ assert_respond_to :boolean, subject
38
+ end
39
+
40
+ should "return unique integers for an identifier using `primary_key`" do
41
+ assert_equal 1, subject.primary_key('test')
42
+ assert_equal 2, subject.primary_key('test')
43
+ assert_equal 1, subject.primary_key('other')
44
+ end
45
+
46
+ should "return a random decimal using `decimal`" do
47
+ assert_kind_of BigDecimal, subject.decimal
48
+ end
49
+
50
+ should "allow passing a maximum value using `decimal`" do
51
+ decimal = subject.decimal(2)
52
+ assert decimal <= 2
53
+ assert decimal >= 0
54
+ end
55
+
56
+ should "return a random time object using `timestamp`" do
57
+ assert_kind_of Time, subject.timestamp
58
+ end
59
+
60
+ should "return an instance of MR::TypeConverter using `type_converter`" do
61
+ assert_instance_of MR::TypeConverter, subject.type_converter
62
+ end
63
+
64
+ end
65
+
66
+ class NewTests < UnitTests
67
+ desc "new"
68
+ setup do
69
+ @record_factory_built_with = nil
70
+ Assert.stub(MR::Factory::RecordFactory, :new) do |*args, &block|
71
+ @record_factory_built_with = args + [block]
72
+ Assert.stub_send(MR::Factory::RecordFactory, :new, *args, &block)
73
+ end
74
+
75
+ @model_factory_built_with = nil
76
+ Assert.stub(MR::Factory::ModelFactory, :new) do |*args, &block|
77
+ @model_factory_built_with = args + [block]
78
+ Assert.stub_send(MR::Factory::ModelFactory, :new, *args, &block)
79
+ end
80
+
81
+ @read_model_factory_built_with = nil
82
+ Assert.stub(MR::Factory::ReadModelFactory, :new) do |*args, &block|
83
+ @read_model_factory_built_with = args + [block]
84
+ Assert.stub_send(MR::Factory::ReadModelFactory, :new, *args, &block)
85
+ end
86
+ end
87
+
88
+ should "build a record factory when passed a record class" do
89
+ block = proc{ Factory.string }
90
+
91
+ factory = subject.new(TestFakeRecord, &block)
92
+ assert_equal [TestFakeRecord, block], @record_factory_built_with
93
+ assert_instance_of MR::Factory::RecordFactory, factory
94
+ end
95
+
96
+ should "build a model factory when passed a model class" do
97
+ block = proc{ Factory.string }
98
+
99
+ factory = subject.new(TestFakeModel, TestFakeRecord, &block)
100
+ assert_equal [TestFakeModel, TestFakeRecord, block], @model_factory_built_with
101
+ assert_instance_of MR::Factory::ModelFactory, factory
102
+ end
103
+
104
+ should "build a read model factory when passed a read model class" do
105
+ block = proc{ Factory.string }
106
+
107
+ factory = subject.new(TestReadModel, &block)
108
+ assert_equal [TestReadModel, block], @read_model_factory_built_with
109
+ assert_instance_of MR::Factory::ReadModelFactory, factory
110
+ end
111
+
112
+ should "build a read model factory when passed a read model struct class" do
113
+ block = proc{ Factory.string }
114
+
115
+ factory = subject.new(TestReadModelStruct, &block)
116
+ assert_equal [TestReadModelStruct, block], @read_model_factory_built_with
117
+ assert_instance_of MR::Factory::ReadModelFactory, factory
118
+ end
119
+
120
+ end
121
+
122
+ class PrimaryKeyProviderTests < UnitTests
123
+ desc "PrimaryKeyProvider"
124
+ setup do
125
+ @provider = PrimaryKeyProvider.new
126
+ @started_at = @provider.current
127
+ end
128
+ subject{ @provider }
129
+
130
+ should have_readers :mutex, :current
131
+
132
+ should "store a mutex and it's current value" do
133
+ assert_instance_of Mutex, subject.mutex
134
+ assert_instance_of Fixnum, subject.current
135
+ end
136
+
137
+ should "increated the counter and return the value using `next`" do
138
+ next_id = subject.next
139
+ assert_equal @started_at + 1, next_id
140
+ assert_equal @started_at + 1, subject.current
141
+ end
142
+
143
+ should "lock getting the next value using `next`" do
144
+ threads = [*0..2].map do |n|
145
+ Thread.new{ Thread.current['id'] = @provider.next }
146
+ end
147
+ primary_keys = threads.map{ |thread| thread.join; thread['id'] }
148
+ assert_includes 1, primary_keys
149
+ assert_includes 2, primary_keys
150
+ end
151
+
152
+ end
153
+
154
+ class NoRecordClassErrorTests < UnitTests
155
+ desc "NoRecordClassError"
156
+ setup do
157
+ @error_class = NoRecordClassError
158
+ end
159
+ subject{ @error_class }
160
+
161
+ should have_imeths :for_association
162
+
163
+ should "be a runtime error" do
164
+ assert_true subject < RuntimeError
165
+ end
166
+
167
+ should "know how to be built for a record reflection" do
168
+ record = TestFakeRecord.new
169
+ ar_association = record.association(:user)
170
+ error = subject.for_association(ar_association)
171
+
172
+ exp = "can't build 'user' association on #{TestFakeRecord} -- try " \
173
+ "manually setting it or building it via a stack if you've " \
174
+ "configured default associations"
175
+ assert_equal exp, error.message
176
+
177
+ ar_association = record.association(:parent)
178
+ error = subject.for_association(ar_association)
179
+
180
+ exp = "can't build 'parent' association on #{TestFakeRecord} -- try " \
181
+ "manually setting it, building it via a stack if you've " \
182
+ "configured default associations, or setting its 'parent_type' " \
183
+ "attribute"
184
+ assert_equal exp, error.message
185
+ end
186
+
187
+ end
188
+
189
+ class TestFakeRecord
190
+ include MR::FakeRecord
191
+
192
+ attribute :parent_type, :string, :null => true
193
+ attribute :parent_id, :integer, :null => true
194
+ attribute :user_id, :integer
195
+
196
+ belongs_to :parent, :polymorphic => true
197
+ belongs_to :user, :class_name => 'MR::Factory::RecordFactory::TestFakeRecord'
198
+ end
199
+
200
+ class TestFakeModel
201
+ include MR::Model
202
+ record_class TestFakeRecord
203
+ end
204
+
205
+ class TestReadModel
206
+ include MR::ReadModel
207
+ end
208
+
209
+ class TestReadModelStruct
210
+ include MR::ReadModelStruct
211
+ end
212
+
213
+ end
@@ -0,0 +1,137 @@
1
+ require 'assert'
2
+ require 'mr/fake_query'
3
+
4
+ require 'mr/fake_record'
5
+ require 'mr/model'
6
+ require 'mr/query'
7
+
8
+ class MR::FakeQuery
9
+
10
+ class UnitTests < Assert::Context
11
+ desc "MR::FakeQuery"
12
+ setup do
13
+ # +1 so we always have at least 2 results
14
+ @results = (Factory.integer(3) + 1).times.map do
15
+ FakeTestModel.new.tap(&:save)
16
+ end
17
+ @query = MR::FakeQuery.new(@results)
18
+ end
19
+ subject{ @query }
20
+
21
+ should have_readers :results, :first, :count
22
+ should have_imeths :results!, :first!, :count!, :paged
23
+
24
+ should "know its results and count" do
25
+ assert_equal @results, subject.results
26
+ assert_equal @results.size, subject.count
27
+ end
28
+
29
+ should "default its results and count" do
30
+ query = MR::FakeQuery.new(nil)
31
+ assert_equal [], query.results
32
+ assert_equal 0, query.count
33
+ end
34
+
35
+ should "know its first result" do
36
+ assert_equal @results.first, subject.first
37
+
38
+ query = MR::FakeQuery.new([nil, []].sample)
39
+ assert_nil query.first
40
+ end
41
+
42
+ should "alias its results, first, and count methods" do
43
+ assert_same subject.results, subject.results!
44
+ assert_same subject.first, subject.first!
45
+ assert_same subject.count, subject.count!
46
+ end
47
+
48
+ should "return an instance of a `FakePagedQuery` with #paged" do
49
+ assert_instance_of MR::FakePagedQuery, subject.paged
50
+ end
51
+
52
+ end
53
+
54
+ class FakePagedQueryTests < UnitTests
55
+ desc "MR::FakePagedQuery"
56
+ setup do
57
+ @page_num = Factory.integer(@results.size)
58
+ @page_size = 1
59
+
60
+ @paged_query_class = MR::FakePagedQuery
61
+ @paged_query = @paged_query_class.new(@query, @page_num, @page_size)
62
+ end
63
+ subject{ @paged_query }
64
+
65
+ should have_readers :page_num, :page_size, :page_offset, :total_count
66
+ should have_imeths :total_count!, :has_next_page?, :is_last_page?
67
+
68
+ should "be a kind of MR::FakeQuery" do
69
+ assert_kind_of MR::FakeQuery, subject
70
+ end
71
+
72
+ should "know its page num/size/offset" do
73
+ assert_equal @page_num, subject.page_num
74
+ assert_equal @page_size, subject.page_size
75
+ exp = MR::PagedQuery::PageOffset.new(@page_num, @page_size)
76
+ assert_equal exp, subject.page_offset
77
+ end
78
+
79
+ should "know its paged results" do
80
+ exp = @results[subject.page_offset, subject.page_size]
81
+ assert_equal exp, subject.results
82
+ end
83
+
84
+ should "know its paged result count" do
85
+ assert_equal @page_size, subject.count
86
+ end
87
+
88
+ should "know its total number of results" do
89
+ assert_equal @results.size, subject.total_count
90
+ end
91
+
92
+ should "alias its total count" do
93
+ assert_same subject.total_count, subject.total_count!
94
+ end
95
+
96
+ should "know if it has a next page or is the last page" do
97
+ unpaged_results = @results.dup
98
+ Assert.stub(@results, :dup){ unpaged_results }
99
+
100
+ page_num = Factory.integer(10)
101
+ page_size = Factory.integer(10)
102
+ page_offset = MR::PagedQuery::PageOffset.new(page_num, page_size)
103
+ page_end = page_offset + page_size
104
+
105
+ total_count = Factory.integer(page_size - 1) + page_end
106
+ Assert.stub(unpaged_results, :size){ total_count }
107
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
108
+
109
+ assert_true paged_query.has_next_page?
110
+ assert_false paged_query.is_last_page?
111
+
112
+ total_count = Factory.integer(page_size - 1) + page_offset
113
+ Assert.stub(unpaged_results, :size){ total_count }
114
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
115
+
116
+ assert_false paged_query.has_next_page?
117
+ assert_true paged_query.is_last_page?
118
+
119
+ Assert.stub(unpaged_results, :size){ page_end }
120
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
121
+
122
+ assert_false paged_query.has_next_page?
123
+ assert_true paged_query.is_last_page?
124
+ end
125
+
126
+ end
127
+
128
+ class FakeTestRecord
129
+ include MR::FakeRecord
130
+ end
131
+
132
+ class FakeTestModel
133
+ include MR::Model
134
+ record_class FakeTestRecord
135
+ end
136
+
137
+ end
@@ -0,0 +1,585 @@
1
+ require 'assert'
2
+ require 'mr/fake_record/associations'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+
7
+ module MR::FakeRecord::Associations
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "MR::FakeRecord::Associations"
11
+ setup do
12
+ @fake_record_class = Class.new do
13
+ include MR::FakeRecord::Associations
14
+ end
15
+ end
16
+ subject{ @fake_record_class }
17
+
18
+ should have_imeths :reflections
19
+ should have_imeths :reflect_on_association, :reflect_on_all_associations
20
+ should have_imeths :belongs_to, :has_one, :has_many
21
+
22
+ should "use much-plugin" do
23
+ assert_includes MuchPlugin, MR::FakeRecord::Associations
24
+ end
25
+
26
+ should "include the fake record attributes mixin" do
27
+ assert_includes MR::FakeRecord::Attributes, subject
28
+ end
29
+
30
+ should "know its reflections" do
31
+ reflections = subject.reflections
32
+ assert_instance_of MR::FakeRecord::ReflectionSet, reflections
33
+ assert_same reflections, subject.reflections
34
+ end
35
+
36
+ should "allow adding belongs to associations" do
37
+ add_belongs_to_called_with = nil
38
+ Assert.stub(subject.reflections, :add_belongs_to) do |*args|
39
+ add_belongs_to_called_with = args
40
+ Assert.stub_send(subject.reflections, :add_belongs_to, *args)
41
+ end
42
+ association_name = Factory.string
43
+ options = { Factory.string => Factory.string }
44
+ subject.belongs_to(association_name, options)
45
+
46
+ exp = [subject, association_name, options]
47
+ assert_equal exp, add_belongs_to_called_with
48
+ end
49
+
50
+ should "allow adding has one associations" do
51
+ add_has_one_called_with = nil
52
+ Assert.stub(subject.reflections, :add_has_one) do |*args|
53
+ add_has_one_called_with = args
54
+ Assert.stub_send(subject.reflections, :add_has_one, *args)
55
+ end
56
+ association_name = Factory.string
57
+ options = { Factory.string => Factory.string }
58
+ subject.has_one(association_name, options)
59
+
60
+ exp = [subject, association_name, options]
61
+ assert_equal exp, add_has_one_called_with
62
+ end
63
+
64
+ should "allow adding has many associations" do
65
+ add_has_many_called_with = nil
66
+ Assert.stub(subject.reflections, :add_has_many) do |*args|
67
+ add_has_many_called_with = args
68
+ Assert.stub_send(subject.reflections, :add_has_many, *args)
69
+ end
70
+ association_name = Factory.string
71
+ options = { Factory.string => Factory.string }
72
+ subject.has_many(association_name, options)
73
+
74
+ exp = [subject, association_name, options]
75
+ assert_equal exp, add_has_many_called_with
76
+ end
77
+
78
+ end
79
+
80
+ class WithAssociationsTests < UnitTests
81
+ desc "with associations"
82
+ setup do
83
+ @fake_record_class.belongs_to :poly_test, :polymorphic => true
84
+ @fake_record_class.belongs_to :belongs_to_test, :class_name => FakeOneRecord.to_s
85
+ @fake_record_class.has_one :has_one_test, :class_name => FakeOneRecord.to_s
86
+ @fake_record_class.has_many :has_many_test, :class_name => FakeOneRecord.to_s
87
+ end
88
+
89
+ should "return a specific reflection using `reflect_on_association`" do
90
+ name = [
91
+ :belongs_to_test,
92
+ :poly_test,
93
+ :has_one_test,
94
+ :has_many_test
95
+ ].sample
96
+ reflection = subject.reflect_on_association(name)
97
+
98
+ assert_instance_of MR::FakeRecord::Reflection, reflection
99
+ assert_equal name, reflection.name
100
+ end
101
+
102
+ should "return all reflections using `reflect_on_all_associations`" do
103
+ reflections = subject.reflect_on_all_associations
104
+ assert_equal 4, reflections.size
105
+ reflections.each{ |r| assert_instance_of MR::FakeRecord::Reflection, r }
106
+ expected = [ :belongs_to_test, :poly_test, :has_one_test, :has_many_test ]
107
+ assert_equal expected, reflections.map(&:name)
108
+ end
109
+
110
+ should "only return specific kinds of reflections when a type is passed " \
111
+ "to `reflect_on_all_associations`" do
112
+ reflections = subject.reflect_on_all_associations(:belongs_to)
113
+ assert_equal [ :belongs_to_test, :poly_test ], reflections.map(&:name)
114
+ reflections = subject.reflect_on_all_associations(:has_many)
115
+ assert_equal [ :has_many_test ], reflections.map(&:name)
116
+ reflections = subject.reflect_on_all_associations(:has_one)
117
+ assert_equal [ :has_one_test ], reflections.map(&:name)
118
+ end
119
+
120
+ end
121
+
122
+ class InstanceTests < UnitTests
123
+ desc "for a fake record instance"
124
+ setup do
125
+ @fake_record_class.belongs_to :test, :class_name => FakeOneRecord.to_s
126
+ @fake_record = @fake_record_class.new
127
+ end
128
+ subject{ @fake_record }
129
+
130
+ should have_imeths :association
131
+
132
+ should "return a matching association using `association`" do
133
+ association = subject.association(:test)
134
+ reflection = @fake_record_class.reflections.find(:test)
135
+ assert_equal reflection.macro, association.reflection.macro
136
+ assert_equal reflection.name, association.reflection.name
137
+ end
138
+
139
+ end
140
+
141
+ class ReflectionSetTests < UnitTests
142
+ desc "ReflectionSet"
143
+ setup do
144
+ @association_set = MR::FakeRecord::ReflectionSet.new
145
+ end
146
+ subject{ @association_set }
147
+
148
+ should have_imeths :belongs_to, :has_one, :has_many
149
+ should have_imeths :find, :all
150
+ should have_imeths :add_belongs_to, :add_has_one, :add_has_many
151
+
152
+ should "know how to add a belongs to" do
153
+ subject.add_belongs_to(@fake_record_class, :fake_one, {
154
+ :class_name => FakeOneRecord.to_s
155
+ })
156
+
157
+ assert_equal 1, subject.belongs_to.size
158
+ reflection = subject.belongs_to.first
159
+ assert_instance_of MR::FakeRecord::Reflection, reflection
160
+ assert_equal :fake_one, reflection.name
161
+ exp = {
162
+ :class_name => FakeOneRecord.to_s,
163
+ :foreign_key => 'fake_one_id'
164
+ }
165
+ assert_equal exp, reflection.options
166
+
167
+ fake_record = @fake_record_class.new
168
+ assert_respond_to :fake_one, fake_record
169
+ assert_respond_to :fake_one=, fake_record
170
+ end
171
+
172
+ should "allow passing a foreign key when adding a belongs to" do
173
+ foreign_key = Factory.string
174
+ subject.add_belongs_to(@fake_record_class, :fake_one, {
175
+ :class_name => FakeOneRecord.to_s,
176
+ :foreign_key => foreign_key
177
+ })
178
+
179
+ exp = {
180
+ :class_name => FakeOneRecord.to_s,
181
+ :foreign_key => foreign_key
182
+ }
183
+ assert_equal exp, subject.belongs_to.first.options
184
+ end
185
+
186
+ should "know how to add a polymorphic belongs to" do
187
+ subject.add_belongs_to(@fake_record_class, :fake_poly, {
188
+ :polymorphic => true
189
+ })
190
+
191
+ assert_equal 1, subject.belongs_to.size
192
+ reflection = subject.belongs_to.first
193
+ assert_instance_of MR::FakeRecord::Reflection, reflection
194
+ assert_equal :fake_poly, reflection.name
195
+ exp = {
196
+ :polymorphic => true,
197
+ :foreign_type => 'fake_poly_type',
198
+ :foreign_key => 'fake_poly_id'
199
+ }
200
+ assert_equal exp, reflection.options
201
+
202
+ fake_record = @fake_record_class.new
203
+ assert_respond_to :fake_poly, fake_record
204
+ assert_respond_to :fake_poly=, fake_record
205
+ end
206
+
207
+ should "allow passing a foreign type and key when adding a poly belongs to" do
208
+ foreign_type = Factory.string
209
+ foreign_key = Factory.string
210
+ subject.add_belongs_to(@fake_record_class, :fake_poly, {
211
+ :polymorphic => true,
212
+ :foreign_type => foreign_type,
213
+ :foreign_key => foreign_key
214
+ })
215
+
216
+ exp = {
217
+ :polymorphic => true,
218
+ :foreign_type => foreign_type,
219
+ :foreign_key => foreign_key
220
+ }
221
+ assert_equal exp, subject.belongs_to.first.options
222
+ end
223
+
224
+ should "know how to add a has one" do
225
+ subject.add_has_one(@fake_record_class, :fake_one, {
226
+ :class_name => FakeOneRecord.to_s
227
+ })
228
+
229
+ assert_equal 1, subject.has_one.size
230
+ reflection = subject.has_one.first
231
+ assert_instance_of MR::FakeRecord::Reflection, reflection
232
+ assert_equal :fake_one, reflection.name
233
+ exp = { :class_name => FakeOneRecord.to_s }
234
+ assert_equal exp, reflection.options
235
+
236
+ fake_record = @fake_record_class.new
237
+ assert_respond_to :fake_one, fake_record
238
+ assert_respond_to :fake_one=, fake_record
239
+ end
240
+
241
+ should "know how to add a has many" do
242
+ subject.add_has_many(@fake_record_class, :fake_ones, {
243
+ :class_name => FakeOneRecord.to_s
244
+ })
245
+
246
+ assert_equal 1, subject.has_many.size
247
+ reflection = subject.has_many.first
248
+ assert_instance_of MR::FakeRecord::Reflection, reflection
249
+ assert_equal :fake_ones, reflection.name
250
+ exp = { :class_name => FakeOneRecord.to_s }
251
+ assert_equal exp, reflection.options
252
+
253
+ fake_record = @fake_record_class.new
254
+ assert_respond_to :fake_ones, fake_record
255
+ assert_respond_to :fake_ones=, fake_record
256
+ end
257
+
258
+ end
259
+
260
+ class WithAssociationsOnReflectionSetTests < ReflectionSetTests
261
+ desc "with associations"
262
+ setup do
263
+ @association_set.add_belongs_to(@fake_record_class, :poly_belongs_to_test, {
264
+ :polymorphic => true
265
+ })
266
+ @association_set.add_belongs_to(@fake_record_class, :belongs_to_test, {
267
+ :class_name => FakeOneRecord.to_s
268
+ })
269
+ @association_set.add_has_one(@fake_record_class, :has_one_test, {
270
+ :class_name => FakeOneRecord.to_s
271
+ })
272
+ @association_set.add_has_many(@fake_record_class, :has_many_test, {
273
+ :class_name => FakeOneRecord.to_s
274
+ })
275
+ end
276
+
277
+ should "return all belongs to reflections sorted using `belongs_to`" do
278
+ reflections = subject.belongs_to
279
+ expected = [ :belongs_to_test, :poly_belongs_to_test ]
280
+ assert_equal expected, reflections.map(&:name)
281
+ end
282
+
283
+ should "return all has one reflections sorted using `has_one`" do
284
+ reflections = subject.has_one
285
+ assert_equal [ :has_one_test ], reflections.map(&:name)
286
+ end
287
+
288
+ should "return all has many reflections sorted using `has_many`" do
289
+ reflections = subject.has_many
290
+ assert_equal [ :has_many_test ], reflections.map(&:name)
291
+ end
292
+
293
+ should "find an reflection using `find`" do
294
+ reflection = subject.find(:belongs_to_test)
295
+ assert_equal :belongs_to_test, reflection.name
296
+ reflection = subject.find(:poly_belongs_to_test)
297
+ assert_equal :poly_belongs_to_test, reflection.name
298
+ reflection = subject.find(:has_one_test)
299
+ assert_equal :has_one_test, reflection.name
300
+ reflection = subject.find(:has_many_test)
301
+ assert_equal :has_many_test, reflection.name
302
+
303
+ assert_nil subject.find(:doesnt_exist)
304
+ end
305
+
306
+ should "return all reflections using `all`" do
307
+ reflections = subject.all
308
+ assert_equal 4, reflections.size
309
+ expected = [ :belongs_to_test, :poly_belongs_to_test, :has_one_test, :has_many_test ]
310
+ assert_equal expected, reflections.map(&:name)
311
+ end
312
+
313
+ should "return all matching reflections passing a type to `all`" do
314
+ reflections = subject.all(:belongs_to)
315
+ expected = [ :belongs_to_test, :poly_belongs_to_test ]
316
+ assert_equal expected, reflections.map(&:name)
317
+ reflections = subject.all(:has_many)
318
+ assert_equal [ :has_many_test ], reflections.map(&:name)
319
+ reflections = subject.all(:has_one)
320
+ assert_equal [ :has_one_test ], reflections.map(&:name)
321
+ end
322
+
323
+ end
324
+
325
+ class ReflectionTests < UnitTests
326
+ desc "MR::FakeRecord::Reflection"
327
+ setup do
328
+ @reflection_class = MR::FakeRecord::Reflection
329
+ @reflection = @reflection_class.new(:belongs_to, :parent, {
330
+ :class_name => FakeOneRecord.to_s,
331
+ :foreign_key => 'parent_id',
332
+ :foreign_type => 'parent_type'
333
+ })
334
+ end
335
+ subject{ @reflection }
336
+
337
+ should have_readers :reader_method_name, :writer_method_name
338
+ should have_readers :name, :macro, :options
339
+ should have_readers :foreign_key, :foreign_type
340
+ should have_imeths :association_class, :klass, :define_accessor_on
341
+
342
+ should "know its method names" do
343
+ assert_equal "parent", subject.reader_method_name
344
+ assert_equal "parent=", subject.writer_method_name
345
+ end
346
+
347
+ should "know its attributes" do
348
+ assert_equal :parent, subject.name
349
+ assert_equal :belongs_to, subject.macro
350
+ assert_equal 'parent_type', subject.foreign_type
351
+ assert_equal 'parent_id', subject.foreign_key
352
+ exp = {
353
+ :class_name => FakeOneRecord.to_s,
354
+ :foreign_key => subject.foreign_key,
355
+ :foreign_type => subject.foreign_type
356
+ }
357
+ assert_equal exp, subject.options
358
+ end
359
+
360
+ should "return its association class using `association_class`" do
361
+ reflection = @reflection_class.new(:belongs_to, :test, :polymorphic => true)
362
+ exp = MR::FakeRecord::PolymorphicBelongsToAssociation
363
+ assert_equal exp, reflection.association_class
364
+
365
+ reflection = @reflection_class.new(:belongs_to, :test)
366
+ exp = MR::FakeRecord::BelongsToAssociation
367
+ assert_equal exp, reflection.association_class
368
+
369
+ reflection = @reflection_class.new(:has_one, :test)
370
+ exp = MR::FakeRecord::HasOneAssociation
371
+ assert_equal exp, reflection.association_class
372
+
373
+ reflection = @reflection_class.new(:has_many, :test)
374
+ exp = MR::FakeRecord::HasManyAssociation
375
+ assert_equal exp, reflection.association_class
376
+ end
377
+
378
+ should "return its class names constantized with `klass`" do
379
+ assert_equal FakeOneRecord, subject.klass
380
+ end
381
+
382
+ should "define accessor methods using `define_accessor_on`" do
383
+ subject.define_accessor_on(@fake_record_class)
384
+ fake_record = @fake_record_class.new
385
+
386
+ assert_respond_to :parent, fake_record
387
+ assert_respond_to :parent=, fake_record
388
+ end
389
+
390
+ should "be sortable" do
391
+ reflections = [
392
+ MR::FakeRecord::Reflection.new(:belongs_to, Factory.string),
393
+ MR::FakeRecord::Reflection.new(:has_one, Factory.string),
394
+ MR::FakeRecord::Reflection.new(:has_many, Factory.string)
395
+ ].shuffle.sort
396
+ exp = [:belongs_to, :has_many, :has_one]
397
+ assert_equal exp, reflections.map(&:macro)
398
+ end
399
+
400
+ end
401
+
402
+ class WithFakeRecordTests < UnitTests
403
+ setup do
404
+ @fake_record_class.class_eval do
405
+ attr_reader :test, :tests
406
+ attr_accessor :parent_type, :parent_id
407
+ end
408
+ @fake_record = @fake_record_class.new
409
+ end
410
+ subject{ @association }
411
+
412
+ end
413
+
414
+ class AssociationTests < WithFakeRecordTests
415
+ desc "Association"
416
+ setup do
417
+ @reflection = MR::FakeRecord::Reflection.new(:belongs_to, :test)
418
+ @association = MR::FakeRecord::Association.new(@fake_record, @reflection)
419
+ end
420
+
421
+ should have_readers :owner, :reflection
422
+ should have_imeths :read, :write, :klass
423
+
424
+ should "know its owner and reflection" do
425
+ assert_equal @fake_record, subject.owner
426
+ assert_equal @reflection, subject.reflection
427
+ end
428
+
429
+ should "return its reflection's klass using `klass`" do
430
+ assert_equal @reflection.klass, subject.klass
431
+ end
432
+
433
+ should "raise NotImplementedError using `read` and `write`" do
434
+ assert_raises(NotImplementedError){ subject.read }
435
+ assert_raises(NotImplementedError){ subject.write('test') }
436
+ end
437
+
438
+ should "be sortable" do
439
+ reflections = [
440
+ MR::FakeRecord::Reflection.new(:belongs_to, :belongs_to_test),
441
+ MR::FakeRecord::Reflection.new(:has_many, :has_many_test)
442
+ ]
443
+ associations = reflections.map do |reflection|
444
+ MR::FakeRecord::Association.new(@fake_record, reflection)
445
+ end
446
+ association_names = associations.sort.map{ |a| a.reflection.name }
447
+ assert_equal reflections.sort.map(&:name), association_names
448
+ end
449
+
450
+ end
451
+
452
+ class OneToOneAssociationTests < WithFakeRecordTests
453
+ desc "OneToOneAssociation"
454
+ setup do
455
+ @reflection = MR::FakeRecord::Reflection.new(:belongs_to, :test)
456
+ @association = MR::FakeRecord::OneToOneAssociation.new(
457
+ @fake_record,
458
+ @reflection
459
+ )
460
+ end
461
+
462
+ should "be a kind of association" do
463
+ assert_kind_of MR::FakeRecord::Association, subject
464
+ end
465
+
466
+ should "read and write its value using `read` and `write`" do
467
+ associated_record = FakeOneRecord.new
468
+ subject.write(associated_record)
469
+ assert_same associated_record, @fake_record.test
470
+ assert_same @fake_record.test, subject.read
471
+ end
472
+
473
+ end
474
+
475
+ class OneToManyAssociationTests < WithFakeRecordTests
476
+ desc "OneToManyAssociation"
477
+ setup do
478
+ @reflection = MR::FakeRecord::Reflection.new(:has_many, :tests)
479
+ @association = MR::FakeRecord::OneToManyAssociation.new(
480
+ @fake_record,
481
+ @reflection
482
+ )
483
+ end
484
+
485
+ should "be a kind of association" do
486
+ assert_kind_of MR::FakeRecord::Association, subject
487
+ end
488
+
489
+ should "return an empty array when the association hasn't been set using `read`" do
490
+ assert_equal [], subject.read
491
+ end
492
+
493
+ should "read and write its value using `read` and `write`" do
494
+ associated_records = [FakeOneRecord.new]
495
+ subject.write(associated_records)
496
+ assert_equal associated_records, @fake_record.tests
497
+ assert_equal @fake_record.tests, subject.read
498
+ end
499
+
500
+ should "allow writing non-array values using `write`" do
501
+ associated_record = FakeOneRecord.new
502
+ subject.write(associated_record)
503
+ assert_equal [associated_record], @fake_record.tests
504
+ end
505
+
506
+ should "allow writing nil values using `write`" do
507
+ subject.write nil
508
+ assert_equal [], @fake_record.tests
509
+ end
510
+
511
+ end
512
+
513
+ class BelongsToAssociationTests < WithFakeRecordTests
514
+ desc "BelongsToAssociation"
515
+ setup do
516
+ @reflection = MR::FakeRecord::Reflection.new(:belongs_to, :parent, {
517
+ :foreign_key => 'parent_id'
518
+ })
519
+ @association = MR::FakeRecord::BelongsToAssociation.new(
520
+ @fake_record,
521
+ @reflection
522
+ )
523
+ end
524
+
525
+ should "be a kind of one to one association" do
526
+ assert_kind_of MR::FakeRecord::OneToOneAssociation, subject
527
+ end
528
+
529
+ should "write its foreign key using `write`" do
530
+ associated_record = FakeOneRecord.new.tap(&:save!)
531
+ subject.write(associated_record)
532
+ assert_equal associated_record.id, @fake_record.parent_id
533
+ end
534
+
535
+ should "allow writing nil values using `write`" do
536
+ subject.write nil
537
+ assert_equal nil, @fake_record.parent_id
538
+ end
539
+
540
+ end
541
+
542
+ class PolymorphicBelongsToAssociationTests < WithFakeRecordTests
543
+ desc "PolymorphicBelongsToAssociation"
544
+ setup do
545
+ @reflection = MR::FakeRecord::Reflection.new(:belongs_to, :parent, {
546
+ :foreign_type => 'parent_type',
547
+ :foreign_key => 'parent_id'
548
+ })
549
+ @association = MR::FakeRecord::PolymorphicBelongsToAssociation.new(
550
+ @fake_record,
551
+ @reflection
552
+ )
553
+ end
554
+
555
+ should "be a kind of belongs to association" do
556
+ assert_kind_of MR::FakeRecord::BelongsToAssociation, subject
557
+ end
558
+
559
+ should "constantize its foreign type using `klass`" do
560
+ @fake_record.parent_type = FakeOneRecord.to_s
561
+ assert_equal FakeOneRecord, subject.klass
562
+ end
563
+
564
+ should "return `nil` if its foreign type isn't set using `klass`" do
565
+ assert_nil subject.klass
566
+ end
567
+
568
+ should "write its foreign type using `write`" do
569
+ associated_record = FakeOneRecord.new.tap(&:save!)
570
+ subject.write(associated_record)
571
+ assert_equal FakeOneRecord.to_s, @fake_record.parent_type
572
+ end
573
+
574
+ should "allow writing nil values using `write`" do
575
+ subject.write nil
576
+ assert_equal nil, @fake_record.parent_type
577
+ end
578
+
579
+ end
580
+
581
+ class FakeOneRecord
582
+ include MR::FakeRecord
583
+ end
584
+
585
+ end