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,346 @@
1
+ require 'assert'
2
+ require 'mr/model/associations'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+ require 'mr/model'
7
+
8
+ module MR::Model::Associations
9
+
10
+ class UnitTests < Assert::Context
11
+ desc "MR::Model::Associations"
12
+ setup do
13
+ @model_class = Class.new do
14
+ include MR::Model::Associations
15
+ record_class FakeRecordWithAssociations
16
+ def initialize(record); set_record record; end
17
+ end
18
+ @record = FakeRecordWithAssociations.new
19
+ end
20
+ subject{ @model_class }
21
+
22
+ should have_imeths :associations
23
+ should have_imeths :belongs_to, :polymorphic_belongs_to
24
+ should have_imeths :has_one, :has_many
25
+
26
+ should "use much-plugin" do
27
+ assert_includes MuchPlugin, MR::Model::Associations
28
+ end
29
+
30
+ should "include Model::Configuration mixin" do
31
+ assert_includes MR::Model::Configuration, subject.included_modules
32
+ end
33
+
34
+ should "return an instance of a AssociationSet using `associations`" do
35
+ associations = subject.associations
36
+ assert_instance_of MR::Model::AssociationSet, associations
37
+ assert_same associations, subject.associations
38
+ end
39
+
40
+ should "add accessors methods for a belongs to association using `belongs_to`" do
41
+ subject.belongs_to :area
42
+ model = subject.new(@record)
43
+ new_record = FakeTestRecord.new
44
+ new_model = FakeTestModel.new(new_record)
45
+
46
+ assert_respond_to :area, model
47
+ assert_respond_to :area=, model
48
+ model.area = new_model
49
+ assert_instance_of FakeTestModel, model.area
50
+ assert_equal new_record, @record.area
51
+ end
52
+
53
+ should "add accessors methods for a polymorphic belongs to association " \
54
+ "using `polymorphic_belongs_to`" do
55
+ subject.polymorphic_belongs_to :parent
56
+ model = subject.new(@record)
57
+ new_record = FakeTestRecord.new
58
+ new_model = FakeTestModel.new(new_record)
59
+
60
+ assert_respond_to :parent, model
61
+ assert_respond_to :parent=, model
62
+ model.parent = new_model
63
+ assert_instance_of FakeTestModel, model.parent
64
+ assert_equal new_record, @record.parent
65
+ end
66
+
67
+ should "add accessors methods for a has one association using `has_one`" do
68
+ subject.has_one :image
69
+ model = subject.new(@record)
70
+ new_record = FakeTestRecord.new
71
+ new_model = FakeTestModel.new(new_record)
72
+
73
+ assert_respond_to :image, model
74
+ assert_respond_to :image=, model
75
+ model.image = new_model
76
+ assert_instance_of FakeTestModel, model.image
77
+ assert_equal new_record, @record.image
78
+ end
79
+
80
+ should "add accessors methods for a has many association using `has_many`" do
81
+ subject.has_many :comments
82
+ model = subject.new(@record)
83
+ first_record = FakeTestRecord.new
84
+ first_model = FakeTestModel.new(first_record)
85
+ second_record = FakeTestRecord.new
86
+ second_model = FakeTestModel.new(second_record)
87
+
88
+ assert_respond_to :comments, model
89
+ assert_respond_to :comments=, model
90
+ model.comments = [ first_model, second_model ]
91
+ assert_instance_of Array, model.comments
92
+ assert_includes first_record, @record.comments
93
+ assert_includes second_record, @record.comments
94
+ end
95
+
96
+ end
97
+
98
+ class InstanceTests < UnitTests
99
+ desc "for a model instance"
100
+ setup do
101
+ @model_class.class_eval do
102
+ belongs_to :area
103
+ polymorphic_belongs_to :parent
104
+ has_one :image
105
+ has_many :comments
106
+ end
107
+ @model = @model_class.new(@record)
108
+ end
109
+ subject{ @model }
110
+
111
+ should "raise an ArgumentError if writing non MR::Model values to associations" do
112
+ assert_raises(ArgumentError){ subject.area = 'test' }
113
+ assert_raises(ArgumentError){ subject.parent = 'test' }
114
+ assert_raises(ArgumentError){ subject.image = 'test' }
115
+ assert_raises(ArgumentError){ subject.comments = [ 'test' ] }
116
+ end
117
+
118
+ end
119
+
120
+ class AssociationSetTests < UnitTests
121
+ desc "AssociationSet"
122
+ setup do
123
+ @association_set = MR::Model::AssociationSet.new
124
+ end
125
+ subject{ @association_set }
126
+
127
+ should have_readers :belongs_to, :polymorphic_belongs_to
128
+ should have_readers :has_one, :has_many
129
+ should have_imeths :add_belongs_to, :add_polymorphic_belongs_to
130
+ should have_imeths :add_has_one, :add_has_many
131
+
132
+ end
133
+
134
+ class AssociationTests < UnitTests
135
+ desc "Association"
136
+ setup do
137
+ @association = MR::Model::Association.new(:test)
138
+ end
139
+ subject{ @association }
140
+
141
+ should have_readers :name
142
+ should have_readers :reader_method_name, :writer_method_name
143
+
144
+ should "know it's name and method names" do
145
+ assert_equal 'test', subject.name
146
+ assert_equal 'test', subject.reader_method_name
147
+ assert_equal 'test=', subject.writer_method_name
148
+ end
149
+
150
+ end
151
+
152
+ class OneToOneAssociationTests < UnitTests
153
+ desc "OneToOneAssociation"
154
+ setup do
155
+ @test_record = FakeTestRecord.new.tap{ |r| r.save! }
156
+ @test_model = FakeTestModel.new(@test_record)
157
+ @other_record = FakeTestRecord.new.tap{ |r| r.save! }
158
+ @other_model = FakeTestModel.new(@other_record)
159
+
160
+ @association = MR::Model::OneToOneAssociation.new(:area)
161
+ end
162
+ subject{ @association }
163
+
164
+ should have_imeths :read, :write
165
+
166
+ should "be a kind of Association" do
167
+ assert_kind_of MR::Model::Association, subject
168
+ end
169
+
170
+ should "allow reading the record's association using `read`" do
171
+ @test_record.area = @other_record
172
+ assert_equal @other_model, subject.read(@test_record)
173
+ end
174
+
175
+ should "allow reading `nil` values for the record's association using `read`" do
176
+ @test_record.area = nil
177
+ assert_nil subject.read(@test_record)
178
+ end
179
+
180
+ should "allow writing the record's association using `write`" do
181
+ record = @other_record
182
+ yielded = nil
183
+ result = subject.write(@other_model, @test_model, @test_record) do |object|
184
+ yielded = object; record
185
+ end
186
+
187
+ assert_equal @other_model, yielded
188
+ assert_equal @other_record, @test_record.area
189
+ assert_equal @test_model.send(subject.name), result
190
+ end
191
+
192
+ should "allow writing `nil` values to the record's association using `write`" do
193
+ subject.write(nil, @test_model, @test_record){ raise 'test' }
194
+ assert_nil @test_record.area
195
+ end
196
+
197
+ should "raise a bad association value error when writing a non MR::Model" do
198
+ assert_raises(MR::Model::BadAssociationValueError) do
199
+ subject.write('test', @test_model, @test_record){ }
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ class OneToManyAssociationTests < UnitTests
206
+ desc "OneToManyAssociation"
207
+ setup do
208
+ @test_record = FakeTestRecord.new.tap{ |r| r.save! }
209
+ @test_model = FakeTestModel.new(@test_record)
210
+ @first_record = FakeTestRecord.new.tap{ |r| r.save! }
211
+ @first_model = FakeTestModel.new(@first_record)
212
+ @second_record = FakeTestRecord.new.tap{ |r| r.save! }
213
+ @second_model = FakeTestModel.new(@second_record)
214
+
215
+ @association = MR::Model::OneToManyAssociation.new(:comments)
216
+ end
217
+ subject{ @association }
218
+
219
+ should have_imeths :read, :write
220
+
221
+ should "be a kind of Association" do
222
+ assert_kind_of MR::Model::Association, subject
223
+ end
224
+
225
+ should "allow reading the record's association using `read`" do
226
+ @test_record.comments = [ @first_record ]
227
+ expected = [ @first_model ]
228
+ assert_equal expected, subject.read(@test_record)
229
+ end
230
+
231
+ should "allow reading `nil` values for the record's association using `read`" do
232
+ @test_record.comments = []
233
+ assert_equal [], subject.read(@test_record)
234
+ end
235
+
236
+ should "allow writing the record's association using `write`" do
237
+ models = [ @first_model, @second_model ]
238
+ yielded = []
239
+ results = subject.write(models, @test_model, @test_record) do |object|
240
+ yielded << object; object.record
241
+ end
242
+
243
+ assert_equal models, yielded
244
+ records = [ @first_record, @second_record ]
245
+ assert_equal records, @test_record.comments
246
+ assert_equal @test_model.send(subject.name), results
247
+ end
248
+
249
+ should "allow writing a single value to the record's association using `write`" do
250
+ subject.write(@second_model, @test_model, @test_record) do |object|
251
+ object.record
252
+ end
253
+ assert_equal [ @second_record ], @test_record.comments
254
+ end
255
+
256
+ should "allow writing `nil` values to the record's association using `write`" do
257
+ subject.write(nil, @test_model, @test_record){ raise 'test' }
258
+ assert_equal [], @test_record.comments
259
+ end
260
+
261
+ should "raise a bad association value error when writing a non MR::Model" do
262
+ assert_raises(MR::Model::BadAssociationValueError) do
263
+ subject.write('test', @test_model, @test_record){ }
264
+ end
265
+ end
266
+
267
+ end
268
+
269
+ class BelongsToAssociationTests < UnitTests
270
+ desc "BelongsToAssociation"
271
+ setup do
272
+ @association = MR::Model::BelongsToAssociation.new(:test)
273
+ end
274
+ subject{ @association }
275
+
276
+ should "be a kind of OneToOneAssociation" do
277
+ assert_kind_of MR::Model::OneToOneAssociation, subject
278
+ end
279
+
280
+ end
281
+
282
+ class HasOneAssociationTests < UnitTests
283
+ desc "HasOneAssociation"
284
+ setup do
285
+ @association = MR::Model::HasOneAssociation.new(:test)
286
+ end
287
+ subject{ @association }
288
+
289
+ should "be a kind of OneToOneAssociation" do
290
+ assert_kind_of MR::Model::OneToOneAssociation, subject
291
+ end
292
+
293
+ end
294
+
295
+ class HasManyAssociationTests < UnitTests
296
+ desc "HasManyAssociation"
297
+ setup do
298
+ @association = MR::Model::HasManyAssociation.new(:test)
299
+ end
300
+ subject{ @association }
301
+
302
+ should "be a kind of OneToManyAssociation" do
303
+ assert_kind_of MR::Model::OneToManyAssociation, subject
304
+ end
305
+
306
+ end
307
+
308
+ class PolymorphicBelongsToAssociationTests < UnitTests
309
+ desc "PolymorphicBelongsToAssociation"
310
+ setup do
311
+ @association = MR::Model::PolymorphicBelongsToAssociation.new(:test)
312
+ end
313
+ subject{ @association }
314
+
315
+ should "be a kind of BelongsToAssociation" do
316
+ assert_kind_of MR::Model::BelongsToAssociation, subject
317
+ end
318
+
319
+ end
320
+
321
+ class FakeRecordWithAssociations
322
+ include MR::FakeRecord
323
+ attribute :area_id, :integer
324
+ attribute :parent_type, :string
325
+ attribute :parent_id, :integer
326
+ belongs_to :area, :class_name => 'MR::Model::Associations::FakeRecordWithAssociations'
327
+ belongs_to :parent, :polymorphic => true
328
+ has_one :image, :class_name => 'MR::Model::Associations::FakeRecordWithAssociations'
329
+ has_many :comments, :class_name => 'MR::Model::Associations::FakeRecordWithAssociations'
330
+ end
331
+
332
+ class FakeTestRecord
333
+ include MR::FakeRecord
334
+ attribute :area_id, :integer
335
+ belongs_to :area, :class_name => 'MR::Model::Associations::FakeTestRecord'
336
+ has_many :comments, :class_name => 'MR::Model::Associations::FakeTestRecord'
337
+ end
338
+
339
+ class FakeTestModel
340
+ include MR::Model
341
+ record_class FakeTestRecord
342
+ belongs_to :area
343
+ has_many :comments
344
+ end
345
+
346
+ end
@@ -0,0 +1,92 @@
1
+ require 'assert'
2
+ require 'mr/model/configuration'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+
7
+ module MR::Model::Configuration
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "MR::Model::Configuration"
11
+ setup do
12
+ @model_class = Class.new do
13
+ include MR::Model::Configuration
14
+ end
15
+ end
16
+ subject{ @model_class }
17
+
18
+ should have_imeths :record_class
19
+
20
+ should "use much-plugin" do
21
+ assert_includes MuchPlugin, MR::Model::Configuration
22
+ end
23
+
24
+ should "allow reading and writing it's record class with `record_class`" do
25
+ subject.record_class FakeTestRecord
26
+ assert_equal FakeTestRecord, subject.record_class
27
+ end
28
+
29
+ should "raise a no record class error if a record class hasn't been set" do
30
+ assert_raises(MR::Model::NoRecordClassError){ subject.record_class }
31
+ end
32
+
33
+ should "raise an ArgumentError when a non MR::Record `record_class` is written" do
34
+ assert_raises(ArgumentError){ subject.record_class('fake-class') }
35
+ end
36
+
37
+ end
38
+
39
+ class InstanceTests < UnitTests
40
+
41
+ # These private methods are tested because they are an interace to the other
42
+ # mixins.
43
+
44
+ desc "for a model instance"
45
+ setup do
46
+ @model_class.class_eval do
47
+ record_class FakeTestRecord
48
+
49
+ def read_record
50
+ record
51
+ end
52
+
53
+ def write_record(record)
54
+ set_record(record)
55
+ end
56
+ end
57
+ @record = FakeTestRecord.new
58
+ @model = @model_class.new
59
+ end
60
+ subject{ @model }
61
+
62
+ should have_imeths :record_class
63
+
64
+ should "allow reading the model class' record class using `record_class`" do
65
+ assert_equal @model_class.record_class, subject.record_class
66
+ end
67
+
68
+ should "allow reading the `record` through the protected method" do
69
+ subject.write_record(@record)
70
+ assert_equal @record, subject.read_record
71
+ end
72
+
73
+ should "set the record's model when `set_record` is called" do
74
+ subject.write_record(@record)
75
+ assert_equal @model, @record.model
76
+ end
77
+
78
+ should "raise a no record error if a record hasn't been set" do
79
+ assert_raises(MR::Model::NoRecordError){ subject.read_record }
80
+ end
81
+
82
+ should "raise an invalid record error when setting record without an MR::Record" do
83
+ assert_raises(MR::Model::InvalidRecordError){ subject.write_record('fake') }
84
+ end
85
+
86
+ end
87
+
88
+ class FakeTestRecord
89
+ include MR::FakeRecord
90
+ end
91
+
92
+ end
@@ -0,0 +1,278 @@
1
+ require 'assert'
2
+ require 'mr/model/fields'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+
7
+ module MR::Model::Fields
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "MR::Model::Fields"
11
+ setup do
12
+ @model_class = Class.new do
13
+ include MR::Model::Fields
14
+ def initialize(record); set_record record; end
15
+ end
16
+ @record = FakeTestRecord.new
17
+ end
18
+ subject{ @model_class }
19
+
20
+ should have_imeths :fields, :field_reader, :field_writer, :field_accessor
21
+ should have_imeths :field_names
22
+
23
+ should "use much-plugin" do
24
+ assert_includes MuchPlugin, MR::Model::Fields
25
+ end
26
+
27
+ should "include Model::Configuration mixin" do
28
+ assert_includes MR::Model::Configuration, subject
29
+ end
30
+
31
+ should "return an instance of a FieldSet using `fields`" do
32
+ fields = subject.fields
33
+ assert_instance_of MR::Model::FieldSet, fields
34
+ assert_same fields, subject.fields
35
+ end
36
+
37
+ should "add reader methods for a field using `field_reader`" do
38
+ subject.field_reader :name
39
+ @record.name = 'test'
40
+ model = subject.new(@record)
41
+
42
+ assert_respond_to :name, model
43
+ assert_respond_to :name_was, model
44
+ assert_respond_to :name_changed?, model
45
+ assert_equal @record.name, model.name
46
+ assert_equal @record.name_was, model.name_was
47
+ assert_equal @record.name_changed?, model.name_changed?
48
+ end
49
+
50
+ should "add writer methods for a field using `field_writer`" do
51
+ subject.field_writer :name
52
+ model = subject.new(@record)
53
+
54
+ assert_respond_to :name=, model
55
+ model.name = 'test'
56
+ assert_equal 'test', @record.name
57
+ end
58
+
59
+ should "add accessor methods for a field using `field_accessor`" do
60
+ subject.field_accessor :name
61
+ model = subject.new(@record)
62
+
63
+ assert_respond_to :name, model
64
+ assert_respond_to :name_changed?, model
65
+ assert_respond_to :name=, model
66
+ model.name = 'test'
67
+ assert_equal 'test', @record.name
68
+ assert_equal @record.name, model.name
69
+ assert_equal @record.name_changed?, model.name_changed?
70
+ end
71
+
72
+ should "know its field names" do
73
+ assert_equal [], subject.field_names
74
+
75
+ subject.field_reader :field1
76
+ subject.field_writer :field2
77
+ subject.field_accessor :field3
78
+
79
+ result = subject.field_names
80
+ assert_equal subject.fields.names, result
81
+ end
82
+
83
+ end
84
+
85
+ class InstanceTests < UnitTests
86
+ desc "for a model instance"
87
+ setup do
88
+ @model_class.class_eval do
89
+ field_accessor :name, :active, :description
90
+ end
91
+ @model = @model_class.new(@record)
92
+ end
93
+ subject{ @model }
94
+
95
+ should have_imeths :fields, :fields=
96
+
97
+ should "allow reading all the fields using `fields`" do
98
+ subject.name = 'test'
99
+ subject.active = true
100
+ subject.description = 'desc'
101
+
102
+ expected = {
103
+ 'name' => 'test',
104
+ 'active' => true,
105
+ 'description' => 'desc'
106
+ }
107
+ assert_equal expected, subject.fields
108
+ end
109
+
110
+ should "allow writing multiple fields using `fields=`" do
111
+ subject.fields = {
112
+ 'name' => 'test',
113
+ 'active' => true,
114
+ 'description' => 'desc'
115
+ }
116
+ assert_equal 'test', subject.name
117
+ assert_equal true, subject.active
118
+ assert_equal 'desc', subject.description
119
+ end
120
+
121
+ should "raise an ArgumentError when a non-Hash is passed to `fields=`" do
122
+ assert_raises(ArgumentError){ subject.fields = 'test' }
123
+ end
124
+
125
+ should "raise a no field error when writing a bad field using `fields=`" do
126
+ assert_raises(MR::Model::NoFieldError) do
127
+ subject.fields = { :not_valid => 'test' }
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ class FieldSetTests < UnitTests
134
+ desc "FieldSet"
135
+ setup do
136
+ @fields_set = MR::Model::FieldSet.new
137
+ end
138
+ subject{ @fields_set }
139
+
140
+ should have_imeths :names, :find, :get
141
+ should have_imeths :read_all, :batch_write
142
+ should have_imeths :add_reader, :add_writer
143
+
144
+ should "know its field names" do
145
+ assert_equal [], subject.names
146
+
147
+ subject.add_reader(:field1, @model_class)
148
+ subject.add_writer(:field2, @model_class)
149
+
150
+ assert_equal 2, subject.names.size
151
+ assert_includes 'field1', subject.names
152
+ assert_includes 'field2', subject.names
153
+ end
154
+
155
+ should "return an existing field or raise a no field error using `find`" do
156
+ subject.get('name')
157
+ field = subject.find('name')
158
+ assert_instance_of MR::Model::Field, field
159
+ assert_equal 'name', field.name
160
+
161
+ assert_raises(MR::Model::NoFieldError){ subject.find('not_valid') }
162
+ end
163
+
164
+ should "return an existing field or build a new field using `get`" do
165
+ field = subject.get('name')
166
+ assert_instance_of MR::Model::Field, field
167
+ assert_equal 'name', field.name
168
+ assert_same field, subject.get('name')
169
+ end
170
+
171
+ should "return a hash of field names and values using `read_all`" do
172
+ assert_equal({}, subject.read_all(@record))
173
+ subject.get('name')
174
+ @record.name = 'test'
175
+ assert_equal({ 'name' => 'test' }, subject.read_all(@record))
176
+ end
177
+
178
+ should "set multiple field values using `batch_write`" do
179
+ subject.get('name')
180
+ subject.get('active')
181
+ values = { :name => 'test', :active => true }
182
+ subject.batch_write(values, @record)
183
+
184
+ assert_equal 'test', @record.name
185
+ assert_equal true, @record.active
186
+ end
187
+
188
+ should "add a field and define reader methods for it using `add_reader`" do
189
+ subject.add_reader('name', @model_class)
190
+
191
+ assert_instance_of MR::Model::Field, subject.find('name')
192
+ model = @model_class.new(@record)
193
+ assert_respond_to :name, model
194
+ assert_respond_to :name_changed?, model
195
+ end
196
+
197
+ should "add a field and define writer methods for it using `add_writer`" do
198
+ subject.add_writer('name', @model_class)
199
+
200
+ assert_instance_of MR::Model::Field, subject.find('name')
201
+ model = @model_class.new(@record)
202
+ assert_respond_to :name=, model
203
+ end
204
+
205
+ end
206
+
207
+ class FieldTests < UnitTests
208
+ desc "Field"
209
+ setup do
210
+ @field = MR::Model::Field.new('name')
211
+ end
212
+ subject{ @field }
213
+
214
+ should have_readers :name
215
+ should have_readers :reader_method_name, :writer_method_name
216
+ should have_readers :was_method_name, :changed_method_name
217
+
218
+ should have_imeths :read, :write, :was, :changed?
219
+ should have_imeths :define_reader_on, :define_writer_on
220
+
221
+ should "know it's name and method names" do
222
+ assert_equal 'name', subject.name
223
+ assert_equal 'name', subject.reader_method_name
224
+ assert_equal 'name_was', subject.was_method_name
225
+ assert_equal 'name_changed?', subject.changed_method_name
226
+ assert_equal 'name=', subject.writer_method_name
227
+ end
228
+
229
+ should "read a record's attribute using `read`" do
230
+ @record.name = 'test'
231
+ assert_equal 'test', subject.read(@record)
232
+ end
233
+
234
+ should "write a record's attribute using `write`" do
235
+ subject.write('test', @record)
236
+ assert_equal 'test', @record.name
237
+ end
238
+
239
+ should "read a record attribute's prevous value using `was`" do
240
+ assert_nil subject.was(@record)
241
+ @record.name = 'Test'
242
+ assert_nil subject.was(@record)
243
+ @record.save!
244
+ assert_equal 'Test', subject.was(@record)
245
+ @record.name = 'New test'
246
+ assert_equal 'Test', subject.was(@record)
247
+ end
248
+
249
+ should "return if a record's attribute has changed using `changed`" do
250
+ assert_equal false, subject.changed?(@record)
251
+ subject.write('test', @record)
252
+ assert_equal true, subject.changed?(@record)
253
+ end
254
+
255
+ should "define reader methods using `define_reader_on`" do
256
+ subject.define_reader_on(@model_class)
257
+ model = @model_class.new(@record)
258
+ assert_respond_to :name, model
259
+ assert_respond_to :name_was, model
260
+ assert_respond_to :name_changed?, model
261
+ end
262
+
263
+ should "define writer methods using `define_writer_on`" do
264
+ subject.define_writer_on(@model_class)
265
+ model = @model_class.new(@record)
266
+ assert_respond_to :name=, model
267
+ end
268
+
269
+ end
270
+
271
+ class FakeTestRecord
272
+ include MR::FakeRecord
273
+ attribute :name, :string
274
+ attribute :active, :boolean
275
+ attribute :description, :text
276
+ end
277
+
278
+ end