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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +13 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/bench/all.rb +4 -0
- data/bench/factory.rb +68 -0
- data/bench/fake_record.rb +174 -0
- data/bench/model.rb +201 -0
- data/bench/read_model.rb +191 -0
- data/bench/results/factory.txt +21 -0
- data/bench/results/fake_record.txt +37 -0
- data/bench/results/model.txt +44 -0
- data/bench/results/read_model.txt +46 -0
- data/bench/setup.rb +132 -0
- data/lib/mr.rb +11 -0
- data/lib/mr/after_commit.rb +49 -0
- data/lib/mr/after_commit/fake_record.rb +39 -0
- data/lib/mr/after_commit/record.rb +48 -0
- data/lib/mr/after_commit/record_procs_methods.rb +82 -0
- data/lib/mr/factory.rb +82 -0
- data/lib/mr/factory/config.rb +240 -0
- data/lib/mr/factory/model_factory.rb +103 -0
- data/lib/mr/factory/model_stack.rb +28 -0
- data/lib/mr/factory/read_model_factory.rb +104 -0
- data/lib/mr/factory/record_factory.rb +130 -0
- data/lib/mr/factory/record_stack.rb +219 -0
- data/lib/mr/fake_query.rb +53 -0
- data/lib/mr/fake_record.rb +58 -0
- data/lib/mr/fake_record/associations.rb +257 -0
- data/lib/mr/fake_record/attributes.rb +168 -0
- data/lib/mr/fake_record/persistence.rb +116 -0
- data/lib/mr/json_field.rb +180 -0
- data/lib/mr/json_field/fake_record.rb +31 -0
- data/lib/mr/json_field/record.rb +38 -0
- data/lib/mr/model.rb +67 -0
- data/lib/mr/model/associations.rb +161 -0
- data/lib/mr/model/configuration.rb +67 -0
- data/lib/mr/model/fields.rb +177 -0
- data/lib/mr/model/persistence.rb +79 -0
- data/lib/mr/query.rb +126 -0
- data/lib/mr/read_model.rb +83 -0
- data/lib/mr/read_model/data.rb +38 -0
- data/lib/mr/read_model/fields.rb +218 -0
- data/lib/mr/read_model/query_expression.rb +188 -0
- data/lib/mr/read_model/querying.rb +214 -0
- data/lib/mr/read_model/set_querying.rb +82 -0
- data/lib/mr/read_model/subquery.rb +98 -0
- data/lib/mr/record.rb +35 -0
- data/lib/mr/test_helpers.rb +229 -0
- data/lib/mr/type_converter.rb +85 -0
- data/lib/mr/version.rb +3 -0
- data/log/.gitkeep +0 -0
- data/mr.gemspec +29 -0
- data/test/helper.rb +21 -0
- data/test/support/db.rb +10 -0
- data/test/support/factory.rb +13 -0
- data/test/support/factory/area.rb +6 -0
- data/test/support/factory/comment.rb +14 -0
- data/test/support/factory/image.rb +6 -0
- data/test/support/factory/user.rb +6 -0
- data/test/support/models/area.rb +58 -0
- data/test/support/models/comment.rb +60 -0
- data/test/support/models/image.rb +53 -0
- data/test/support/models/user.rb +96 -0
- data/test/support/read_model/querying.rb +150 -0
- data/test/support/read_models/comment_with_user_data.rb +27 -0
- data/test/support/read_models/set_data.rb +49 -0
- data/test/support/read_models/subquery_data.rb +41 -0
- data/test/support/read_models/user_with_area_data.rb +15 -0
- data/test/support/schema.rb +39 -0
- data/test/support/setup_test_db.rb +10 -0
- data/test/system/factory/model_factory_tests.rb +87 -0
- data/test/system/factory/model_stack_tests.rb +30 -0
- data/test/system/factory/record_factory_tests.rb +84 -0
- data/test/system/factory/record_stack_tests.rb +51 -0
- data/test/system/factory_tests.rb +32 -0
- data/test/system/read_model_tests.rb +199 -0
- data/test/system/with_model_tests.rb +275 -0
- data/test/unit/after_commit/fake_record_tests.rb +110 -0
- data/test/unit/after_commit/record_procs_methods_tests.rb +177 -0
- data/test/unit/after_commit/record_tests.rb +134 -0
- data/test/unit/after_commit_tests.rb +113 -0
- data/test/unit/factory/config_tests.rb +651 -0
- data/test/unit/factory/model_factory_tests.rb +473 -0
- data/test/unit/factory/model_stack_tests.rb +97 -0
- data/test/unit/factory/read_model_factory_tests.rb +195 -0
- data/test/unit/factory/record_factory_tests.rb +446 -0
- data/test/unit/factory/record_stack_tests.rb +549 -0
- data/test/unit/factory_tests.rb +213 -0
- data/test/unit/fake_query_tests.rb +137 -0
- data/test/unit/fake_record/associations_tests.rb +585 -0
- data/test/unit/fake_record/attributes_tests.rb +265 -0
- data/test/unit/fake_record/persistence_tests.rb +239 -0
- data/test/unit/fake_record_tests.rb +106 -0
- data/test/unit/json_field/fake_record_tests.rb +75 -0
- data/test/unit/json_field/record_tests.rb +80 -0
- data/test/unit/json_field_tests.rb +302 -0
- data/test/unit/model/associations_tests.rb +346 -0
- data/test/unit/model/configuration_tests.rb +92 -0
- data/test/unit/model/fields_tests.rb +278 -0
- data/test/unit/model/persistence_tests.rb +114 -0
- data/test/unit/model_tests.rb +137 -0
- data/test/unit/query_tests.rb +300 -0
- data/test/unit/read_model/data_tests.rb +56 -0
- data/test/unit/read_model/fields_tests.rb +416 -0
- data/test/unit/read_model/query_expression_tests.rb +381 -0
- data/test/unit/read_model/querying_tests.rb +613 -0
- data/test/unit/read_model/set_querying_tests.rb +149 -0
- data/test/unit/read_model/subquery_tests.rb +242 -0
- data/test/unit/read_model_tests.rb +187 -0
- data/test/unit/record_tests.rb +45 -0
- data/test/unit/test_helpers_tests.rb +431 -0
- data/test/unit/type_converter_tests.rb +207 -0
- 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
|