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,114 @@
1
+ require 'assert'
2
+ require 'mr/model/persistence'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+
7
+ module MR::Model::Persistence
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "MR::Model::Persistence"
11
+ setup do
12
+ @model_class = Class.new do
13
+ include MR::Model::Persistence
14
+ record_class FakeTestRecord
15
+ def initialize(record); set_record record; end
16
+ end
17
+ end
18
+ subject{ @model_class }
19
+
20
+ should have_imeths :transaction
21
+
22
+ should "use much-plugin" do
23
+ assert_includes MuchPlugin, MR::Model::Persistence
24
+ end
25
+
26
+ should "call the record class's transaction method using `transaction`" do
27
+ yielded = nil
28
+ subject.transaction{ yielded = true }
29
+ assert yielded
30
+ end
31
+
32
+ end
33
+
34
+ class InstanceTests < UnitTests
35
+ desc "for a model instance"
36
+ setup do
37
+ @record = FakeTestRecord.new
38
+ @model = @model_class.new(@record)
39
+ end
40
+ subject{ @model }
41
+
42
+ should have_imeths :save, :destroy
43
+ should have_imeths :transaction
44
+ should have_imeths :errors, :valid?
45
+ should have_imeths :new?, :destroyed?
46
+
47
+ should "save the record using `save`" do
48
+ assert @record.new_record?
49
+ subject.save
50
+ assert_not @record.new_record?
51
+ end
52
+
53
+ should "destroy the record using `destroy`" do
54
+ assert_not @record.destroyed?
55
+ subject.destroy
56
+ assert @record.destroyed?
57
+ end
58
+
59
+ should "call the record's transaction method using `transaction`" do
60
+ yielded = nil
61
+ subject.transaction{ yielded = true }
62
+ assert yielded
63
+ end
64
+
65
+ should "return the record's error messages using `errors`" do
66
+ @record.errors.add(:name, 'something went wrong')
67
+ assert_equal @record.errors.messages, subject.errors
68
+ end
69
+
70
+ should "call the record's valid method using `valid?`" do
71
+ assert_equal true, subject.valid?
72
+ @record.errors.add(:name, 'something went wrong')
73
+ assert_equal false, subject.valid?
74
+ end
75
+
76
+ should "call the record's new record method using `new?`" do
77
+ assert_equal true, subject.new?
78
+ @record.save!
79
+ assert_equal false, subject.new?
80
+ end
81
+
82
+ should "call the record's destroyed method using `destroyed?`" do
83
+ @record.save!
84
+ assert_equal false, subject.destroyed?
85
+ @record.destroy
86
+ assert_equal true, subject.destroyed?
87
+ end
88
+
89
+ should "raise an invalid error when calling `save` with an invalid record" do
90
+ @record.errors.add(:name, 'something went wrong')
91
+ @record.errors.add(:name, 'another thing failed')
92
+ @record.errors.add(:active, 'is not valid')
93
+
94
+ exception = nil
95
+ begin; subject.save; rescue StandardError => exception; end
96
+
97
+ assert_instance_of MR::Model::InvalidError, exception
98
+ assert_equal subject.errors, exception.errors
99
+ description = subject.errors.map do |(attribute, messages)|
100
+ messages.map{|message| "#{attribute.inspect} #{message}" }
101
+ end.sort.join(', ')
102
+ expected = "Invalid #{subject.class} couldn't be saved: #{description}"
103
+ assert_equal expected, exception.message
104
+ expected = "test/unit/model/persistence_tests.rb"
105
+ assert_match expected, exception.backtrace.first
106
+ end
107
+
108
+ end
109
+
110
+ class FakeTestRecord
111
+ include MR::FakeRecord
112
+ end
113
+
114
+ end
@@ -0,0 +1,137 @@
1
+ require 'assert'
2
+ require 'mr/model'
3
+
4
+ require 'much-plugin'
5
+ require 'mr/fake_record'
6
+
7
+ module MR::Model
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "MR::Model"
11
+ setup do
12
+ @model_class = Class.new do
13
+ include MR::Model
14
+ record_class FakeTestRecord
15
+ field_reader :id
16
+ field_accessor :name, :active
17
+ end
18
+ end
19
+ subject{ @model_class }
20
+
21
+ should have_imeths :find, :all
22
+
23
+ should "use much-plugin" do
24
+ assert_includes MuchPlugin, MR::Model
25
+ end
26
+
27
+ should "include the configuration, fields, associations and persistence mixins" do
28
+ assert_includes MR::Model::Configuration, subject
29
+ assert_includes MR::Model::Fields, subject
30
+ assert_includes MR::Model::Associations, subject
31
+ assert_includes MR::Model::Persistence, subject
32
+ end
33
+
34
+ should "allow passing a record to it's initialize" do
35
+ fake_record = FakeTestRecord.new(:name => 'test')
36
+ model = subject.new(fake_record)
37
+ assert_equal 'test', model.name
38
+ end
39
+
40
+ should "allow passing fields to it's initialize" do
41
+ model = subject.new(:name => 'test')
42
+ assert_equal 'test', model.name
43
+ end
44
+
45
+ should "allow passing both a record and fields to it's initialize" do
46
+ fake_record = FakeTestRecord.new(:name => 'test1')
47
+ model = subject.new(fake_record, :name => 'test2')
48
+ assert_equal 'test2', model.name
49
+ assert_equal 'test2', fake_record.name
50
+ end
51
+
52
+ end
53
+
54
+ class WithRecordClassSpyTests < UnitTests
55
+ setup do
56
+ @fake_records = [*1..2].map{ FakeTestRecord.new.tap(&:save!) }
57
+ RecordClassSpy.fake_records = @fake_records
58
+ @model_class.record_class RecordClassSpy
59
+ end
60
+ teardown do
61
+ RecordClassSpy.fake_records = nil
62
+ end
63
+
64
+ should "call find on the record class using `find`" do
65
+ fake_record = @fake_records.first
66
+ model = subject.find(fake_record.id)
67
+ assert_equal subject.new(fake_record), model
68
+ end
69
+
70
+ should "call all on the record class and map building instances using `all`" do
71
+ exp = @fake_records.map{ |fake_record| subject.new(fake_record) }
72
+ assert_equal exp, subject.all
73
+ end
74
+
75
+ end
76
+
77
+ class InstanceTests < UnitTests
78
+ desc "for a model instance"
79
+ setup do
80
+ @fake_record = FakeTestRecord.new
81
+ @model = @model_class.new(@fake_record, :name => 'test', :active => true)
82
+ @model.save
83
+ end
84
+ subject{ @model }
85
+
86
+ should have_imeths :==, :eql?, :hash, :inspect
87
+
88
+ should "return a readable inspect" do
89
+ object_hex = (subject.object_id << 1).to_s(16)
90
+ expected = "#<#{subject.class}:0x#{object_hex} @active=true " \
91
+ "@id=#{subject.id} @name=\"test\">"
92
+ assert_equal expected, subject.inspect
93
+ end
94
+
95
+ should "be comparable using `==`" do
96
+ same_model = @model_class.new(@fake_record)
97
+ assert_equal same_model, subject
98
+ other_model = @model_class.new.tap(&:save)
99
+ assert_not_equal other_model, subject
100
+ end
101
+
102
+ should "be comparable using `eql?`" do
103
+ same_model = @model_class.new(@fake_record)
104
+ assert_true subject.eql?(same_model)
105
+ other_model = @model_class.new.tap(&:save)
106
+ assert_false subject.eql?(other_model)
107
+ end
108
+
109
+ should "demeter its fixnum hash value to its record" do
110
+ assert_equal @fake_record.hash, subject.hash
111
+ end
112
+
113
+ end
114
+
115
+ class FakeTestRecord
116
+ include MR::FakeRecord
117
+ attribute :name, :string
118
+ attribute :active, :boolean
119
+ end
120
+
121
+ class RecordClassSpy
122
+ include MR::Record
123
+
124
+ def self.fake_records=(values)
125
+ @fake_records = values
126
+ end
127
+
128
+ def self.find(id)
129
+ @fake_records.detect{ |fake_record| fake_record.id == id }
130
+ end
131
+
132
+ def self.all
133
+ @fake_records
134
+ end
135
+ end
136
+
137
+ end
@@ -0,0 +1,300 @@
1
+ require 'assert'
2
+ require 'mr/query'
3
+
4
+ require 'ardb/relation_spy'
5
+ require 'mr/fake_record'
6
+ require 'mr/model'
7
+
8
+ class MR::Query
9
+
10
+ class UnitTests < Assert::Context
11
+ desc "MR::Query"
12
+ setup do
13
+ @relation = FakeTestRecord.scoped
14
+ @relation.results = [
15
+ FakeTestRecord.new(:id => 1),
16
+ FakeTestRecord.new(:id => 2)
17
+ ]
18
+ @model_class = FakeTestModel
19
+
20
+ @count_relation_built_with = nil
21
+ @count_relation = nil
22
+ @count_called = false
23
+ Assert.stub(CountRelation, :new) do |*args|
24
+ @count_relation_built_with = args
25
+ @count_relation = Assert.stub_send(CountRelation, :new, *args)
26
+ Assert.stub(@count_relation, :count) do
27
+ @count_called = true
28
+ Assert.stub_send(@count_relation, :count)
29
+ end
30
+ @count_relation
31
+ end
32
+
33
+ @query = MR::Query.new(FakeTestModel, @relation)
34
+ end
35
+ subject{ @query }
36
+
37
+ should have_readers :model_class, :relation
38
+ should have_imeths :results, :results!, :first, :first!, :count, :count!
39
+
40
+ should "know its results" do
41
+ results = subject.results
42
+ assert_equal @relation.results.map{ |r| @model_class.new(r) }, results
43
+ assert_same results, subject.results
44
+ end
45
+
46
+ should "allow re-caching its results" do
47
+ results = subject.results
48
+ assert_same results, subject.results
49
+
50
+ new_results = subject.results!
51
+ assert_not_same results, new_results
52
+ assert_same new_results, subject.results
53
+
54
+ assert_not_same new_results, subject.results!
55
+ end
56
+
57
+ should "know its first result" do
58
+ result = subject.first
59
+ assert_equal @model_class.new(@relation.results.first), result
60
+ assert_same result, subject.first
61
+ end
62
+
63
+ should "return `nil` for its first result if there are no results" do
64
+ @relation.results = []
65
+ assert_nil subject.first
66
+ end
67
+
68
+ should "allow re-caching its first result" do
69
+ result = subject.first
70
+ assert_same result, subject.first
71
+
72
+ new_result = subject.first!
73
+ assert_not_same result, new_result
74
+ assert_same new_result, subject.first
75
+
76
+ assert_not_same new_result, subject.first!
77
+ end
78
+
79
+ should "know how to run a count query to get its count" do
80
+ assert_equal 2, subject.count
81
+ assert_equal [@relation], @count_relation_built_with
82
+ assert_true @count_called
83
+
84
+ # test that it caches the count, a count relation isn't rebuilt and count
85
+ # isn't called
86
+ @count_relation_built_with = nil
87
+ @count_called = false
88
+ assert_equal 2, subject.count
89
+ assert_nil @count_relation_built_with
90
+ assert_false @count_called
91
+ end
92
+
93
+ should "allow re-caching its count" do
94
+ subject.count
95
+ assert_equal [@relation], @count_relation_built_with
96
+
97
+ @count_relation_built_with = nil
98
+ @count_called = false
99
+ subject.count!
100
+ assert_nil @count_relation_built_with
101
+ assert_true @count_called
102
+ end
103
+
104
+ should "know how to build a paged query" do
105
+ paged_query = subject.paged(1, 10)
106
+
107
+ assert_instance_of MR::PagedQuery, paged_query
108
+ assert_equal 1, paged_query.page_num
109
+ assert_equal 10, paged_query.page_size
110
+ end
111
+
112
+ end
113
+
114
+ class PagedQueryTests < UnitTests
115
+ desc "MR::PagedQuery"
116
+ setup do
117
+ @unpaged_relation = @relation.dup
118
+ @paged_query_class = MR::PagedQuery
119
+ @paged_query = @paged_query_class.new(@query, 1, 1)
120
+ end
121
+ subject{ @paged_query }
122
+
123
+ should have_readers :page_num, :page_size, :page_offset
124
+ should have_imeths :total_count, :total_count!
125
+ should have_imeths :has_next_page?, :is_last_page?
126
+
127
+ should "be an mr query" do
128
+ assert_kind_of MR::Query, subject
129
+ end
130
+
131
+ should "default its page number and page size" do
132
+ paged_query = @paged_query_class.new(@query)
133
+
134
+ assert_equal 1, paged_query.page_num
135
+ assert_equal 25, paged_query.page_size
136
+ end
137
+
138
+ should "not allow invalid page numbers or page size values" do
139
+ paged_query = @paged_query_class.new(@query, -1, -10)
140
+
141
+ assert_equal 1, paged_query.page_num
142
+ assert_equal 25, paged_query.page_size
143
+
144
+ paged_query = @paged_query_class.new(@query, 'a', 10.4)
145
+
146
+ # 'a'.to_i is 0, thus it forces it to 1
147
+ assert_equal 1, paged_query.page_num
148
+ # 10.4.to_i is 10, which is valid
149
+ assert_equal 10.4.to_i, paged_query.page_size
150
+ end
151
+
152
+ should "correctly calculate limits and offsets" do
153
+ paged_query = @paged_query_class.new(@query, 1, 10)
154
+
155
+ assert_equal 0, @relation.offset_value
156
+ assert_equal 10, @relation.limit_value
157
+
158
+ paged_query = @paged_query_class.new(@query, 5, 7)
159
+
160
+ assert_equal 28, @relation.offset_value
161
+ assert_equal 7, @relation.limit_value
162
+ end
163
+
164
+ should "know its paged results" do
165
+ exp = @relation.results[0, 1].map{ |r| @model_class.new(r) }
166
+ assert_equal exp, subject.results
167
+ end
168
+
169
+ should "know how to run a paged count query to get its count" do
170
+ assert_equal 1, subject.count
171
+ assert_equal [@relation], @count_relation_built_with
172
+ assert_true @count_called
173
+ end
174
+
175
+ should "know how to run an unpaged count query to get its total count" do
176
+ assert_equal 2, subject.total_count
177
+ assert_equal [@unpaged_relation], @count_relation_built_with
178
+ assert_true @count_called
179
+
180
+ # test that it caches the total count, a count relation isn't rebuilt and
181
+ # count isn't called
182
+ @count_relation_built_with = nil
183
+ @count_called = false
184
+ assert_equal 2, subject.total_count
185
+ assert_nil @count_relation_built_with
186
+ assert_false @count_called
187
+ end
188
+
189
+ should "allow re-caching its total count" do
190
+ subject.total_count
191
+ assert_equal [@unpaged_relation], @count_relation_built_with
192
+
193
+ @count_relation_built_with = nil
194
+ @count_called = false
195
+ subject.total_count!
196
+ assert_nil @count_relation_built_with
197
+ assert_true @count_called
198
+ end
199
+
200
+ should "know if it has a next page or is the last page" do
201
+ page_num = Factory.integer(10)
202
+ page_size = Factory.integer(10)
203
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
204
+
205
+ page_end = paged_query.page_offset + page_size
206
+ total_count = Factory.integer(page_size - 1) + page_end
207
+ Assert.stub(paged_query, :total_count){ total_count }
208
+
209
+ assert_true paged_query.has_next_page?
210
+ assert_false paged_query.is_last_page?
211
+
212
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
213
+ total_count = Factory.integer(page_size - 1) + paged_query.page_offset
214
+ Assert.stub(paged_query, :total_count){ total_count }
215
+
216
+ assert_false paged_query.has_next_page?
217
+ assert_true paged_query.is_last_page?
218
+
219
+ paged_query = @paged_query_class.new(@query, page_num, page_size)
220
+ Assert.stub(paged_query, :total_count){ page_end }
221
+
222
+ assert_false paged_query.has_next_page?
223
+ assert_true paged_query.is_last_page?
224
+ end
225
+
226
+ end
227
+
228
+ class CountRelationTests < UnitTests
229
+ desc "CountRelation"
230
+ setup do
231
+ @relation.select('some_table.some_column')
232
+ @relation.where('some_table.some_column = ?', 1)
233
+ @relation.order('some_table.some_column DESC')
234
+ end
235
+ subject{ CountRelation }
236
+
237
+ should "return the relation with emptied select and order values when " \
238
+ "the original relation was not grouped" do
239
+ count_relation = subject.new(@relation)
240
+
241
+ select_expressions = count_relation.applied.select{ |e| e.type == :select }
242
+ assert_true select_expressions.empty?
243
+ order_expressions = count_relation.applied.select{ |e| e.type == :order }
244
+ assert_true order_expressions.empty?
245
+ # still has the where expression
246
+ where_expressions = count_relation.applied.select{ |e| e.type == :where }
247
+ assert_equal 1, where_expressions.size
248
+ end
249
+
250
+ should "return a new relation for counting a subquery of the original " \
251
+ "relation with emptied select and order values when " \
252
+ "the original relation was grouped" do
253
+ @relation.group_values = [ 'table.id' ]
254
+ count_relation = subject.new(@relation)
255
+
256
+ assert_not_equal count_relation.applied, @relation.applied
257
+ assert_equal 1, count_relation.applied.size
258
+
259
+ from_expression = count_relation.applied.first
260
+ assert_equal :from, from_expression.type
261
+
262
+ exp_relation = @relation.except(:select, :order).select('1')
263
+ exp = "(#{exp_relation.to_sql}) AS grouped_records"
264
+ assert_equal [exp], from_expression.args
265
+ end
266
+
267
+ end
268
+
269
+ class RelationSpy < Ardb::RelationSpy
270
+ attr_reader :klass
271
+ attr_accessor :group_values
272
+
273
+ def initialize(klass, *args)
274
+ super(*args)
275
+ @klass = klass
276
+ @group_values = []
277
+ end
278
+
279
+ # this is just a random readable string, it looks like:
280
+ # select(id).where(some_id, 1)
281
+ def to_sql
282
+ @applied.reverse.map{ |e| "#{e.type}(#{e.args.join(", ")})" }.join('.')
283
+ end
284
+
285
+ end
286
+
287
+ class FakeTestRecord
288
+ include MR::FakeRecord
289
+
290
+ def self.scoped
291
+ RelationSpy.new(self)
292
+ end
293
+ end
294
+
295
+ class FakeTestModel
296
+ include MR::Model
297
+ record_class FakeTestRecord
298
+ end
299
+
300
+ end