enumerize 1.0.0 → 1.1.0

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.
@@ -0,0 +1,40 @@
1
+ module Enumerize
2
+ module Scope
3
+ module Sequel
4
+ def enumerize(name, options={})
5
+ super
6
+
7
+ _enumerize_module.dependent_eval do
8
+ if defined?(::Sequel::Model) && self < ::Sequel::Model
9
+ if options[:scope]
10
+ _define_sequel_scope_methods!(name, options)
11
+ end
12
+
13
+ require 'enumerize/hooks/sequel_dataset'
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def _define_sequel_scope_methods!(name, options)
21
+ klass = self
22
+ scope_name = options[:scope] == true ? "with_#{name}" : options[:scope]
23
+
24
+ def_dataset_method scope_name do |*values|
25
+ values = values.map { |value| klass.enumerized_attributes[name].find_value(value).value }
26
+ values = values.first if values.size == 1
27
+
28
+ where(name => values)
29
+ end
30
+
31
+ if options[:scope] == true
32
+ def_dataset_method "without_#{name}" do |*values|
33
+ values = values.map { |value| klass.enumerized_attributes[name].find_value(value).value }
34
+ exclude(name => values)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ module Enumerize
2
+ module SequelSupport
3
+ def enumerize(name, options={})
4
+ super
5
+
6
+ _enumerize_module.dependent_eval do
7
+ if defined?(::Sequel::Model) && self < ::Sequel::Model
8
+ include InstanceMethods
9
+
10
+ require 'enumerize/hooks/sequel_dataset'
11
+ end
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ module InstanceMethods
18
+ def validate
19
+ super
20
+
21
+ self.class.enumerized_attributes.each do |attr|
22
+ value = read_attribute_for_validation(attr.name)
23
+ next if value.blank?
24
+
25
+ if attr.kind_of? Multiple
26
+ errors.add attr.name, "is invalid" unless value.respond_to?(:all?) && value.all? { |v| v.blank? || attr.find_value(v) }
27
+ else
28
+ errors.add attr.name, "is not included in the list" unless attr.find_value(value)
29
+ end
30
+ end
31
+ end
32
+
33
+ def _set_default_value_for_enumerized_attributes
34
+ _enumerized_values_for_validation.delete_if do |k, v|
35
+ v.nil?
36
+ end
37
+
38
+ if defined?(Sequel::Plugins::Serialization::InstanceMethods)
39
+ modules = self.class.ancestors
40
+ plugin_idx = modules.index(Sequel::Plugins::Serialization::InstanceMethods)
41
+
42
+ if plugin_idx && plugin_idx < modules.index(Enumerize::SequelSupport::InstanceMethods)
43
+ abort "ERROR: You need to enable the Sequel serialization plugin before calling any enumerize methods on a model."
44
+ end
45
+
46
+ plugin_idx = modules.index(Sequel::Plugins::ValidationHelpers::InstanceMethods)
47
+
48
+ if plugin_idx && plugin_idx < modules.index(Enumerize::SequelSupport::InstanceMethods)
49
+ abort "ERROR: You need to enable the Sequel validation_helpers plugin before calling any enumerize methods on a model."
50
+ end
51
+ end
52
+
53
+ super
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/enumerize/set.rb CHANGED
@@ -37,12 +37,13 @@ module Enumerize
37
37
  end
38
38
 
39
39
  def texts
40
- @values.collect { |value| value.text }
40
+ @values.map(&:text)
41
41
  end
42
42
 
43
43
  delegate :join, to: :to_ary
44
44
 
45
45
  def ==(other)
46
+ return false unless other.respond_to?(:each)
46
47
  other.size == size && other.all? { |v| @values.include?(@attr.find_value(v)) }
47
48
  end
48
49
 
@@ -1,3 +1,3 @@
1
1
  module Enumerize
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,254 @@
1
+ require 'active_record'
2
+
3
+ silence_warnings do
4
+ ActiveRecord::Migration.verbose = false
5
+ ActiveRecord::Base.logger = Logger.new(nil)
6
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
7
+ end
8
+
9
+ ActiveRecord::Base.connection.instance_eval do
10
+ create_table :users do |t|
11
+ t.string :sex
12
+ t.string :role
13
+ t.string :account_type
14
+ end
15
+ end
16
+
17
+ class User < ActiveRecord::Base
18
+ extend Enumerize
19
+
20
+ enumerize :sex, :in => [:male, :female], scope: true
21
+ enumerize :role, :in => [:user, :admin], scope: :having_role
22
+ enumerize :account_type, :in => [:basic, :premium]
23
+ end
24
+
25
+ RSpec.describe Enumerize::Integrations::RSpec::Matcher do
26
+
27
+ let(:model) do
28
+ Class.new do
29
+ extend Enumerize
30
+
31
+ def self.name
32
+ 'Model'
33
+ end
34
+ end
35
+ end
36
+
37
+ subject do
38
+ model.new
39
+ end
40
+
41
+ describe 'without qualifier' do
42
+
43
+ it 'accepts when has defined a enumerize' do
44
+ model.enumerize(:sex, :in => [:male, :female])
45
+ expect(subject).to enumerize(:sex)
46
+ end
47
+
48
+ it 'rejects when has not defined a enumerize' do
49
+ message = 'Expected Model to define enumerize :sex'
50
+ expect do
51
+ expect(subject).to enumerize(:sex)
52
+ end.to fail_with(message)
53
+ end
54
+ end
55
+
56
+ describe '#in' do
57
+
58
+ context 'defined as array' do
59
+
60
+ before do
61
+ model.enumerize(:sex, :in => [:male, :female])
62
+ end
63
+
64
+ it 'accepts the right params as a array' do
65
+ expect(subject).to enumerize(:sex).in(:male, :female)
66
+ end
67
+
68
+ it 'accepts the the right params in a different order' do
69
+ expect(subject).to enumerize(:sex).in(:female, :male)
70
+ end
71
+
72
+ it 'rejects wrong params' do
73
+ message = 'Expected Model to define enumerize :sex in: "boy", "girl"'
74
+ expect do
75
+ expect(subject).to enumerize(:sex).in(:boy, :girl)
76
+ end.to fail_with(message)
77
+ end
78
+
79
+ it 'has the right message when negated' do
80
+ message = 'Did not expect Model to define enumerize :sex in: "female", "male"'
81
+ expect do
82
+ expect(subject).to_not enumerize(:sex).in(:male, :female)
83
+ end.to fail_with(message)
84
+ end
85
+ end
86
+
87
+ context 'defined as hash' do
88
+
89
+ before do
90
+ model.enumerize(:sex, :in => { male: 0, female: 1 })
91
+ end
92
+
93
+ it 'accepts the right params as a array' do
94
+ expect(subject).to enumerize(:sex).in(:male, :female)
95
+ end
96
+
97
+ it 'accepts the right params as a hash' do
98
+ expect(subject).to enumerize(:sex).in(male: 0, female: 1)
99
+ end
100
+
101
+ it 'accepts the right params as a hash in a different order' do
102
+ expect(subject).to enumerize(:sex).in(female: 1, male: 0)
103
+ end
104
+
105
+ it 'rejects wrong keys' do
106
+ message = 'Expected Model to define enumerize :sex in: "{:boy=>0, :girl=>1}"'
107
+ expect do
108
+ expect(subject).to enumerize(:sex).in(boy: 0, girl: 1)
109
+ end.to fail_with(message)
110
+ end
111
+
112
+ it 'rejects wrong values' do
113
+ message = 'Expected Model to define enumerize :sex in: "{:male=>2, :female=>3}"'
114
+ expect do
115
+ expect(subject).to enumerize(:sex).in(male: 2, female: 3)
116
+ end.to fail_with(message)
117
+ end
118
+ end
119
+
120
+ it 'has the right description' do
121
+ matcher = enumerize(:sex).in(:male, :female)
122
+ expect(matcher.description).to eq('define enumerize :sex in: "female", "male"')
123
+ end
124
+ end
125
+
126
+ describe '#with_default' do
127
+
128
+ before do
129
+ model.enumerize(:sex, :in => [:male, :female], default: :female)
130
+ end
131
+
132
+ it 'accepts the right default value' do
133
+ expect(subject).to enumerize(:sex).in(:male, :female).with_default(:female)
134
+ end
135
+
136
+ it 'rejects the wrong default value' do
137
+ message = 'Expected Model to define enumerize :sex in: "female", "male" with "male" as default value'
138
+ expect do
139
+ expect(subject).to enumerize(:sex).in(:male, :female).with_default(:male)
140
+ end.to fail_with(message)
141
+ end
142
+
143
+ it 'rejects if the `in` is wrong with a correct default value' do
144
+ message = 'Expected Model to define enumerize :sex in: "boy", "girl" with "female" as default value'
145
+ expect do
146
+ expect(subject).to enumerize(:sex).in(:boy, :girl).with_default(:female)
147
+ end.to fail_with(message)
148
+ end
149
+
150
+ it 'has the right description' do
151
+ matcher = enumerize(:sex).in(:male, :female).with_default(:female)
152
+ message = 'define enumerize :sex in: "female", "male" with "female" as default value'
153
+ expect(matcher.description).to eq(message)
154
+ end
155
+ end
156
+
157
+ describe '#with_i18n_scope' do
158
+
159
+ context 'defined as string' do
160
+
161
+ before do
162
+ model.enumerize(:sex, :in => [:male, :female], i18n_scope: 'sex')
163
+ end
164
+
165
+ it 'accepts the right i18n_scope' do
166
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope('sex')
167
+ end
168
+
169
+ it 'rejects the wrong i18n_scope' do
170
+ message = 'Expected Model to define enumerize :sex in: "female", "male" i18n_scope: "gender"'
171
+ expect do
172
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope('gender')
173
+ end.to fail_with(message)
174
+ end
175
+ end
176
+
177
+ context 'defined as array' do
178
+
179
+ before do
180
+ model.enumerize(:sex, :in => [:male, :female], i18n_scope: ['sex', 'more.sex'])
181
+ end
182
+
183
+ it 'accepts the wrong i18n_scope' do
184
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope(['sex', 'more.sex'])
185
+ end
186
+
187
+ it 'rejects the wrong i18n_scope' do
188
+ message = 'Expected Model to define enumerize :sex in: "female", "male" i18n_scope: ["sex"]'
189
+ expect do
190
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope(['sex'])
191
+ end.to fail_with(message)
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#with_predicates' do
197
+
198
+ it 'accepts when predicates is defined as a boolean' do
199
+ model.enumerize(:sex, :in => [:male, :female], predicates: true)
200
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(true)
201
+ end
202
+
203
+ it 'accepts when predicates is defined as a hash' do
204
+ model.enumerize(:sex, :in => [:male, :female], predicates: { prefix: true })
205
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(prefix: true)
206
+ end
207
+
208
+ it 'rejects when predicates is not defined' do
209
+ model.enumerize(:sex, :in => [:male, :female])
210
+ message = 'Expected Model to define enumerize :sex in: "female", "male" predicates: true'
211
+ expect do
212
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(true)
213
+ end.to fail_with(message)
214
+ end
215
+ end
216
+
217
+ describe '#with_multiple' do
218
+
219
+ it 'accepts when has defined the multiple' do
220
+ model.enumerize(:sex, :in => [:male, :female], multiple: true)
221
+ expect(subject).to enumerize(:sex).in(:male, :female).with_multiple(true)
222
+ end
223
+
224
+ it 'rejects when has not defined the multiple' do
225
+ model.enumerize(:sex, :in => [:male, :female])
226
+ message = 'Expected Model to define enumerize :sex in: "female", "male" multiple: true'
227
+ expect do
228
+ expect(subject).to enumerize(:sex).in(:male, :female).with_multiple(true)
229
+ end.to fail_with(message)
230
+ end
231
+ end
232
+
233
+ describe '#with_scope' do
234
+
235
+ subject do
236
+ User.new
237
+ end
238
+
239
+ it 'accepts when scope is defined as a boolean' do
240
+ expect(subject).to enumerize(:sex).in(:male, :female).with_scope(true)
241
+ end
242
+
243
+ it 'accepts when scope is defined as a hash' do
244
+ expect(subject).to enumerize(:role).in(:user, :admin).with_scope(scope: :having_role)
245
+ end
246
+
247
+ it 'rejects when scope is not defined' do
248
+ message = 'Expected User to define enumerize :account_type in: "basic", "premium" scope: true'
249
+ expect do
250
+ expect(subject).to enumerize(:account_type).in(:basic, :premium).with_scope(true)
251
+ end.to fail_with(message)
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,24 @@
1
+ require 'rails'
2
+ require 'enumerize'
3
+ require 'rspec/matchers/fail_matchers'
4
+
5
+ RSpec.configure do |config|
6
+ config.disable_monkey_patching!
7
+ config.order = :random
8
+ Kernel.srand config.seed
9
+ config.filter_run focus: true
10
+ config.run_all_when_everything_filtered = true
11
+ config.expect_with :rspec do |expectations|
12
+ expectations.syntax = :expect
13
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
14
+ end
15
+ config.mock_with :rspec do |mocks|
16
+ mocks.syntax = :expect
17
+ mocks.verify_partial_doubles = true
18
+ end
19
+ if config.files_to_run.one?
20
+ config.default_formatter = 'doc'
21
+ end
22
+ config.warnings = true
23
+ config.include RSpec::Matchers::FailMatchers
24
+ end
@@ -0,0 +1,291 @@
1
+ require 'test_helper'
2
+ require 'sequel'
3
+ require 'logger'
4
+ require 'jdbc/sqlite3' if RUBY_PLATFORM == 'java'
5
+
6
+ module SequelTest
7
+ silence_warnings do
8
+ DB = if RUBY_PLATFORM == 'java'
9
+ Sequel.connect('jdbc:sqlite::memory:')
10
+ else
11
+ Sequel.sqlite
12
+ end
13
+ DB.loggers << Logger.new(nil)
14
+ end
15
+
16
+ DB.create_table :users do
17
+ primary_key :id
18
+ String :sex
19
+ String :role
20
+ String :lambda_role
21
+ String :name
22
+ String :interests
23
+ String :status
24
+ String :account_type, default: "basic"
25
+ end
26
+
27
+ DB.create_table :documents do
28
+ primary_key :id
29
+ String :visibility
30
+ Time :created_at
31
+ Time :updated_at
32
+ end
33
+
34
+ class Document < Sequel::Model
35
+ extend Enumerize
36
+ enumerize :visibility, :in => [:public, :private, :protected], :scope => true, :default => :public
37
+ end
38
+
39
+ module RoleEnum
40
+ extend Enumerize
41
+ enumerize :role, :in => [:user, :admin], :default => :user, scope: :having_role
42
+ enumerize :lambda_role, :in => [:user, :admin], :default => lambda { :admin }
43
+ end
44
+
45
+ class User < Sequel::Model
46
+ plugin :serialization, :json, :interests
47
+ plugin :dirty
48
+ plugin :defaults_setter
49
+ plugin :validation_helpers
50
+ extend Enumerize
51
+ include RoleEnum
52
+
53
+ enumerize :sex, :in => [:male, :female]
54
+
55
+ enumerize :interests, :in => [:music, :sports, :dancing, :programming], :multiple => true
56
+
57
+ enumerize :status, :in => { active: 1, blocked: 2 }, scope: true
58
+
59
+ enumerize :account_type, :in => [:basic, :premium]
60
+ end
61
+
62
+ class UniqStatusUser < User
63
+ def validate
64
+ super
65
+ validates_unique :status
66
+ validates_presence :sex
67
+ end
68
+ end
69
+
70
+ describe Enumerize::SequelSupport do
71
+ it 'sets nil if invalid value is passed' do
72
+ user = User.new
73
+ user.sex = :invalid
74
+ user.sex.must_equal nil
75
+ end
76
+
77
+ it 'saves value' do
78
+ User.filter.delete
79
+ user = User.new
80
+ user.sex = :female
81
+ user.save
82
+ user.sex.must_equal 'female'
83
+ end
84
+
85
+ it 'loads value' do
86
+ User.filter.delete
87
+ User.create(:sex => :male)
88
+ store_translations(:en, :enumerize => {:sex => {:male => 'Male'}}) do
89
+ user = User.first
90
+ user.sex.must_equal 'male'
91
+ user.sex_text.must_equal 'Male'
92
+ end
93
+ end
94
+
95
+ it 'has default value' do
96
+ User.new.role.must_equal 'user'
97
+ User.new.values[:role].must_equal 'user'
98
+ end
99
+
100
+ it 'does not set default value for not selected attributes' do
101
+ User.filter.delete
102
+ User.create(:sex => :male)
103
+
104
+ assert_equal [:id], User.select(:id).first.values.keys
105
+ end
106
+
107
+ it 'has default value with lambda' do
108
+ User.new.lambda_role.must_equal 'admin'
109
+ User.new.values[:lambda_role].must_equal 'admin'
110
+ end
111
+ it 'uses after_initialize callback to set default value' do
112
+ User.filter.delete
113
+ User.create(sex: 'male', lambda_role: nil)
114
+
115
+ user = User.where(:sex => 'male').first
116
+ user.lambda_role.must_equal 'admin'
117
+ end
118
+
119
+ it 'uses default value from db column' do
120
+ User.new.account_type.must_equal 'basic'
121
+ end
122
+
123
+ it 'validates inclusion' do
124
+ user = User.new
125
+ user.role = 'wrong'
126
+ user.wont_be :valid?
127
+ user.errors[:role].must_include 'is not included in the list'
128
+ end
129
+
130
+ it 'validates inclusion on mass assignment' do
131
+ assert_raises Sequel::ValidationFailed do
132
+ User.create(role: 'wrong')
133
+ end
134
+ end
135
+
136
+ it "uses persisted value for validation if it hasn't been set" do
137
+ user = User.create :sex => :male
138
+ User[user.id].read_attribute_for_validation(:sex).must_equal 'male'
139
+ end
140
+
141
+ it 'is valid with empty string assigned' do
142
+ user = User.new
143
+ user.role = ''
144
+ user.must_be :valid?
145
+ end
146
+
147
+ it 'stores nil when empty string assigned' do
148
+ user = User.new
149
+ user.role = ''
150
+ user.values[:role].must_equal nil
151
+ end
152
+
153
+ it 'supports multiple attributes' do
154
+ user = User.new
155
+ user.interests ||= []
156
+ user.interests.must_be_empty
157
+ user.interests << "music"
158
+ user.interests.must_equal %w(music)
159
+ user.save
160
+
161
+ user = User[user.id]
162
+ user.interests.must_be_instance_of Enumerize::Set
163
+ user.interests.must_equal %w(music)
164
+ user.interests << "sports"
165
+ user.interests.must_equal %w(music sports)
166
+
167
+ user.interests = []
168
+ interests = user.interests
169
+ interests << "music"
170
+ interests.must_equal %w(music)
171
+ interests << "dancing"
172
+ interests.must_equal %w(music dancing)
173
+ end
174
+
175
+ it 'returns invalid multiple value for validation' do
176
+ user = User.new
177
+ user.interests << :music
178
+ user.interests << :invalid
179
+ values = user.read_attribute_for_validation(:interests)
180
+ values.must_equal %w(music invalid)
181
+ end
182
+
183
+ it 'validates multiple attributes' do
184
+ user = User.new
185
+ user.interests << :invalid
186
+ user.wont_be :valid?
187
+
188
+ user.interests = Object.new
189
+ user.wont_be :valid?
190
+
191
+ user.interests = ['music', '']
192
+ user.must_be :valid?
193
+ end
194
+
195
+ it 'stores custom values for multiple attributes' do
196
+ User.filter.delete
197
+
198
+ klass = Class.new(User)
199
+ klass.enumerize :interests, in: { music: 0, sports: 1, dancing: 2, programming: 3}, multiple: true
200
+
201
+ user = klass.new
202
+ user.interests << :music
203
+ user.interests.must_equal %w(music)
204
+ user.save
205
+
206
+ user = klass[user.id]
207
+ user.interests.must_equal %w(music)
208
+ end
209
+
210
+ it 'adds scope' do
211
+ User.filter.delete
212
+
213
+ user_1 = User.create(status: :active, role: :admin)
214
+ user_2 = User.create(status: :blocked)
215
+
216
+ User.with_status(:active).to_a.must_equal [user_1]
217
+ User.with_status(:blocked).to_a.must_equal [user_2]
218
+ User.with_status(:active, :blocked).to_set.must_equal [user_1, user_2].to_set
219
+
220
+ User.without_status(:active).to_a.must_equal [user_2]
221
+ User.without_status(:active, :blocked).to_a.must_equal []
222
+
223
+ User.having_role(:admin).to_a.must_equal [user_1]
224
+ end
225
+
226
+ it 'allows either key or value as valid' do
227
+ user_1 = User.new(status: :active)
228
+ user_2 = User.new(status: 1)
229
+ user_3 = User.new(status: '1')
230
+
231
+ user_1.status.must_equal 'active'
232
+ user_2.status.must_equal 'active'
233
+ user_3.status.must_equal 'active'
234
+
235
+ user_1.must_be :valid?
236
+ user_2.must_be :valid?
237
+ user_3.must_be :valid?
238
+ end
239
+
240
+ it 'supports defining enumerized attributes on abstract class' do
241
+ Document.filter.delete
242
+
243
+ document = Document.new
244
+ document.visibility = :protected
245
+ document.visibility.must_equal 'protected'
246
+ end
247
+
248
+ it 'supports defining enumerized scopes on abstract class' do
249
+ Document.filter.delete
250
+
251
+ document_1 = Document.create(visibility: :public)
252
+ document_2 = Document.create(visibility: :private)
253
+
254
+ Document.with_visibility(:public).to_a.must_equal [document_1]
255
+ end
256
+
257
+ it 'validates uniqueness' do
258
+ user = User.create(status: :active, sex: "male")
259
+
260
+ user = UniqStatusUser.new
261
+ user.sex = "male"
262
+ user.status = :active
263
+ user.valid?.must_equal false
264
+
265
+ user.errors[:status].wont_be :empty?
266
+ end
267
+
268
+ it "doesn't update record" do
269
+ Document.filter.delete
270
+
271
+ expected = Time.new(2010, 10, 10)
272
+
273
+ document = Document.new
274
+ document.updated_at = expected
275
+ document.save
276
+
277
+ document = Document.last
278
+ document.save
279
+
280
+ assert_equal expected, document.updated_at
281
+ end
282
+
283
+ it 'changes from dirty should be serialized as scalar values' do
284
+ user = User.create(:status => :active)
285
+ user.status = :blocked
286
+
287
+ expected = { status: [1, 2] }.to_yaml
288
+ assert_equal expected, user.column_changes.to_yaml
289
+ end
290
+ end
291
+ end