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,56 @@
1
+ require 'assert'
2
+ require 'mr/read_model/data'
3
+
4
+ module MR::ReadModel::Data
5
+
6
+ class UnitTests < Assert::Context
7
+ desc "MR::ReadModel::Data"
8
+ setup do
9
+ @read_model_class = Class.new do
10
+ include MR::ReadModel::Data
11
+ end
12
+ end
13
+ subject{ @read_model_class }
14
+
15
+ end
16
+
17
+ class InstanceTests < UnitTests
18
+
19
+ # These private methods are tested because they are an interace to the other
20
+ # mixins.
21
+
22
+ desc "for a read model instance"
23
+ setup do
24
+ @read_model_class.class_eval do
25
+
26
+ def read_data
27
+ self.read_model_data
28
+ end
29
+
30
+ def write_data(data)
31
+ set_read_model_data(data)
32
+ end
33
+
34
+ end
35
+ @data = {}
36
+ @read_model = @read_model_class.new
37
+ end
38
+ subject{ @read_model }
39
+
40
+ should "allow reading the `data` through the protected method" do
41
+ subject.write_data(@data)
42
+ assert_equal @data, subject.read_data
43
+ end
44
+
45
+ should "raise a no record error if a record hasn't been set" do
46
+ assert_raises(MR::ReadModel::NoDataError){ subject.read_data }
47
+ end
48
+
49
+ should "raise an invalid data error when setting data that doesn't " \
50
+ "respond to the index (`[]`) method" do
51
+ assert_raises(MR::ReadModel::InvalidDataError){ subject.write_data(true) }
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,416 @@
1
+ require 'assert'
2
+ require 'mr/read_model/fields'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/factory'
6
+ require 'mr/json_field'
7
+ require 'mr/read_model'
8
+ require 'mr/read_model/data'
9
+
10
+ module MR::ReadModel::Fields
11
+
12
+ class UnitTests < Assert::Context
13
+ desc "MR::ReadModel::Fields"
14
+ setup do
15
+ @read_model_class = Class.new do
16
+ include MR::ReadModel::Fields
17
+ def initialize(data); set_read_model_data(data); end
18
+ end
19
+
20
+ @struct_class = Class.new{ include MR::ReadModelStruct }
21
+ end
22
+ subject{ @read_model_class }
23
+
24
+ should have_imeths :fields, :field
25
+ should have_imeths :json_struct_list, :json_struct_lists
26
+ should have_imeths :json_struct_obj, :json_struct_objs
27
+
28
+ should "use much-plugin" do
29
+ assert_includes MuchPlugin, MR::ReadModel::Fields
30
+ end
31
+
32
+ should "use the mr read model data mixin" do
33
+ assert_includes MR::ReadModel::Data, subject
34
+ end
35
+
36
+ should "return a FieldSet using `fields`" do
37
+ fields = subject.fields
38
+ assert_instance_of MR::ReadModel::FieldSet, fields
39
+ assert_same fields, subject.fields
40
+ end
41
+
42
+ should "add a field to the FieldSet using `field`" do
43
+ subject.field :test, :string
44
+ field = subject.fields.find(:test)
45
+
46
+ assert_instance_of MR::ReadModel::Field, field
47
+ assert_equal 'test', field.name
48
+ assert_equal :string, field.type
49
+ end
50
+
51
+ should "define a reader method for the field using `field`" do
52
+ subject.field :test, :string
53
+ data = { 'test' => 'something' }
54
+ read_model = subject.new(data)
55
+
56
+ assert_respond_to :test, read_model
57
+ assert_equal 'something', read_model.test
58
+ end
59
+
60
+ should "raise an argument error when `field` is passed an invalid field type" do
61
+ assert_raises(ArgumentError){ subject.field(:test, :invalid) }
62
+ end
63
+
64
+ should "know how to add a json struct list" do
65
+ field_name = Factory.string
66
+ struct_class_name = Factory.boolean ? @struct_class.to_s : @struct_class
67
+ subject.json_struct_list(field_name, struct_class_name)
68
+
69
+ assert_equal 1, subject.json_struct_lists.size
70
+ field = subject.json_struct_lists.first
71
+ assert_instance_of MR::ReadModel::JsonStructListField, field
72
+ assert_equal field_name, field.name
73
+ assert_equal struct_class_name, field.struct_class_name
74
+ assert_respond_to field.name, subject.new({})
75
+ end
76
+
77
+ should "not add duplicate json struct lists" do
78
+ assert_equal 0, subject.json_struct_lists.size
79
+
80
+ field_name = Factory.string
81
+ subject.json_struct_list(field_name, @struct_class)
82
+
83
+ assert_equal 1, subject.json_struct_lists.size
84
+
85
+ subject.json_struct_list(field_name, @struct_class)
86
+
87
+ assert_equal 1, subject.json_struct_lists.size
88
+ end
89
+
90
+ should "know how to add a json struct obj" do
91
+ field_name = Factory.string
92
+ struct_class_name = Factory.boolean ? @struct_class.to_s : @struct_class
93
+ subject.json_struct_obj(field_name, struct_class_name)
94
+
95
+ assert_equal 1, subject.json_struct_objs.size
96
+ field = subject.json_struct_objs.first
97
+ assert_instance_of MR::ReadModel::JsonStructObjField, field
98
+ assert_equal field_name, field.name
99
+ assert_equal struct_class_name, field.struct_class_name
100
+ assert_respond_to field.name, subject.new({})
101
+ end
102
+
103
+ should "not add duplicate json struct objs" do
104
+ assert_equal 0, subject.json_struct_objs.size
105
+
106
+ field_name = Factory.string
107
+ subject.json_struct_obj(field_name, @struct_class)
108
+
109
+ assert_equal 1, subject.json_struct_objs.size
110
+
111
+ subject.json_struct_obj(field_name, @struct_class)
112
+
113
+ assert_equal 1, subject.json_struct_objs.size
114
+ end
115
+
116
+ end
117
+
118
+ class InstanceTests < UnitTests
119
+ desc "for a read model instance"
120
+ setup do
121
+ @read_model_class.field(:name, :string)
122
+ @read_model_class.field(:active, :boolean)
123
+ @read_model_class.field(:description, :string)
124
+ @read_model_class.json_struct_obj(:struct_obj, @struct_class)
125
+ @read_model_class.json_struct_list(:struct_list, @struct_class)
126
+
127
+ @struct_class.field(:name, :string)
128
+
129
+ @struct_list_datas = Factory.integer(3).times.map do
130
+ { 'name' => Factory.string }
131
+ end
132
+ @struct_obj_data = { 'name' => Factory.string }
133
+ @data = {
134
+ 'name' => 'test',
135
+ 'active' => true,
136
+ 'description' => 'desc',
137
+ 'struct_list' => MR::JsonField.encode(@struct_list_datas),
138
+ 'struct_obj' => MR::JsonField.encode(@struct_obj_data)
139
+ }
140
+ @read_model = @read_model_class.new(@data)
141
+ end
142
+ subject{ @read_model }
143
+
144
+ should have_imeths :fields
145
+
146
+ should "know how to read json struct list fields" do
147
+ results = subject.struct_list
148
+
149
+ assert_instance_of Array, results
150
+ assert_equal @struct_list_datas.size, results.size
151
+ results.each_with_index do |result, n|
152
+ assert_instance_of @struct_class, result
153
+ exp = @struct_list_datas[n]['name']
154
+ assert_equal exp, result.name
155
+ end
156
+ assert_same results, subject.struct_list
157
+ end
158
+
159
+ should "know how to read json struct obj fields" do
160
+ result = subject.struct_obj
161
+
162
+ assert_instance_of @struct_class, result
163
+ assert_equal @struct_obj_data['name'], result.name
164
+ assert_same result, subject.struct_obj
165
+ end
166
+
167
+ should "allow reading all the fields using `fields`" do
168
+ exp = {
169
+ 'name' => subject.name,
170
+ 'active' => subject.active,
171
+ 'description' => subject.description,
172
+ 'struct_list' => subject.struct_list,
173
+ 'struct_obj' => subject.struct_obj
174
+ }
175
+ assert_equal exp, subject.fields
176
+ end
177
+
178
+ end
179
+
180
+ class FieldSetTests < UnitTests
181
+ desc "FieldSet"
182
+ setup do
183
+ @field_set = MR::ReadModel::FieldSet.new.tap do |s|
184
+ s.add :name, :string
185
+ s.add :active, :boolean
186
+ end
187
+ end
188
+ subject{ @field_set }
189
+
190
+ should have_imeths :find, :read_all
191
+ should have_imeths :add
192
+ should have_imeths :each
193
+
194
+ should "be enumerable" do
195
+ assert_includes Enumerable, MR::ReadModel::FieldSet
196
+ end
197
+
198
+ should "return all of it's fields values using `read_all`" do
199
+ @data = { 'name' => 'Name', 'active' => 'true' }
200
+ expected = { 'name' => 'Name', 'active' => true }
201
+ assert_equal expected, subject.read_all(@data)
202
+ end
203
+
204
+ should "yield it's fields using `each`" do
205
+ yielded_fields = []
206
+ subject.each{ |f| yielded_fields << f }
207
+ assert_includes subject.find(:name), yielded_fields
208
+ assert_includes subject.find(:active), yielded_fields
209
+ end
210
+
211
+ end
212
+
213
+ class FieldTests < UnitTests
214
+ desc "Field"
215
+ setup do
216
+ @field = MR::ReadModel::Field.new(:test, :boolean)
217
+ end
218
+ subject{ @field }
219
+
220
+ should have_readers :name, :type
221
+ should have_readers :method_name, :ivar_name
222
+ should have_imeths :read, :define_on
223
+
224
+ should "know it's name and type" do
225
+ assert_equal 'test', subject.name
226
+ assert_equal :boolean, subject.type
227
+ end
228
+
229
+ should "know it's method name and ivar name" do
230
+ assert_equal 'test', subject.method_name
231
+ assert_equal '@test', subject.ivar_name
232
+ end
233
+
234
+ should "read a value and type-cast it from passed-in data using `read`" do
235
+ assert_equal true, subject.read('test' => 'true')
236
+ assert_equal false, subject.read('test' => 'false')
237
+ end
238
+
239
+ should "return `nil` when passed-in data's value is `nil` using `read`" do
240
+ assert_equal nil, subject.read('test' => nil)
241
+ end
242
+
243
+ should "define a reader method on an object using `define_on`" do
244
+ subject.define_on(@read_model_class)
245
+ read_model = @read_model_class.new('test' => 'true')
246
+
247
+ assert_respond_to subject.method_name, read_model
248
+ value = read_model.send(subject.method_name)
249
+ assert_same value, read_model.send(subject.method_name)
250
+ end
251
+
252
+ should "raise an invalid field type error when built with an invalid field type" do
253
+ assert_raises(MR::ReadModel::InvalidFieldTypeError) do
254
+ MR::ReadModel::Field.new(:test, :invalid)
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ class JsonStructFieldSetupTests < UnitTests
261
+ setup do
262
+ @read_model_class = TestReadModel
263
+ @field_name = Factory.string
264
+ @struct_class = @read_model_class::TestStruct
265
+ end
266
+ subject{ @field }
267
+
268
+ class TestReadModel
269
+ include MR::ReadModel
270
+
271
+ class TestStruct
272
+ include MR::ReadModelStruct
273
+
274
+ field :name, :string
275
+ end
276
+ end
277
+
278
+ end
279
+
280
+ class JsonStructFieldTests < JsonStructFieldSetupTests
281
+ desc "JsonStructField"
282
+ setup do
283
+ @field_class = MR::ReadModel::JsonStructField
284
+ @field = @field_class.new(@read_model_class, @field_name, @struct_class)
285
+ end
286
+
287
+ should have_readers :read_model_class, :name, :struct_class_name
288
+ should have_imeths :struct_class, :reader
289
+
290
+ should "know its struct class" do
291
+ assert_equal @struct_class, subject.struct_class
292
+
293
+ field = @field_class.new(@read_model_class, @field_name, 'TestStruct')
294
+ assert_equal @struct_class, field.struct_class
295
+
296
+ field = @field_class.new(@read_model_class, @field_name, '::GlobalTestReadModelStruct')
297
+ assert_equal GlobalTestReadModelStruct, field.struct_class
298
+ end
299
+
300
+ should "know how to read json struct data from a read model data" do
301
+ json_data = if Factory.boolean
302
+ Factory.integer(3).times.map{ { Factory.string => Factory.string } }
303
+ else
304
+ { Factory.string => Factory.string }
305
+ end
306
+ read_model_data = { @field_name => MR::JsonField.encode(json_data) }
307
+
308
+ assert_equal json_data, subject.reader(read_model_data)
309
+ end
310
+
311
+ should "know how to read json struct data when its read model data isn't JSON" do
312
+ json_data = if Factory.boolean
313
+ Factory.integer(3).times.map{ { Factory.string => Factory.string } }
314
+ else
315
+ { Factory.string => Factory.string }
316
+ end
317
+ read_model_data = { @field_name => json_data }
318
+
319
+ assert_equal json_data, subject.reader(read_model_data)
320
+ end
321
+
322
+ should "return `nil` if the read model data doesn't contain the json struct data" do
323
+ read_model_data = if Factory.boolean
324
+ { @field_name => nil }
325
+ else
326
+ {}
327
+ end
328
+
329
+ assert_nil subject.reader(read_model_data)
330
+ end
331
+
332
+ should "raise a custom message if the json struct data can't be decoded" do
333
+ read_model_data = { @field_name => Factory.text }
334
+ original_error_message = Factory.text
335
+ Assert.stub(MR::JsonField, :decode) do
336
+ raise MR::JsonField::InvalidJSONError, original_error_message
337
+ end
338
+
339
+ exception = assert_raises(MR::JsonField::InvalidJSONError) do
340
+ subject.reader(read_model_data)
341
+ end
342
+ exp = "can't decode `#{@field_name}` JSON: #{original_error_message}"
343
+ assert_equal exp, exception.message
344
+ end
345
+
346
+ class ::GlobalTestReadModelStruct
347
+ include MR::ReadModelStruct
348
+ end
349
+
350
+ end
351
+
352
+ class JsonStructListFieldTests < JsonStructFieldSetupTests
353
+ desc "JsonStructListField"
354
+ setup do
355
+ @field_class = MR::ReadModel::JsonStructListField
356
+ @field = @field_class.new(@read_model_class, @field_name, @struct_class)
357
+ end
358
+
359
+ should have_imeths :reader
360
+
361
+ should "know how to read json struct list data from a read model data" do
362
+ json_datas = Factory.integer(3).times.map{ { 'name' => Factory.string } }
363
+ read_model_data = { @field_name => MR::JsonField.encode(json_datas) }
364
+
365
+ exp = json_datas.map{ |data| @struct_class.new(data) }
366
+ assert_equal exp, subject.reader(read_model_data)
367
+
368
+ read_model_data = { @field_name => json_datas }
369
+ assert_equal exp, subject.reader(read_model_data)
370
+ end
371
+
372
+ should "return `nil` if the read model data doesn't contain the json struct list data" do
373
+ read_model_data = if Factory.boolean
374
+ { @field_name => nil }
375
+ else
376
+ {}
377
+ end
378
+
379
+ assert_nil subject.reader(read_model_data)
380
+ end
381
+
382
+ end
383
+
384
+ class JsonStructObjFieldTests < JsonStructFieldSetupTests
385
+ desc "JsonStructObjField"
386
+ setup do
387
+ @field_class = MR::ReadModel::JsonStructObjField
388
+ @field = @field_class.new(@read_model_class, @field_name, @struct_class)
389
+ end
390
+
391
+ should have_imeths :reader
392
+
393
+ should "know how to read json struct obj data from a read model data" do
394
+ json_data = { 'name' => Factory.string }
395
+ read_model_data = { @field_name => MR::JsonField.encode(json_data) }
396
+
397
+ exp = @struct_class.new(json_data)
398
+ assert_equal exp, subject.reader(read_model_data)
399
+
400
+ read_model_data = { @field_name => json_data }
401
+ assert_equal exp, subject.reader(read_model_data)
402
+ end
403
+
404
+ should "return `nil` if the read model data doesn't contain the json struct obj data" do
405
+ read_model_data = if Factory.boolean
406
+ { @field_name => nil }
407
+ else
408
+ {}
409
+ end
410
+
411
+ assert_nil subject.reader(read_model_data)
412
+ end
413
+
414
+ end
415
+
416
+ end