torque-postgresql 1.1.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/torque/postgresql.rb +0 -2
- data/lib/torque/postgresql/adapter.rb +0 -1
- data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
- data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
- data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
- data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
- data/lib/torque/postgresql/associations.rb +0 -3
- data/lib/torque/postgresql/associations/association_scope.rb +18 -60
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
- data/lib/torque/postgresql/associations/preloader.rb +0 -32
- data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
- data/lib/torque/postgresql/autosave_association.rb +4 -4
- data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
- data/lib/torque/postgresql/coder.rb +1 -2
- data/lib/torque/postgresql/config.rb +0 -6
- data/lib/torque/postgresql/inheritance.rb +13 -17
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
- data/lib/torque/postgresql/relation.rb +11 -16
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
- data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
- data/lib/torque/postgresql/version.rb +1 -1
- data/spec/en.yml +19 -0
- data/spec/factories/authors.rb +6 -0
- data/spec/factories/comments.rb +13 -0
- data/spec/factories/posts.rb +6 -0
- data/spec/factories/tags.rb +5 -0
- data/spec/factories/texts.rb +5 -0
- data/spec/factories/users.rb +6 -0
- data/spec/factories/videos.rb +5 -0
- data/spec/mocks/cache_query.rb +16 -0
- data/spec/mocks/create_table.rb +35 -0
- data/spec/models/activity.rb +3 -0
- data/spec/models/activity_book.rb +4 -0
- data/spec/models/activity_post.rb +7 -0
- data/spec/models/activity_post/sample.rb +4 -0
- data/spec/models/author.rb +4 -0
- data/spec/models/author_journalist.rb +4 -0
- data/spec/models/comment.rb +3 -0
- data/spec/models/course.rb +2 -0
- data/spec/models/geometry.rb +2 -0
- data/spec/models/guest_comment.rb +4 -0
- data/spec/models/post.rb +6 -0
- data/spec/models/tag.rb +2 -0
- data/spec/models/text.rb +2 -0
- data/spec/models/time_keeper.rb +2 -0
- data/spec/models/user.rb +8 -0
- data/spec/models/video.rb +2 -0
- data/spec/schema.rb +141 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/tests/arel_spec.rb +72 -0
- data/spec/tests/auxiliary_statement_spec.rb +593 -0
- data/spec/tests/belongs_to_many_spec.rb +240 -0
- data/spec/tests/coder_spec.rb +367 -0
- data/spec/tests/collector_spec.rb +59 -0
- data/spec/tests/distinct_on_spec.rb +65 -0
- data/spec/tests/enum_set_spec.rb +306 -0
- data/spec/tests/enum_spec.rb +621 -0
- data/spec/tests/geometric_builder_spec.rb +221 -0
- data/spec/tests/has_many_spec.rb +390 -0
- data/spec/tests/interval_spec.rb +167 -0
- data/spec/tests/lazy_spec.rb +24 -0
- data/spec/tests/period_spec.rb +954 -0
- data/spec/tests/quoting_spec.rb +24 -0
- data/spec/tests/range_spec.rb +36 -0
- data/spec/tests/relation_spec.rb +57 -0
- data/spec/tests/table_inheritance_spec.rb +403 -0
- metadata +103 -15
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
- data/lib/torque/postgresql/schema_dumper.rb +0 -91
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Geometries' do
|
4
|
+
context 'on build' do
|
5
|
+
let(:klass) do
|
6
|
+
klass = Class.new(Torque::PostgreSQL::GeometryBuilder)
|
7
|
+
klass.define_singleton_method(:name) { 'TestSample' }
|
8
|
+
klass.const_set('PIECES', %i[a b c d].freeze)
|
9
|
+
klass.const_set('FORMATION', '(%s, %s, <%s, {%s}>)'.freeze)
|
10
|
+
klass
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:instance) { klass.new }
|
14
|
+
|
15
|
+
context '#type' do
|
16
|
+
it 'originally does not have the constant defined' do
|
17
|
+
expect(klass.constants).not_to include('TYPE')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'creates the type constant based on the name' do
|
21
|
+
expect(instance.type).to be_eql(:test_sample)
|
22
|
+
expect(klass.constants).to include(:TYPE)
|
23
|
+
expect(klass::TYPE).to be_eql(:test_sample)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns the constant value' do
|
27
|
+
klass.const_set('TYPE', 'another_type')
|
28
|
+
expect(instance.type).to be_eql('another_type')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#pieces' do
|
33
|
+
it 'returns the definition pieces' do
|
34
|
+
expect(instance.pieces).to be_eql([:a, :b, :c, :d])
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns whatever is in the constant' do
|
38
|
+
klass.send(:remove_const, 'PIECES')
|
39
|
+
klass.const_set('PIECES', %i[a].freeze)
|
40
|
+
expect(instance.pieces).to be_eql([:a])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#formation' do
|
45
|
+
it 'returns the definition set' do
|
46
|
+
expect(instance.formation).to be_eql("(%s, %s, <%s, {%s}>)")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns whatever is in the constant' do
|
50
|
+
klass.send(:remove_const, 'FORMATION')
|
51
|
+
klass.const_set('FORMATION', '(<%s>)'.freeze)
|
52
|
+
expect(instance.formation).to be_eql("(<%s>)")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context '#cast' do
|
57
|
+
let(:config_class) { double }
|
58
|
+
|
59
|
+
before { allow(instance).to receive(:config_class).and_return(config_class) }
|
60
|
+
|
61
|
+
it 'accepts string values' do
|
62
|
+
expect(instance.cast('')).to be_nil
|
63
|
+
|
64
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(4)
|
65
|
+
expect(instance.cast('1, 2, 3, 4')).to be_eql(4)
|
66
|
+
|
67
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(8)
|
68
|
+
expect(instance.cast('(1, {2}, <3>, 4)')).to be_eql(8)
|
69
|
+
|
70
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(7)
|
71
|
+
expect(instance.cast('1, 2, 3, 4, 5, 6')).to be_eql(7)
|
72
|
+
|
73
|
+
expect(config_class).to receive(:new).with(1.0, 2.0, 3.0, 4.0).and_return(1)
|
74
|
+
expect(instance.cast('1.0, 2.0, 3.0, 4.0')).to be_eql(1)
|
75
|
+
|
76
|
+
expect { instance.cast(['6 6 6']) }.to raise_error(RuntimeError, 'Invalid format')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'accepts hash values' do
|
80
|
+
expect(instance.cast({})).to be_nil
|
81
|
+
|
82
|
+
expect { instance.cast({ 'a' => 1, 'b' => 2 }) }.to raise_error(RuntimeError, 'Invalid format')
|
83
|
+
|
84
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(4)
|
85
|
+
expect(instance.cast({ 'a' => 1, 'b' => 2 , 'c' => 3, 'd' => 4})).to be_eql(4)
|
86
|
+
|
87
|
+
expect(config_class).to receive(:new).with(1.0, 2.0, 3.0, 4.0).and_return(5)
|
88
|
+
expect(instance.cast({ 'a' => 1.0, 'b' => 2.0, 'c' => 3.0, 'd' => 4.0})).to be_eql(5)
|
89
|
+
|
90
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(2)
|
91
|
+
expect(instance.cast({ a: 1, b: 2 , c: 3, d: 4, e: 5, f: 6})).to be_eql(2)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'accepts array values' do
|
95
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(4)
|
96
|
+
expect(instance.cast([1, 2, 3, 4])).to be_eql(4)
|
97
|
+
|
98
|
+
expect(config_class).to receive(:new).with(1.1, 1.2, 1.3, 1.4).and_return(9)
|
99
|
+
expect(instance.cast(['1.1', '1.2', '1.3', '1.4'])).to be_eql(9)
|
100
|
+
|
101
|
+
expect(config_class).to receive(:new).with(6, 5, 4, 3).and_return(2)
|
102
|
+
expect(instance.cast([6, 5, 4, 3, 2, 1])).to be_eql(2)
|
103
|
+
|
104
|
+
expect(instance.cast([])).to be_nil
|
105
|
+
|
106
|
+
expect { instance.cast([6, 5, 4]) }.to raise_error(RuntimeError, 'Invalid format')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context '#serialize' do
|
111
|
+
before { allow(instance).to receive(:config_class).and_return(OpenStruct) }
|
112
|
+
|
113
|
+
it 'return value nil' do
|
114
|
+
expect(instance.serialize(nil)).to be_nil
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'accepts config class' do
|
118
|
+
expect(instance.serialize(OpenStruct.new)).to be_nil
|
119
|
+
expect(instance.serialize(OpenStruct.new(a: 1, b: 2, c: 3, d: 4))).to be_eql('(1, 2, <3, {4}>)')
|
120
|
+
expect(instance.serialize(OpenStruct.new(a: 1, b: 2, c: 3, d: 4, e: 5))).to be_eql('(1, 2, <3, {4}>)')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'accepts hash value' do
|
124
|
+
expect { instance.cast({a: 1, b: 2, c: 3}) }.to raise_error(RuntimeError, 'Invalid format')
|
125
|
+
expect(instance.serialize({a: 1, b: 2, c: 3, d: 4})).to be_eql('(1, 2, <3, {4}>)')
|
126
|
+
expect(instance.serialize({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6})).to be_eql('(1, 2, <3, {4}>)')
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'accepts array value' do
|
130
|
+
expect { instance.serialize([6, 5, 4]) }.to raise_error(RuntimeError, 'Invalid format')
|
131
|
+
expect(instance.serialize([1, 2, 3, 4])).to be_eql('(1, 2, <3, {4}>)')
|
132
|
+
expect(instance.serialize([5, 4, 3, 2, 1, 0])).to be_eql('(5, 4, <3, {2}>)')
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
context '#deserialize' do
|
138
|
+
let(:config_class) { double }
|
139
|
+
|
140
|
+
before { allow(instance).to receive(:config_class).and_return(config_class) }
|
141
|
+
|
142
|
+
it 'return value nil' do
|
143
|
+
expect(instance.deserialize(nil)).to be_nil
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'accept correct format' do
|
147
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(6)
|
148
|
+
expect(instance.deserialize('(1, 2, <3, {4}>)')).to be_eql(6)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context '#type_cast_for_schema' do
|
153
|
+
before { allow(instance).to receive(:config_class).and_return(OpenStruct) }
|
154
|
+
|
155
|
+
it 'returns the array for schema' do
|
156
|
+
result = instance.type_cast_for_schema(OpenStruct.new(a: 1, b: 2, c: 3, d: 4))
|
157
|
+
expect(result).to be_eql([1, 2, 3, 4])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'on box' do
|
163
|
+
let(:klass) { Torque::PostgreSQL::Adapter::OID::Box }
|
164
|
+
let(:value_klass) { Torque::PostgreSQL::Box }
|
165
|
+
let(:instance) { klass.new }
|
166
|
+
let(:value_instance) { instance.cast([1, 2, 3, 4]) }
|
167
|
+
|
168
|
+
before { allow(instance).to receive(:config_class).and_return(value_klass) }
|
169
|
+
|
170
|
+
it '#points' do
|
171
|
+
mock_klass = Struct.new(:a, :b)
|
172
|
+
Torque::PostgreSQL.config.geometry.point_class = mock_klass
|
173
|
+
|
174
|
+
result = value_instance.points
|
175
|
+
expect(result).to be_a(Array)
|
176
|
+
expect(result.size).to be_eql(4)
|
177
|
+
expect(result).to all(be_a(mock_klass))
|
178
|
+
|
179
|
+
expect(result[0].a).to be_eql(1.0)
|
180
|
+
expect(result[0].b).to be_eql(2.0)
|
181
|
+
expect(result[1].a).to be_eql(1.0)
|
182
|
+
expect(result[1].b).to be_eql(4.0)
|
183
|
+
expect(result[2].a).to be_eql(3.0)
|
184
|
+
expect(result[2].b).to be_eql(2.0)
|
185
|
+
expect(result[3].a).to be_eql(3.0)
|
186
|
+
expect(result[3].b).to be_eql(4.0)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'on circle' do
|
191
|
+
let(:klass) { Torque::PostgreSQL::Adapter::OID::Circle }
|
192
|
+
let(:value_klass) { Torque::PostgreSQL::Circle }
|
193
|
+
let(:instance) { klass.new }
|
194
|
+
let(:value_instance) { instance.cast([1, 2, 3]) }
|
195
|
+
|
196
|
+
before { allow(instance).to receive(:config_class).and_return(value_klass) }
|
197
|
+
|
198
|
+
it '#center' do
|
199
|
+
mock_klass = Struct.new(:a, :b)
|
200
|
+
Torque::PostgreSQL.config.geometry.point_class = mock_klass
|
201
|
+
|
202
|
+
result = value_instance.center
|
203
|
+
expect(result).to be_a(mock_klass)
|
204
|
+
expect(result.a).to be_eql(1.0)
|
205
|
+
expect(result.b).to be_eql(2.0)
|
206
|
+
end
|
207
|
+
|
208
|
+
it '#center=' do
|
209
|
+
mock_klass = Struct.new(:x, :y)
|
210
|
+
Torque::PostgreSQL.config.geometry.point_class = mock_klass
|
211
|
+
|
212
|
+
value_instance.center = [1, 2]
|
213
|
+
expect(value_instance.x).to be_eql(1)
|
214
|
+
expect(value_instance.y).to be_eql(2)
|
215
|
+
|
216
|
+
value_instance.center = mock_klass.new(3, 4)
|
217
|
+
expect(value_instance.x).to be_eql(3)
|
218
|
+
expect(value_instance.y).to be_eql(4)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,390 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'HasMany' do
|
4
|
+
context 'on builder' do
|
5
|
+
let(:builder) { ActiveRecord::Associations::Builder::HasMany }
|
6
|
+
|
7
|
+
it 'adds the array option' do
|
8
|
+
expect(builder.send(:valid_options, [])).to include(:array)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'on original' do
|
13
|
+
let(:other) { Text }
|
14
|
+
|
15
|
+
before { User.has_many :texts }
|
16
|
+
subject { User.create(name: 'User 1') }
|
17
|
+
after { User._reflections = {} }
|
18
|
+
|
19
|
+
it 'has the method' do
|
20
|
+
expect(subject).to respond_to(:texts)
|
21
|
+
expect(subject._reflections).to include('texts')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'loads associated records' do
|
25
|
+
expect(subject.texts.to_sql).to match(Regexp.new(<<-SQL.squish))
|
26
|
+
SELECT "texts"\\.\\* FROM "texts" WHERE \\(?"texts"\\."user_id" = #{subject.id}\\)?
|
27
|
+
SQL
|
28
|
+
|
29
|
+
expect(subject.texts.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
30
|
+
expect(subject.texts.to_a).to be_eql([])
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can be marked as loaded' do
|
34
|
+
expect(subject.texts.loaded?).to be_eql(false)
|
35
|
+
expect(subject.texts).to respond_to(:load_target)
|
36
|
+
expect(subject.texts.load_target).to be_eql([])
|
37
|
+
expect(subject.texts.loaded?).to be_eql(true)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can find specific records' do
|
41
|
+
records = FactoryBot.create_list(:text, 10, user_id: subject.id)
|
42
|
+
ids = records.map(&:id).sample(5)
|
43
|
+
|
44
|
+
expect(subject.texts).to respond_to(:find)
|
45
|
+
records = subject.texts.find(*ids)
|
46
|
+
|
47
|
+
expect(records.size).to be_eql(5)
|
48
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can return last n records' do
|
52
|
+
records = FactoryBot.create_list(:text, 10, user_id: subject.id)
|
53
|
+
ids = records.map(&:id).last(5)
|
54
|
+
|
55
|
+
expect(subject.texts).to respond_to(:last)
|
56
|
+
records = subject.texts.last(5)
|
57
|
+
|
58
|
+
expect(records.size).to be_eql(5)
|
59
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can return first n records' do
|
63
|
+
records = FactoryBot.create_list(:text, 10, user_id: subject.id)
|
64
|
+
ids = records.map(&:id).first(5)
|
65
|
+
|
66
|
+
expect(subject.texts).to respond_to(:take)
|
67
|
+
records = subject.texts.take(5)
|
68
|
+
|
69
|
+
expect(records.size).to be_eql(5)
|
70
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'can build an associated record' do
|
74
|
+
record = subject.texts.build(content: 'Test')
|
75
|
+
expect(record).to be_a(other)
|
76
|
+
expect(record).not_to be_persisted
|
77
|
+
expect(record.content).to be_eql('Test')
|
78
|
+
expect(record.user_id).to be_eql(subject.id)
|
79
|
+
|
80
|
+
expect(subject.save).to be_truthy
|
81
|
+
expect(subject.texts.size).to be_eql(1)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'can create an associated record' do
|
85
|
+
record = subject.texts.create(content: 'Test')
|
86
|
+
expect(subject.texts).to respond_to(:create!)
|
87
|
+
|
88
|
+
expect(record).to be_a(other)
|
89
|
+
expect(record).to be_persisted
|
90
|
+
expect(record.content).to be_eql('Test')
|
91
|
+
expect(record.user_id).to be_eql(subject.id)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'can concat records' do
|
95
|
+
FactoryBot.create(:text, user_id: subject.id)
|
96
|
+
expect(subject.texts.size).to be_eql(1)
|
97
|
+
|
98
|
+
subject.texts.concat(other.new(content: 'Test'))
|
99
|
+
expect(subject.texts.size).to be_eql(2)
|
100
|
+
expect(subject.texts.last.content).to be_eql('Test')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'can replace records' do
|
104
|
+
FactoryBot.create(:text, user_id: subject.id)
|
105
|
+
expect(subject.texts.size).to be_eql(1)
|
106
|
+
|
107
|
+
subject.texts.replace([other.new(content: 'Test 1'), other.new(content: 'Test 2')])
|
108
|
+
expect(subject.texts.size).to be_eql(2)
|
109
|
+
expect(subject.texts[0].content).to be_eql('Test 1')
|
110
|
+
expect(subject.texts[1].content).to be_eql('Test 2')
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'can delete all records' do
|
114
|
+
FactoryBot.create_list(:text, 5, user_id: subject.id)
|
115
|
+
expect(subject.texts.size).to be_eql(5)
|
116
|
+
|
117
|
+
subject.texts.delete_all
|
118
|
+
expect(subject.texts.size).to be_eql(0)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'can destroy all records' do
|
122
|
+
FactoryBot.create_list(:text, 5, user_id: subject.id)
|
123
|
+
expect(subject.texts.size).to be_eql(5)
|
124
|
+
|
125
|
+
subject.texts.destroy_all
|
126
|
+
expect(subject.texts.size).to be_eql(0)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'can have sum operations' do
|
130
|
+
result = FactoryBot.create_list(:text, 5, user_id: subject.id).map(&:id).reduce(:+)
|
131
|
+
expect(subject.texts).to respond_to(:sum)
|
132
|
+
expect(subject.texts.sum(:id)).to be_eql(result)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'can have a pluck operation' do
|
136
|
+
result = FactoryBot.create_list(:text, 5, user_id: subject.id).map(&:content).sort
|
137
|
+
expect(subject.texts).to respond_to(:pluck)
|
138
|
+
expect(subject.texts.pluck(:content).sort).to be_eql(result)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'can be markes as empty' do
|
142
|
+
expect(subject.texts).to respond_to(:empty?)
|
143
|
+
expect(subject.texts.empty?).to be_truthy
|
144
|
+
|
145
|
+
FactoryBot.create(:text, user_id: subject.id)
|
146
|
+
expect(subject.texts.empty?).to be_falsey
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'can check if a record is included on the list' do
|
150
|
+
inside = FactoryBot.create(:text, user_id: subject.id)
|
151
|
+
outside = FactoryBot.create(:text)
|
152
|
+
|
153
|
+
expect(subject.texts).to respond_to(:include?)
|
154
|
+
expect(subject.texts.include?(inside)).to be_truthy
|
155
|
+
expect(subject.texts.include?(outside)).to be_falsey
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'can append records' do
|
159
|
+
FactoryBot.create(:text, user_id: subject.id)
|
160
|
+
expect(subject.texts.size).to be_eql(1)
|
161
|
+
|
162
|
+
subject.texts << other.new(content: 'Test')
|
163
|
+
expect(subject.texts.size).to be_eql(2)
|
164
|
+
expect(subject.texts.last.content).to be_eql('Test')
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'can clear records' do
|
168
|
+
FactoryBot.create(:text, user_id: subject.id)
|
169
|
+
expect(subject.texts.size).to be_eql(1)
|
170
|
+
|
171
|
+
subject.texts.clear
|
172
|
+
expect(subject.texts.size).to be_eql(0)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'can reload records' do
|
176
|
+
expect(subject.texts.size).to be_eql(0)
|
177
|
+
FactoryBot.create(:text, user_id: subject.id)
|
178
|
+
|
179
|
+
expect(subject.texts.size).to be_eql(0)
|
180
|
+
|
181
|
+
subject.texts.reload
|
182
|
+
expect(subject.texts.size).to be_eql(1)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'can preload records' do
|
186
|
+
FactoryBot.create_list(:text, 5, user_id: subject.id)
|
187
|
+
entries = User.all.includes(:texts).load
|
188
|
+
|
189
|
+
expect(entries.size).to be_eql(1)
|
190
|
+
expect(entries.first.texts).to be_loaded
|
191
|
+
expect(entries.first.texts.size).to be_eql(5)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'can joins records' do
|
195
|
+
query = User.all.joins(:texts)
|
196
|
+
expect(query.to_sql).to match(/INNER JOIN "texts"/)
|
197
|
+
expect { query.load }.not_to raise_error
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'on array' do
|
202
|
+
let(:other) { Video }
|
203
|
+
|
204
|
+
before { Tag.has_many :videos, array: true }
|
205
|
+
subject { Tag.create(name: 'A') }
|
206
|
+
after { Tag._reflections = {} }
|
207
|
+
|
208
|
+
it 'has the method' do
|
209
|
+
expect(subject).to respond_to(:videos)
|
210
|
+
expect(subject._reflections).to include('videos')
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'loads associated records' do
|
214
|
+
expect(subject.videos.to_sql).to match(Regexp.new(<<-SQL.squish))
|
215
|
+
SELECT "videos"\\.\\* FROM "videos"
|
216
|
+
WHERE \\(?"videos"\\."tag_ids" && ARRAY\\[#{subject.id}\\]::bigint\\[\\]\\)?
|
217
|
+
SQL
|
218
|
+
|
219
|
+
expect(subject.videos.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
220
|
+
expect(subject.videos.to_a).to be_eql([])
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'can be marked as loaded' do
|
224
|
+
expect(subject.videos.loaded?).to be_eql(false)
|
225
|
+
expect(subject.videos).to respond_to(:load_target)
|
226
|
+
expect(subject.videos.load_target).to be_eql([])
|
227
|
+
expect(subject.videos.loaded?).to be_eql(true)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'can find specific records' do
|
231
|
+
records = FactoryBot.create_list(:video, 10, tag_ids: [subject.id])
|
232
|
+
ids = records.map(&:id).sample(5)
|
233
|
+
|
234
|
+
expect(subject.videos).to respond_to(:find)
|
235
|
+
records = subject.videos.find(*ids)
|
236
|
+
|
237
|
+
expect(records.size).to be_eql(5)
|
238
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'can return last n records' do
|
242
|
+
records = FactoryBot.create_list(:video, 10, tag_ids: [subject.id])
|
243
|
+
ids = records.map(&:id).last(5)
|
244
|
+
|
245
|
+
expect(subject.videos).to respond_to(:last)
|
246
|
+
records = subject.videos.last(5)
|
247
|
+
|
248
|
+
expect(records.size).to be_eql(5)
|
249
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'can return first n records' do
|
253
|
+
records = FactoryBot.create_list(:video, 10, tag_ids: [subject.id])
|
254
|
+
ids = records.map(&:id).first(5)
|
255
|
+
|
256
|
+
expect(subject.videos).to respond_to(:take)
|
257
|
+
records = subject.videos.take(5)
|
258
|
+
|
259
|
+
expect(records.size).to be_eql(5)
|
260
|
+
expect(records.map(&:id).sort).to be_eql(ids.sort)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'can build an associated record' do
|
264
|
+
record = subject.videos.build(title: 'Test')
|
265
|
+
expect(record).to be_a(other)
|
266
|
+
expect(record).not_to be_persisted
|
267
|
+
expect(record.title).to be_eql('Test')
|
268
|
+
|
269
|
+
expect(subject.save).to be_truthy
|
270
|
+
expect(record.tag_ids).to be_eql([subject.id])
|
271
|
+
expect(subject.videos.size).to be_eql(1)
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'can create an associated record' do
|
275
|
+
record = subject.videos.create(title: 'Test')
|
276
|
+
expect(subject.videos).to respond_to(:create!)
|
277
|
+
|
278
|
+
expect(record).to be_a(other)
|
279
|
+
expect(record).to be_persisted
|
280
|
+
expect(record.title).to be_eql('Test')
|
281
|
+
expect(record.tag_ids).to be_eql([subject.id])
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'can concat records' do
|
285
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
286
|
+
expect(subject.videos.size).to be_eql(1)
|
287
|
+
|
288
|
+
subject.videos.concat(other.new(title: 'Test'))
|
289
|
+
expect(subject.videos.size).to be_eql(2)
|
290
|
+
expect(subject.videos.last.title).to be_eql('Test')
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'can replace records' do
|
294
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
295
|
+
expect(subject.videos.size).to be_eql(1)
|
296
|
+
|
297
|
+
subject.videos.replace([other.new(title: 'Test 1'), other.new(title: 'Test 2')])
|
298
|
+
expect(subject.videos.size).to be_eql(2)
|
299
|
+
expect(subject.videos[0].title).to be_eql('Test 1')
|
300
|
+
expect(subject.videos[1].title).to be_eql('Test 2')
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'can delete all records' do
|
304
|
+
FactoryBot.create_list(:video, 5, tag_ids: [subject.id])
|
305
|
+
expect(subject.videos.size).to be_eql(5)
|
306
|
+
|
307
|
+
subject.videos.delete_all
|
308
|
+
expect(subject.videos.size).to be_eql(0)
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'can destroy all records' do
|
312
|
+
FactoryBot.create_list(:video, 5, tag_ids: [subject.id])
|
313
|
+
expect(subject.videos.size).to be_eql(5)
|
314
|
+
|
315
|
+
subject.videos.destroy_all
|
316
|
+
expect(subject.videos.size).to be_eql(0)
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'can have sum operations' do
|
320
|
+
result = FactoryBot.create_list(:video, 5, tag_ids: [subject.id]).map(&:id).reduce(:+)
|
321
|
+
expect(subject.videos).to respond_to(:sum)
|
322
|
+
expect(subject.videos.sum(:id)).to be_eql(result)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'can have a pluck operation' do
|
326
|
+
result = FactoryBot.create_list(:video, 5, tag_ids: [subject.id]).map(&:title).sort
|
327
|
+
expect(subject.videos).to respond_to(:pluck)
|
328
|
+
expect(subject.videos.pluck(:title).sort).to be_eql(result)
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'can be markes as empty' do
|
332
|
+
expect(subject.videos).to respond_to(:empty?)
|
333
|
+
expect(subject.videos.empty?).to be_truthy
|
334
|
+
|
335
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
336
|
+
expect(subject.videos.empty?).to be_falsey
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'can check if a record is included on the list' do
|
340
|
+
inside = FactoryBot.create(:video, tag_ids: [subject.id])
|
341
|
+
outside = FactoryBot.create(:video)
|
342
|
+
|
343
|
+
expect(subject.videos).to respond_to(:include?)
|
344
|
+
expect(subject.videos.include?(inside)).to be_truthy
|
345
|
+
expect(subject.videos.include?(outside)).to be_falsey
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'can append records' do
|
349
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
350
|
+
expect(subject.videos.size).to be_eql(1)
|
351
|
+
|
352
|
+
subject.videos << other.new(title: 'Test')
|
353
|
+
expect(subject.videos.size).to be_eql(2)
|
354
|
+
expect(subject.videos.last.title).to be_eql('Test')
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'can clear records' do
|
358
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
359
|
+
expect(subject.videos.size).to be_eql(1)
|
360
|
+
|
361
|
+
subject.videos.clear
|
362
|
+
expect(subject.videos.size).to be_eql(0)
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'can reload records' do
|
366
|
+
expect(subject.videos.size).to be_eql(0)
|
367
|
+
FactoryBot.create(:video, tag_ids: [subject.id])
|
368
|
+
|
369
|
+
expect(subject.videos.size).to be_eql(0)
|
370
|
+
|
371
|
+
subject.videos.reload
|
372
|
+
expect(subject.videos.size).to be_eql(1)
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'can preload records' do
|
376
|
+
FactoryBot.create_list(:video, 5, tag_ids: [subject.id])
|
377
|
+
entries = Tag.all.includes(:videos).load
|
378
|
+
|
379
|
+
expect(entries.size).to be_eql(1)
|
380
|
+
expect(entries.first.videos).to be_loaded
|
381
|
+
expect(entries.first.videos.size).to be_eql(5)
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'can joins records' do
|
385
|
+
query = Tag.all.joins(:videos)
|
386
|
+
expect(query.to_sql).to match(/INNER JOIN "videos"/)
|
387
|
+
expect { query.load }.not_to raise_error
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|