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,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