torque-postgresql 1.1.1 → 2.0.1
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 +5 -5
- data/Rakefile +5 -2
- 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 -61
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +2 -1
- data/lib/torque/postgresql/associations/preloader.rb +0 -24
- data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
- data/lib/torque/postgresql/auxiliary_statement.rb +12 -17
- data/lib/torque/postgresql/coder.rb +1 -2
- data/lib/torque/postgresql/config.rb +0 -4
- data/lib/torque/postgresql/inheritance.rb +13 -17
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
- data/lib/torque/postgresql/relation.rb +11 -16
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +9 -15
- data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
- data/lib/torque/postgresql/schema_cache.rb +19 -11
- 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 +628 -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 +416 -0
- metadata +103 -16
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
- data/lib/torque/postgresql/schema_dumper.rb +0 -88
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Data collector', type: :helper do
|
4
|
+
let(:methods_list) { [:foo, :bar] }
|
5
|
+
subject { Torque::PostgreSQL::Collector.new(*methods_list) }
|
6
|
+
|
7
|
+
it 'is a class creator' do
|
8
|
+
expect(subject).to be_a(Class)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has the requested methods' do
|
12
|
+
instance = subject.new
|
13
|
+
methods_list.each do |name|
|
14
|
+
expect(instance).to respond_to(name)
|
15
|
+
expect(instance).to respond_to("#{name}=")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'instace values starts as nil' do
|
20
|
+
instance = subject.new
|
21
|
+
methods_list.each do |name|
|
22
|
+
expect(instance.send(name)).to be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'set values on the same method' do
|
27
|
+
instance = subject.new
|
28
|
+
methods_list.each do |name|
|
29
|
+
expect(instance.send(name, name)).to eql(name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'get value on the same method' do
|
34
|
+
instance = subject.new
|
35
|
+
methods_list.each do |name|
|
36
|
+
instance.send(name, name)
|
37
|
+
expect(instance.send(name)).to eql(name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'accepts any kind of value' do
|
42
|
+
instance = subject.new
|
43
|
+
|
44
|
+
instance.foo 123
|
45
|
+
expect(instance.foo).to eql(123)
|
46
|
+
|
47
|
+
instance.foo 'chars'
|
48
|
+
expect(instance.foo).to eql('chars')
|
49
|
+
|
50
|
+
instance.foo :test, :test
|
51
|
+
expect(instance.foo).to eql([:test, :test])
|
52
|
+
|
53
|
+
instance.foo test: :test
|
54
|
+
expect(instance.foo).to eql({test: :test})
|
55
|
+
|
56
|
+
instance.foo nil
|
57
|
+
expect(instance.foo).to be_nil
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'DistinctOn' do
|
4
|
+
|
5
|
+
context 'on relation' do
|
6
|
+
subject { Post.unscoped }
|
7
|
+
|
8
|
+
it 'has its method' do
|
9
|
+
expect(subject).to respond_to(:distinct_on)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'does not mess with original distinct form without select' do
|
13
|
+
expect(subject.distinct.to_sql).to \
|
14
|
+
eql('SELECT DISTINCT "posts".* FROM "posts"')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not mess with original distinct form with select' do
|
18
|
+
expect(subject.select(:name).distinct.to_sql).to \
|
19
|
+
eql('SELECT DISTINCT "name" FROM "posts"')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is able to do the basic form' do
|
23
|
+
expect(subject.distinct_on(:title).to_sql).to \
|
24
|
+
eql('SELECT DISTINCT ON ( "posts"."title" ) "posts".* FROM "posts"')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is able to do with multiple attributes' do
|
28
|
+
expect(subject.distinct_on(:title, :content).to_sql).to \
|
29
|
+
eql('SELECT DISTINCT ON ( "posts"."title", "posts"."content" ) "posts".* FROM "posts"')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'is able to do with relation' do
|
33
|
+
expect(subject.distinct_on(author: :name).to_sql).to \
|
34
|
+
eql('SELECT DISTINCT ON ( "authors"."name" ) "posts".* FROM "posts"')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is able to do with relation and multiple attributes' do
|
38
|
+
expect(subject.distinct_on(author: [:name, :age]).to_sql).to \
|
39
|
+
eql('SELECT DISTINCT ON ( "authors"."name", "authors"."age" ) "posts".* FROM "posts"')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'raises with invalid relation' do
|
43
|
+
expect { subject.distinct_on(tags: :name).to_sql }.to \
|
44
|
+
raise_error(ArgumentError, /Relation for/)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises with third level hash' do
|
48
|
+
expect { subject.distinct_on(author: [comments: :body]).to_sql }.to \
|
49
|
+
raise_error(ArgumentError, /on third level/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'on model' do
|
54
|
+
subject { Post }
|
55
|
+
|
56
|
+
it 'has its method' do
|
57
|
+
expect(subject).to respond_to(:distinct_on)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns a relation when using the method' do
|
61
|
+
expect(subject.distinct_on(:title)).to be_a(ActiveRecord::Relation)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Enum' do
|
4
|
+
let(:connection) { ActiveRecord::Base.connection }
|
5
|
+
let(:attribute_klass) { Torque::PostgreSQL::Attributes::EnumSet }
|
6
|
+
|
7
|
+
def decorate(model, field, options = {})
|
8
|
+
attribute_klass.include_on(model, :enum_set)
|
9
|
+
model.enum_set(field, **options)
|
10
|
+
end
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
Torque::PostgreSQL.config.enum.set_method = :pg_set_enum
|
14
|
+
Torque::PostgreSQL::Attributes::EnumSet.include_on(ActiveRecord::Base)
|
15
|
+
|
16
|
+
# Define a method to find yet to define constants
|
17
|
+
Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:const_missing) do |name|
|
18
|
+
Torque::PostgreSQL::Attributes::EnumSet.lookup(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Define a helper method to get a sample value
|
22
|
+
Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:sample) do |name|
|
23
|
+
Torque::PostgreSQL::Attributes::EnumSet.lookup(name).sample
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'on table definition' do
|
28
|
+
subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
|
29
|
+
|
30
|
+
it 'can be defined as an array' do
|
31
|
+
subject.enum(:content_status, array: true)
|
32
|
+
expect(subject['content_status'].name).to be_eql('content_status')
|
33
|
+
expect(subject['content_status'].type).to be_eql(:content_status)
|
34
|
+
|
35
|
+
array = subject['content_status'].respond_to?(:options) \
|
36
|
+
? subject['content_status'].options[:array] \
|
37
|
+
: subject['content_status'].array
|
38
|
+
|
39
|
+
expect(array).to be_eql(true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'on schema' do
|
44
|
+
it 'can be used on tables' do
|
45
|
+
dump_io = StringIO.new
|
46
|
+
checker = /t\.enum +"conflicts", +array: true, +subtype: :conflicts/
|
47
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
48
|
+
expect(dump_io.string).to match checker
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can have a default value as an array of symbols' do
|
52
|
+
dump_io = StringIO.new
|
53
|
+
checker = /t\.enum +"types", +default: \[:A, :B\], +array: true, +subtype: :types/
|
54
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
55
|
+
expect(dump_io.string).to match checker
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'on value' do
|
60
|
+
subject { Enum::TypesSet }
|
61
|
+
let(:values) { %w(A B C D) }
|
62
|
+
let(:error) { Torque::PostgreSQL::Attributes::EnumSet::EnumSetError }
|
63
|
+
let(:mock_enum) do
|
64
|
+
enum_klass = Class.new(subject::EnumSource.superclass)
|
65
|
+
enum_klass.instance_variable_set(:@values, values << '15')
|
66
|
+
|
67
|
+
klass = Class.new(subject.superclass)
|
68
|
+
klass.const_set('EnumSource', enum_klass)
|
69
|
+
klass
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'class exists' do
|
73
|
+
namespace = Torque::PostgreSQL.config.enum.namespace
|
74
|
+
expect(namespace.const_defined?('TypesSet')).to be_truthy
|
75
|
+
expect(subject.const_defined?('EnumSource')).to be_truthy
|
76
|
+
expect(subject < Torque::PostgreSQL::Attributes::EnumSet).to be_truthy
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns the db type name' do
|
80
|
+
expect(subject.type_name).to be_eql('types[]')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'values match database values' do
|
84
|
+
expect(subject.values).to be_eql(values)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'values can be reach using fetch, as in hash enums' do
|
88
|
+
expect(subject).to respond_to(:fetch)
|
89
|
+
|
90
|
+
value = subject.fetch('A', 'A')
|
91
|
+
expect(value).to be_a(subject)
|
92
|
+
expect(value).to be_eql(subject.A)
|
93
|
+
|
94
|
+
value = subject.fetch('other', 'other')
|
95
|
+
expect(value).to be_nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'values can be reach using [], as in hash enums' do
|
99
|
+
expect(subject).to respond_to(:[])
|
100
|
+
|
101
|
+
value = subject['A']
|
102
|
+
expect(value).to be_a(subject)
|
103
|
+
expect(value).to be_eql(subject.A)
|
104
|
+
|
105
|
+
value = subject['other']
|
106
|
+
expect(value).to be_nil
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'accepts respond_to against value' do
|
110
|
+
expect(subject).to respond_to(:A)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'allows fast creation of values' do
|
114
|
+
value = subject.A
|
115
|
+
expect(value).to be_a(subject)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'keeps blank values as Lazy' do
|
119
|
+
expect(subject.new(nil)).to be_nil
|
120
|
+
expect(subject.new([])).to be_blank
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'can start from nil value using lazy' do
|
124
|
+
lazy = Torque::PostgreSQL::Attributes::Lazy
|
125
|
+
value = subject.new(nil)
|
126
|
+
|
127
|
+
expect(value.__class__).to be_eql(lazy)
|
128
|
+
expect(value.to_s).to be_eql('')
|
129
|
+
expect(value.to_i).to be_nil
|
130
|
+
|
131
|
+
expect(value.A?).to be_falsey
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'accepts values to come from numeric as power' do
|
135
|
+
expect(subject.new(0)).to be_blank
|
136
|
+
expect(subject.new(1)).to be_eql(subject.A)
|
137
|
+
expect(subject.new(3)).to be_eql(subject.A | subject.B)
|
138
|
+
expect { subject.new(16) }.to raise_error(error, /out of bounds/)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'accepts values to come from numeric list' do
|
142
|
+
expect(subject.new([0])).to be_eql(subject.A)
|
143
|
+
expect(subject.new([0, 1])).to be_eql(subject.A | subject.B)
|
144
|
+
expect { subject.new([4]) }.to raise_error(error.superclass, /out of bounds/)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'accepts string initialization' do
|
148
|
+
expect(subject.new('A')).to be_eql(subject.A)
|
149
|
+
expect { subject.new('E') }.to raise_error(error.superclass, /not valid for/)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'allows values bitwise operations' do
|
153
|
+
expect((subject.A | subject.B).to_i).to be_eql(3)
|
154
|
+
expect((subject.A & subject.B).to_i).to be_nil
|
155
|
+
expect(((subject.A | subject.B) & subject.B).to_i).to be_eql(2)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'allows values comparison' do
|
159
|
+
value = subject.B | subject.C
|
160
|
+
expect(value).to be > subject.A
|
161
|
+
expect(value).to be < subject.D
|
162
|
+
expect(value).to be_eql(6)
|
163
|
+
expect(value).to_not be_eql(1)
|
164
|
+
expect(subject.A == mock_enum.A).to be_falsey
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'accepts value checking' do
|
168
|
+
value = subject.B | subject.C
|
169
|
+
expect(value).to respond_to(:B?)
|
170
|
+
expect(value.B?).to be_truthy
|
171
|
+
expect(value.C?).to be_truthy
|
172
|
+
expect(value.A?).to be_falsey
|
173
|
+
expect(value.D?).to be_falsey
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'accepts replace and bang value' do
|
177
|
+
value = subject.B | subject.C
|
178
|
+
expect(value).to respond_to(:B!)
|
179
|
+
expect(value.A!).to be_eql(7)
|
180
|
+
expect(value.replace(:D)).to be_eql(subject.D)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'accepts values turn into integer by its power' do
|
184
|
+
expect(subject.B.to_i).to be_eql(2)
|
185
|
+
expect(subject.C.to_i).to be_eql(4)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'accepts values turn into an array of integer by index' do
|
189
|
+
expect((subject.B | subject.C).map(&:to_i)).to be_eql([1, 2])
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'can return a sample for resting purposes' do
|
193
|
+
expect(subject).to receive(:new).with(Numeric)
|
194
|
+
subject.sample
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'on OID' do
|
199
|
+
let(:enum) { Enum::TypesSet }
|
200
|
+
let(:enum_source) { enum::EnumSource }
|
201
|
+
subject { Torque::PostgreSQL::Adapter::OID::EnumSet.new('types', enum_source) }
|
202
|
+
|
203
|
+
context 'on deserialize' do
|
204
|
+
it 'returns nil' do
|
205
|
+
expect(subject.deserialize(nil)).to be_nil
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'returns enum' do
|
209
|
+
value = subject.deserialize('{B,C}')
|
210
|
+
expect(value).to be_a(enum)
|
211
|
+
expect(value).to be_eql(enum.B | enum.C)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'on serialize' do
|
216
|
+
it 'returns nil' do
|
217
|
+
expect(subject.serialize(nil)).to be_nil
|
218
|
+
expect(subject.serialize(0)).to be_nil
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'returns as string' do
|
222
|
+
expect(subject.serialize(enum.B | enum.C)).to be_eql('{B,C}')
|
223
|
+
expect(subject.serialize(3)).to be_eql('{A,B}')
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'on cast' do
|
228
|
+
it 'accepts nil' do
|
229
|
+
expect(subject.cast(nil)).to be_nil
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'accepts invalid values as nil' do
|
233
|
+
expect(subject.cast([])).to be_nil
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'accepts array of strings' do
|
237
|
+
value = subject.cast(['A'])
|
238
|
+
expect(value).to be_a(enum)
|
239
|
+
expect(value).to be_eql(enum.A)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'accepts array of numbers' do
|
243
|
+
value = subject.cast([1])
|
244
|
+
expect(value).to be_a(enum)
|
245
|
+
expect(value).to be_eql(enum.B)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context 'on I18n' do
|
251
|
+
subject { Enum::TypesSet }
|
252
|
+
|
253
|
+
it 'has the text method' do
|
254
|
+
expect(subject.new(0)).to respond_to(:text)
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'brings the correct values' do
|
258
|
+
expect(subject.new(0).text).to be_eql('')
|
259
|
+
expect(subject.new(1).text).to be_eql('A')
|
260
|
+
expect(subject.new(2).text).to be_eql('B')
|
261
|
+
expect(subject.new(3).text).to be_eql('A and B')
|
262
|
+
expect(subject.new(7).text).to be_eql('A, B, and C')
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context 'on model' do
|
267
|
+
before(:each) { decorate(Course, :types) }
|
268
|
+
|
269
|
+
subject { Course }
|
270
|
+
let(:instance) { Course.new }
|
271
|
+
|
272
|
+
it 'has all enum set methods' do
|
273
|
+
expect(subject).to respond_to(:types)
|
274
|
+
expect(subject).to respond_to(:types_keys)
|
275
|
+
expect(subject).to respond_to(:types_texts)
|
276
|
+
expect(subject).to respond_to(:types_options)
|
277
|
+
|
278
|
+
expect(subject).to respond_to(:has_types)
|
279
|
+
expect(subject).to respond_to(:has_any_types)
|
280
|
+
|
281
|
+
expect(instance).to respond_to(:types_text)
|
282
|
+
|
283
|
+
subject.types.each do |value|
|
284
|
+
value = value.underscore
|
285
|
+
expect(subject).to respond_to(value)
|
286
|
+
expect(instance).to respond_to(value + '?')
|
287
|
+
expect(instance).to respond_to(value + '!')
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'scope the model correctly' do
|
292
|
+
query = subject.a.to_sql
|
293
|
+
expect(query).to match(/"courses"."types" @> ARRAY\['A'\]::types\[\]/)
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'has a match all scope' do
|
297
|
+
query = subject.has_types('B', 'A').to_sql
|
298
|
+
expect(query).to match(/"courses"."types" @> ARRAY\['B', 'A'\]::types\[\]/)
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'has a match any scope' do
|
302
|
+
query = subject.has_any_types('B', 'A').to_sql
|
303
|
+
expect(query).to match(/"courses"."types" && ARRAY\['B', 'A'\]::types\[\]/)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
@@ -0,0 +1,628 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Enum' do
|
4
|
+
let(:connection) { ActiveRecord::Base.connection }
|
5
|
+
let(:attribute_klass) { Torque::PostgreSQL::Attributes::Enum }
|
6
|
+
|
7
|
+
def decorate(model, field, options = {})
|
8
|
+
attribute_klass.include_on(model, :pg_enum)
|
9
|
+
model.pg_enum(field, **options)
|
10
|
+
end
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
Torque::PostgreSQL.config.enum.base_method = :pg_enum
|
14
|
+
Torque::PostgreSQL::Attributes::Enum.include_on(ActiveRecord::Base)
|
15
|
+
|
16
|
+
# Define a method to find yet to define constants
|
17
|
+
Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:const_missing) do |name|
|
18
|
+
Torque::PostgreSQL::Attributes::Enum.lookup(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Define a helper method to get a sample value
|
22
|
+
Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:sample) do |name|
|
23
|
+
Torque::PostgreSQL::Attributes::Enum.lookup(name).sample
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'on migration' do
|
28
|
+
it 'can be created' do
|
29
|
+
connection.create_enum(:status, %i(foo bar))
|
30
|
+
expect(connection.type_exists?(:status)).to be_truthy
|
31
|
+
expect(connection.enum_values(:status)).to be_eql(['foo', 'bar'])
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can be deleted' do
|
35
|
+
connection.create_enum(:status, %i(foo bar))
|
36
|
+
expect(connection.type_exists?(:status)).to be_truthy
|
37
|
+
|
38
|
+
connection.drop_type(:status)
|
39
|
+
expect(connection.type_exists?(:status)).to be_falsey
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'can be renamed' do
|
43
|
+
connection.rename_type(:content_status, :status)
|
44
|
+
expect(connection.type_exists?(:content_status)).to be_falsey
|
45
|
+
expect(connection.type_exists?(:status)).to be_truthy
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can have prefix' do
|
49
|
+
connection.create_enum(:status, %i(foo bar), prefix: true)
|
50
|
+
expect(connection.enum_values(:status)).to be_eql(['status_foo', 'status_bar'])
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can have suffix' do
|
54
|
+
connection.create_enum(:status, %i(foo bar), suffix: 'tst')
|
55
|
+
expect(connection.enum_values(:status)).to be_eql(['foo_tst', 'bar_tst'])
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'inserts values at the end' do
|
59
|
+
connection.create_enum(:status, %i(foo bar))
|
60
|
+
connection.add_enum_values(:status, %i(baz qux))
|
61
|
+
expect(connection.enum_values(:status)).to be_eql(['foo', 'bar', 'baz', 'qux'])
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'inserts values in the beginning' do
|
65
|
+
connection.create_enum(:status, %i(foo bar))
|
66
|
+
connection.add_enum_values(:status, %i(baz qux), prepend: true)
|
67
|
+
expect(connection.enum_values(:status)).to be_eql(['baz', 'qux', 'foo', 'bar'])
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'inserts values in the middle' do
|
71
|
+
connection.create_enum(:status, %i(foo bar))
|
72
|
+
connection.add_enum_values(:status, %i(baz), after: 'foo')
|
73
|
+
expect(connection.enum_values(:status)).to be_eql(['foo', 'baz', 'bar'])
|
74
|
+
|
75
|
+
connection.add_enum_values(:status, %i(qux), before: 'bar')
|
76
|
+
expect(connection.enum_values(:status)).to be_eql(['foo', 'baz', 'qux', 'bar'])
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'inserts values with prefix or suffix' do
|
80
|
+
connection.create_enum(:status, %i(foo bar))
|
81
|
+
connection.add_enum_values(:status, %i(baz), prefix: true)
|
82
|
+
connection.add_enum_values(:status, %i(qux), suffix: 'tst')
|
83
|
+
expect(connection.enum_values(:status)).to be_eql(['foo', 'bar', 'status_baz', 'qux_tst'])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'on table definition' do
|
88
|
+
subject { ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.new('articles') }
|
89
|
+
|
90
|
+
it 'has the enum method' do
|
91
|
+
expect(subject).to respond_to(:enum)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'can be used in a single form' do
|
95
|
+
subject.enum('content_status')
|
96
|
+
expect(subject['content_status'].name).to be_eql('content_status')
|
97
|
+
expect(subject['content_status'].type).to be_eql(:content_status)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'can be used in a multiple form' do
|
101
|
+
subject.enum('foo', 'bar', 'baz', subtype: :content_status)
|
102
|
+
expect(subject['foo'].type).to be_eql(:content_status)
|
103
|
+
expect(subject['bar'].type).to be_eql(:content_status)
|
104
|
+
expect(subject['baz'].type).to be_eql(:content_status)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'can have custom type' do
|
108
|
+
subject.enum('foo', subtype: :content_status)
|
109
|
+
expect(subject['foo'].name).to be_eql('foo')
|
110
|
+
expect(subject['foo'].type).to be_eql(:content_status)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'raises StatementInvalid when type isn\'t defined' do
|
114
|
+
subject.enum('foo')
|
115
|
+
creation = connection.send(:schema_creation).accept subject
|
116
|
+
expect{ connection.execute creation }.to raise_error(ActiveRecord::StatementInvalid)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'on schema' do
|
121
|
+
it 'dumps when has it' do
|
122
|
+
dump_io = StringIO.new
|
123
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
124
|
+
expect(dump_io.string).to match /create_enum \"content_status\", \[/
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'sorts the enum entries to better consistency' do
|
128
|
+
dump_io = StringIO.new
|
129
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
130
|
+
items = dump_io.string.scan(/create_enum "(\w+)"/).flatten
|
131
|
+
expect(items).to be_eql(items.sort)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'do not dump when has none' do
|
135
|
+
connection.drop_type(:content_status, force: :cascade)
|
136
|
+
|
137
|
+
dump_io = StringIO.new
|
138
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
139
|
+
expect(dump_io.string).not_to match /create_enum \"content_status\", \[/
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'can be used on tables too' do
|
143
|
+
dump_io = StringIO.new
|
144
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
145
|
+
expect(dump_io.string).to match /t\.enum +"status", +subtype: :content_status/
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'can have a default value as symbol' do
|
149
|
+
dump_io = StringIO.new
|
150
|
+
ActiveRecord::SchemaDumper.dump(connection, dump_io)
|
151
|
+
expect(dump_io.string).to match /t\.enum +"role", +default: :visitor, +subtype: :roles/
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'on value' do
|
156
|
+
subject { Enum::ContentStatus }
|
157
|
+
let(:values) { %w(created draft published archived) }
|
158
|
+
let(:error) { Torque::PostgreSQL::Attributes::Enum::EnumError }
|
159
|
+
let(:mock_enum) do
|
160
|
+
klass = Class.new(subject.superclass)
|
161
|
+
klass.instance_variable_set(:@values, values << '15')
|
162
|
+
klass
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'class exists' do
|
166
|
+
namespace = Torque::PostgreSQL.config.enum.namespace
|
167
|
+
expect(namespace.const_defined?('ContentStatus')).to be_truthy
|
168
|
+
expect(subject < Torque::PostgreSQL::Attributes::Enum).to be_truthy
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'lazy loads values' do
|
172
|
+
expect(subject.instance_variable_defined?(:@values)).to be_falsey
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'returns the db type name' do
|
176
|
+
expect(subject.type_name).to be_eql('content_status')
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'values match database values' do
|
180
|
+
expect(subject.values).to be_eql(values)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'can return a sample value' do
|
184
|
+
expect(Enum).to respond_to(:sample)
|
185
|
+
expect(Enum::ContentStatus).to respond_to(:sample)
|
186
|
+
expect(Enum::ContentStatus.sample).to satisfy { |v| values.include?(v) }
|
187
|
+
expect(Enum.sample(:content_status)).to satisfy { |v| values.include?(v) }
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'values can be iterated by using each direct on class' do
|
191
|
+
expect(subject).to respond_to(:each)
|
192
|
+
expect(subject.each).to be_a(Enumerator)
|
193
|
+
expect(subject.each.entries).to be_eql(values)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'values can be reach using fetch, as in hash enums' do
|
197
|
+
expect(subject).to respond_to(:fetch)
|
198
|
+
|
199
|
+
value = subject.fetch('archived', 'archived')
|
200
|
+
expect(value).to be_a(subject)
|
201
|
+
expect(value).to be_eql(subject.archived)
|
202
|
+
|
203
|
+
value = subject.fetch('other', 'other')
|
204
|
+
expect(value).to be_nil
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'values can be reach using [], as in hash enums' do
|
208
|
+
expect(subject).to respond_to(:[])
|
209
|
+
|
210
|
+
value = subject['archived']
|
211
|
+
expect(value).to be_a(subject)
|
212
|
+
expect(value).to be_eql(subject.archived)
|
213
|
+
|
214
|
+
value = subject['other']
|
215
|
+
expect(value).to be_nil
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'accepts respond_to against value' do
|
219
|
+
expect(subject).to respond_to(:archived)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'allows fast creation of values' do
|
223
|
+
value = subject.draft
|
224
|
+
expect(value).to be_a(subject)
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'keeps blank values as Lazy' do
|
228
|
+
expect(subject.new(nil)).to be_nil
|
229
|
+
expect(subject.new([])).to be_nil
|
230
|
+
expect(subject.new('')).to be_nil
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'can start from nil value using lazy' do
|
234
|
+
lazy = Torque::PostgreSQL::Attributes::Lazy
|
235
|
+
value = subject.new(nil)
|
236
|
+
|
237
|
+
expect(value.__class__).to be_eql(lazy)
|
238
|
+
expect(value.to_s).to be_eql('')
|
239
|
+
expect(value.to_i).to be_nil
|
240
|
+
|
241
|
+
expect(value.draft?).to be_falsey
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'accepts values to come from numeric' do
|
245
|
+
expect(subject.new(0)).to be_eql(subject.created)
|
246
|
+
expect { subject.new(5) }.to raise_error(error, /out of bounds/)
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'accepts string initialization' do
|
250
|
+
expect(subject.new('created')).to be_eql(subject.created)
|
251
|
+
expect { subject.new('updated') }.to raise_error(error, /not valid for/)
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'allows values comparison' do
|
255
|
+
value = subject.draft
|
256
|
+
expect(value).to be > subject.created
|
257
|
+
expect(value).to be < subject.archived
|
258
|
+
expect(value).to be_eql(subject.draft)
|
259
|
+
expect(value).to_not be_eql(subject.published)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'allows values comparison with string' do
|
263
|
+
value = subject.draft
|
264
|
+
expect(value).to be > :created
|
265
|
+
expect(value).to be < :archived
|
266
|
+
expect(value).to be_eql(:draft)
|
267
|
+
expect(value).to_not be_eql(:published)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'allows values comparison with symbol' do
|
271
|
+
value = subject.draft
|
272
|
+
expect(value).to be > 'created'
|
273
|
+
expect(value).to be < 'archived'
|
274
|
+
expect(value).to be_eql('draft')
|
275
|
+
expect(value).to_not be_eql('published')
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'allows values comparison with number' do
|
279
|
+
value = subject.draft
|
280
|
+
expect(value).to be > 0
|
281
|
+
expect(value).to be < 3
|
282
|
+
expect(value).to be_eql(1)
|
283
|
+
expect(value).to_not be_eql(2.5)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'does not allow cross-enum comparison' do
|
287
|
+
expect { subject.draft < mock_enum.published }.to raise_error(error, /^Comparison/)
|
288
|
+
expect { subject.draft > mock_enum.created }.to raise_error(error, /^Comparison/)
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'does not allow other types comparison' do
|
292
|
+
expect { subject.draft > true }.to raise_error(error, /^Comparison/)
|
293
|
+
expect { subject.draft < [] }.to raise_error(error, /^Comparison/)
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'accepts value checking' do
|
297
|
+
value = subject.draft
|
298
|
+
expect(value).to respond_to(:archived?)
|
299
|
+
expect(value.draft?).to be_truthy
|
300
|
+
expect(value.published?).to be_falsey
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'accepts replace and bang value' do
|
304
|
+
value = subject.draft
|
305
|
+
expect(value).to respond_to(:archived!)
|
306
|
+
expect(value.archived!).to be_eql(subject.archived)
|
307
|
+
expect(value.replace('created')).to be_eql(subject.created)
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'accepts values turn into integer by its index' do
|
311
|
+
mock_value = mock_enum.new('15')
|
312
|
+
expect(subject.created.to_i).to be_eql(0)
|
313
|
+
expect(subject.archived.to_i).to be_eql(3)
|
314
|
+
expect(mock_value.to_i).to_not be_eql(15)
|
315
|
+
expect(mock_value.to_i).to be_eql(4)
|
316
|
+
end
|
317
|
+
|
318
|
+
context 'on members' do
|
319
|
+
it 'has enumerable operations' do
|
320
|
+
expect(subject).to respond_to(:all?)
|
321
|
+
expect(subject).to respond_to(:any?)
|
322
|
+
expect(subject).to respond_to(:collect)
|
323
|
+
expect(subject).to respond_to(:count)
|
324
|
+
expect(subject).to respond_to(:cycle)
|
325
|
+
expect(subject).to respond_to(:detect)
|
326
|
+
expect(subject).to respond_to(:drop)
|
327
|
+
expect(subject).to respond_to(:drop_while)
|
328
|
+
expect(subject).to respond_to(:each)
|
329
|
+
expect(subject).to respond_to(:each_with_index)
|
330
|
+
expect(subject).to respond_to(:entries)
|
331
|
+
expect(subject).to respond_to(:find)
|
332
|
+
expect(subject).to respond_to(:find_all)
|
333
|
+
expect(subject).to respond_to(:find_index)
|
334
|
+
expect(subject).to respond_to(:first)
|
335
|
+
expect(subject).to respond_to(:flat_map)
|
336
|
+
expect(subject).to respond_to(:include?)
|
337
|
+
expect(subject).to respond_to(:inject)
|
338
|
+
expect(subject).to respond_to(:lazy)
|
339
|
+
expect(subject).to respond_to(:map)
|
340
|
+
expect(subject).to respond_to(:member?)
|
341
|
+
expect(subject).to respond_to(:one?)
|
342
|
+
expect(subject).to respond_to(:reduce)
|
343
|
+
expect(subject).to respond_to(:reject)
|
344
|
+
expect(subject).to respond_to(:reverse_each)
|
345
|
+
expect(subject).to respond_to(:select)
|
346
|
+
expect(subject).to respond_to(:sort)
|
347
|
+
expect(subject).to respond_to(:zip)
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'works with map' do
|
351
|
+
result = subject.map(&:to_i)
|
352
|
+
expect(result).to be_eql([0, 1, 2, 3])
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
context 'on OID' do
|
358
|
+
let(:enum) { Enum::ContentStatus }
|
359
|
+
subject { Torque::PostgreSQL::Adapter::OID::Enum.new('content_status') }
|
360
|
+
|
361
|
+
context 'on deserialize' do
|
362
|
+
it 'returns nil' do
|
363
|
+
expect(subject.deserialize(nil)).to be_nil
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'returns enum' do
|
367
|
+
value = subject.deserialize('created')
|
368
|
+
expect(value).to be_a(enum)
|
369
|
+
expect(value).to be_eql(enum.created)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
context 'on serialize' do
|
374
|
+
it 'returns nil' do
|
375
|
+
expect(subject.serialize(nil)).to be_nil
|
376
|
+
expect(subject.serialize('test')).to be_nil
|
377
|
+
expect(subject.serialize(15)).to be_nil
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'returns as string' do
|
381
|
+
expect(subject.serialize(enum.created)).to be_eql('created')
|
382
|
+
expect(subject.serialize(1)).to be_eql('draft')
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
context 'on cast' do
|
387
|
+
it 'accepts nil' do
|
388
|
+
expect(subject.cast(nil)).to be_nil
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'accepts invalid values as nil' do
|
392
|
+
expect(subject.cast(false)).to be_nil
|
393
|
+
expect(subject.cast(true)).to be_nil
|
394
|
+
expect(subject.cast([])).to be_nil
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'accepts string' do
|
398
|
+
value = subject.cast('created')
|
399
|
+
expect(value).to be_a(enum)
|
400
|
+
expect(value).to be_eql(enum.created)
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'accepts numeric' do
|
404
|
+
value = subject.cast(1)
|
405
|
+
expect(value).to be_a(enum)
|
406
|
+
expect(value).to be_eql(enum.draft)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context 'on I18n' do
|
412
|
+
subject { Enum::ContentStatus }
|
413
|
+
|
414
|
+
it 'has the text method' do
|
415
|
+
expect(subject.new(0)).to respond_to(:text)
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'brings the correct values' do
|
419
|
+
expect(subject.new(0).text).to be_eql('1 - Created')
|
420
|
+
expect(subject.new(1).text).to be_eql('Draft (2)')
|
421
|
+
expect(subject.new(2).text).to be_eql('Finally published')
|
422
|
+
expect(subject.new(3).text).to be_eql('Archived')
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'on model' do
|
427
|
+
before(:each) { decorate(User, :role) }
|
428
|
+
|
429
|
+
subject { User }
|
430
|
+
let(:instance) { FactoryBot.build(:user) }
|
431
|
+
|
432
|
+
it 'has all enum methods' do
|
433
|
+
expect(subject).to respond_to(:roles)
|
434
|
+
expect(subject).to respond_to(:roles_keys)
|
435
|
+
expect(subject).to respond_to(:roles_texts)
|
436
|
+
expect(subject).to respond_to(:roles_options)
|
437
|
+
expect(instance).to respond_to(:role_text)
|
438
|
+
|
439
|
+
subject.roles.each do |value|
|
440
|
+
expect(subject).to respond_to(value)
|
441
|
+
expect(instance).to respond_to(value + '?')
|
442
|
+
expect(instance).to respond_to(value + '!')
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'plural method brings the list of values' do
|
447
|
+
result = subject.roles
|
448
|
+
expect(result).to be_a(Array)
|
449
|
+
expect(result).to be_eql(Enum::Roles.values)
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'text value now uses model and attribute references' do
|
453
|
+
instance.role = :visitor
|
454
|
+
expect(instance.role_text).to be_eql('A simple Visitor')
|
455
|
+
|
456
|
+
instance.role = :assistant
|
457
|
+
expect(instance.role_text).to be_eql('An Assistant')
|
458
|
+
|
459
|
+
instance.role = :manager
|
460
|
+
expect(instance.role_text).to be_eql('The Manager')
|
461
|
+
|
462
|
+
instance.role = :admin
|
463
|
+
expect(instance.role_text).to be_eql('Super Duper Admin')
|
464
|
+
end
|
465
|
+
|
466
|
+
it 'has scopes correctly applied' do
|
467
|
+
subject.roles.each do |value|
|
468
|
+
expect(subject.send(value).to_sql).to match(/WHERE "users"."role" = '#{value}'/)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'has scopes available on associations' do
|
473
|
+
author = FactoryBot.create(:author)
|
474
|
+
FactoryBot.create(:post, author: author)
|
475
|
+
|
476
|
+
decorate(Post, :status)
|
477
|
+
expect(author.posts).to respond_to(:test_scope)
|
478
|
+
|
479
|
+
Enum::ContentStatus.each do |value|
|
480
|
+
expect(author.posts).to be_a(ActiveRecord::Associations::CollectionProxy)
|
481
|
+
expect(author.posts).to respond_to(value.to_sym)
|
482
|
+
expect(author.posts.send(value).to_sql).to match(/AND "posts"."status" = '#{value}'/)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'ask methods work' do
|
487
|
+
instance.role = :assistant
|
488
|
+
expect(instance.manager?).to be_falsey
|
489
|
+
expect(instance.assistant?).to be_truthy
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'bang methods work' do
|
493
|
+
instance.admin!
|
494
|
+
expect(instance.persisted?).to be_truthy
|
495
|
+
|
496
|
+
updated_at = instance.updated_at
|
497
|
+
Torque::PostgreSQL.config.enum.save_on_bang = false
|
498
|
+
instance.visitor!
|
499
|
+
Torque::PostgreSQL.config.enum.save_on_bang = true
|
500
|
+
|
501
|
+
expect(instance.role).to be_eql(:visitor)
|
502
|
+
expect(instance.updated_at).to be_eql(updated_at)
|
503
|
+
|
504
|
+
instance.reload
|
505
|
+
expect(instance.role).to be_eql(:admin)
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'raises when starting an enum with conflicting methods' do
|
509
|
+
Torque::PostgreSQL.config.enum.raise_conflicting = true
|
510
|
+
AText = Class.new(ActiveRecord::Base)
|
511
|
+
AText.table_name = 'texts'
|
512
|
+
|
513
|
+
expect { decorate(AText, :conflict) }.to raise_error(ArgumentError, /already exists in/)
|
514
|
+
Torque::PostgreSQL.config.enum.raise_conflicting = false
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'scope the model correctly' do
|
518
|
+
query = subject.manager.to_sql
|
519
|
+
expect(query).to match(/"users"."role" = 'manager'/)
|
520
|
+
end
|
521
|
+
|
522
|
+
context 'on inherited classes' do
|
523
|
+
it 'has all enum methods' do
|
524
|
+
klass = Class.new(User)
|
525
|
+
instance = klass.new
|
526
|
+
|
527
|
+
expect(klass).to respond_to(:roles)
|
528
|
+
expect(klass).to respond_to(:roles_keys)
|
529
|
+
expect(klass).to respond_to(:roles_texts)
|
530
|
+
expect(klass).to respond_to(:roles_options)
|
531
|
+
expect(instance).to respond_to(:role_text)
|
532
|
+
|
533
|
+
klass.roles.each do |value|
|
534
|
+
expect(klass).to respond_to(value)
|
535
|
+
expect(instance).to respond_to(value + '?')
|
536
|
+
expect(instance).to respond_to(value + '!')
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
context 'without autoload' do
|
542
|
+
subject { Author }
|
543
|
+
let(:instance) { FactoryBot.build(:author) }
|
544
|
+
|
545
|
+
it 'has both rails original enum and the new pg_enum' do
|
546
|
+
expect(subject).to respond_to(:enum)
|
547
|
+
expect(subject).to respond_to(:pg_enum)
|
548
|
+
expect(subject.method(:pg_enum).arity).to eql(-1)
|
549
|
+
end
|
550
|
+
|
551
|
+
it 'does not create all methods' do
|
552
|
+
AAuthor = Class.new(ActiveRecord::Base)
|
553
|
+
AAuthor.table_name = 'authors'
|
554
|
+
|
555
|
+
expect(AAuthor).to_not respond_to(:specialties)
|
556
|
+
expect(AAuthor).to_not respond_to(:specialties_keys)
|
557
|
+
expect(AAuthor).to_not respond_to(:specialties_texts)
|
558
|
+
expect(AAuthor).to_not respond_to(:specialties_options)
|
559
|
+
expect(AAuthor.instance_methods).to_not include(:specialty_text)
|
560
|
+
|
561
|
+
Enum::Specialties.values.each do |value|
|
562
|
+
expect(AAuthor).to_not respond_to(value)
|
563
|
+
expect(AAuthor.instance_methods).to_not include(value + '?')
|
564
|
+
expect(AAuthor.instance_methods).to_not include(value + '!')
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
it 'can be manually initiated' do
|
569
|
+
decorate(Author, :specialty)
|
570
|
+
expect(subject).to respond_to(:specialties)
|
571
|
+
expect(subject).to respond_to(:specialties_keys)
|
572
|
+
expect(subject).to respond_to(:specialties_texts)
|
573
|
+
expect(subject).to respond_to(:specialties_options)
|
574
|
+
expect(instance).to respond_to(:specialty_text)
|
575
|
+
|
576
|
+
Enum::Specialties.values.each do |value|
|
577
|
+
expect(subject).to respond_to(value)
|
578
|
+
expect(instance).to respond_to(value + '?')
|
579
|
+
expect(instance).to respond_to(value + '!')
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
context 'with prefix' do
|
585
|
+
before(:each) { decorate(Author, :specialty, prefix: 'in') }
|
586
|
+
subject { Author }
|
587
|
+
let(:instance) { FactoryBot.build(:author) }
|
588
|
+
|
589
|
+
it 'creates all methods correctly' do
|
590
|
+
expect(subject).to respond_to(:specialties)
|
591
|
+
expect(subject).to respond_to(:specialties_keys)
|
592
|
+
expect(subject).to respond_to(:specialties_texts)
|
593
|
+
expect(subject).to respond_to(:specialties_options)
|
594
|
+
expect(instance).to respond_to(:specialty_text)
|
595
|
+
|
596
|
+
subject.specialties.each do |value|
|
597
|
+
expect(subject).to respond_to('in_' + value)
|
598
|
+
expect(instance).to respond_to('in_' + value + '?')
|
599
|
+
expect(instance).to respond_to('in_' + value + '!')
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
context 'with suffix, only, and except' do
|
605
|
+
before(:each) do
|
606
|
+
decorate(Author, :specialty, suffix: 'expert', only: %w(books movies), except: 'books')
|
607
|
+
end
|
608
|
+
|
609
|
+
subject { Author }
|
610
|
+
let(:instance) { FactoryBot.build(:author) }
|
611
|
+
|
612
|
+
it 'creates only the requested methods' do
|
613
|
+
expect(subject).to respond_to('movies_expert')
|
614
|
+
expect(instance).to respond_to('movies_expert?')
|
615
|
+
expect(instance).to respond_to('movies_expert!')
|
616
|
+
|
617
|
+
expect(subject).to_not respond_to('books_expert')
|
618
|
+
expect(instance).to_not respond_to('books_expert?')
|
619
|
+
expect(instance).to_not respond_to('books_expert!')
|
620
|
+
|
621
|
+
expect(subject).to_not respond_to('plays_expert')
|
622
|
+
expect(instance).to_not respond_to('plays_expert?')
|
623
|
+
expect(instance).to_not respond_to('plays_expert!')
|
624
|
+
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|