mr 0.35.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|