mr 0.35.2

Sign up to get free protection for your applications and to get access to all the features.
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