enumerize 0.8.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -12
  5. data/CHANGELOG.md +141 -0
  6. data/Gemfile +5 -21
  7. data/Gemfile.global +20 -0
  8. data/Gemfile.mongo_mapper +7 -0
  9. data/Gemfile.rails42 +7 -0
  10. data/Gemfile.rails51 +7 -0
  11. data/README.md +185 -9
  12. data/Rakefile +2 -4
  13. data/enumerize.gemspec +1 -0
  14. data/lib/enumerize/activerecord.rb +68 -27
  15. data/lib/enumerize/attribute.rb +42 -10
  16. data/lib/enumerize/base.rb +28 -17
  17. data/lib/enumerize/hooks/formtastic.rb +3 -8
  18. data/lib/enumerize/hooks/sequel_dataset.rb +15 -0
  19. data/lib/enumerize/hooks/simple_form.rb +5 -11
  20. data/lib/enumerize/hooks/uniqueness.rb +3 -8
  21. data/lib/enumerize/integrations/rspec/matcher.rb +107 -26
  22. data/lib/enumerize/mongoid.rb +13 -0
  23. data/lib/enumerize/predicatable.rb +7 -16
  24. data/lib/enumerize/predicates.rb +3 -1
  25. data/lib/enumerize/scope/activerecord.rb +37 -0
  26. data/lib/enumerize/scope/mongoid.rb +35 -0
  27. data/lib/enumerize/scope/sequel.rb +40 -0
  28. data/lib/enumerize/sequel.rb +57 -0
  29. data/lib/enumerize/set.rb +18 -8
  30. data/lib/enumerize/value.rb +10 -9
  31. data/lib/enumerize/version.rb +3 -1
  32. data/lib/enumerize.rb +23 -1
  33. data/spec/enumerize/integrations/rspec/matcher_spec.rb +258 -0
  34. data/spec/spec_helper.rb +28 -0
  35. data/test/activerecord_test.rb +238 -10
  36. data/test/attribute_test.rb +22 -0
  37. data/test/base_test.rb +7 -7
  38. data/test/formtastic_test.rb +3 -12
  39. data/test/mongo_mapper_test.rb +7 -1
  40. data/test/mongoid_test.rb +63 -7
  41. data/test/multiple_test.rb +21 -0
  42. data/test/predicates_test.rb +6 -0
  43. data/test/sequel_test.rb +291 -0
  44. data/test/set_test.rb +14 -0
  45. data/test/simple_form_test.rb +0 -1
  46. data/test/support/mock_controller.rb +4 -0
  47. data/test/support/view_test_helper.rb +4 -0
  48. data/test/test_helper.rb +23 -2
  49. data/test/value_test.rb +51 -21
  50. metadata +40 -9
  51. data/Gemfile.rails4 +0 -23
  52. data/lib/enumerize/form_helper.rb +0 -23
  53. data/test/rspec_matcher_test.rb +0 -76
  54. data/test/rspec_spec.rb +0 -13
@@ -0,0 +1,37 @@
1
+ module Enumerize
2
+ module Scope
3
+ module ActiveRecord
4
+ def enumerize(name, options={})
5
+ super
6
+
7
+ _enumerize_module.dependent_eval do
8
+ if self < ::ActiveRecord::Base
9
+ if options[:scope]
10
+ _define_activerecord_scope_methods!(name, options)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def _define_activerecord_scope_methods!(name, options)
19
+ scope_name = options[:scope] == true ? "with_#{name}" : options[:scope]
20
+
21
+ define_singleton_method scope_name do |*values|
22
+ values = enumerized_attributes[name].find_values(*values).map(&:value)
23
+ values = values.first if values.size == 1
24
+
25
+ where(name => values)
26
+ end
27
+
28
+ if options[:scope] == true
29
+ define_singleton_method "without_#{name}" do |*values|
30
+ values = enumerized_attributes[name].find_values(*values).map(&:value)
31
+ where(arel_table[name].not_in(values))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ module Enumerize
2
+ module Scope
3
+ module Mongoid
4
+ def enumerize(name, options={})
5
+ super
6
+
7
+ _enumerize_module.dependent_eval do
8
+ if self < ::Mongoid::Document
9
+ if options[:scope]
10
+ _define_mongoid_scope_methods!(name, options)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def _define_mongoid_scope_methods!(name, options)
19
+ scope_name = options[:scope] == true ? "with_#{name}" : options[:scope]
20
+
21
+ define_singleton_method scope_name do |*values|
22
+ values = enumerized_attributes[name].find_values(*values).map(&:value)
23
+ self.in(name => values)
24
+ end
25
+
26
+ if options[:scope] == true
27
+ define_singleton_method "without_#{name}" do |*values|
28
+ values = enumerized_attributes[name].find_values(*values).map(&:value)
29
+ not_in(name => values)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -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
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
1
3
  module Enumerize
2
4
  class Set
3
5
  include Enumerable
@@ -8,12 +10,15 @@ module Enumerize
8
10
  def initialize(obj, attr, values)
9
11
  @obj = obj
10
12
  @attr = attr
11
- @values = ::Set.new
13
+ @values = []
12
14
 
13
15
  if values.respond_to?(:each)
14
16
  values.each do |input|
15
17
  value = @attr.find_value(input)
16
- @values << value if value
18
+
19
+ if value && !@values.include?(value)
20
+ @values << value
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -31,9 +36,14 @@ module Enumerize
31
36
  @values.to_a
32
37
  end
33
38
 
39
+ def texts
40
+ @values.map(&:text)
41
+ end
42
+
34
43
  delegate :join, to: :to_ary
35
44
 
36
45
  def ==(other)
46
+ return false unless other.respond_to?(:each)
37
47
  other.size == size && other.all? { |v| @values.include?(@attr.find_value(v)) }
38
48
  end
39
49
 
@@ -52,14 +62,14 @@ module Enumerize
52
62
  "#<Enumerize::Set {#{join(', ')}}>"
53
63
  end
54
64
 
65
+ def encode_with(coder)
66
+ coder.represent_object(Array, @values)
67
+ end
68
+
55
69
  private
56
70
 
57
- def define_query_method(value)
58
- singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
59
- def #{value}?
60
- include?("#{value}")
61
- end
62
- RUBY
71
+ def predicate_call(value)
72
+ include?(value)
63
73
  end
64
74
 
65
75
  def mutate!
@@ -17,26 +17,27 @@ module Enumerize
17
17
  I18n.t(i18n_keys[0], :default => i18n_keys[1..-1])
18
18
  end
19
19
 
20
+ def ==(other)
21
+ super(other.to_s) || value == other
22
+ end
23
+
20
24
  def encode_with(coder)
21
25
  coder.represent_object(self.class.superclass, @value)
22
26
  end
23
27
 
24
28
  private
25
29
 
26
- def define_query_method(value)
27
- singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
28
- def #{value}?
29
- #{value == self}
30
- end
31
- RUBY
30
+ def predicate_call(value)
31
+ value == self
32
32
  end
33
33
 
34
34
  def i18n_keys
35
35
  @i18n_keys ||= begin
36
36
  i18n_keys = i18n_scopes
37
- i18n_keys << [:"enumerize.#{@attr.name}.#{self}"]
38
- i18n_keys << self.humanize # humanize value if there are no translations
39
- i18n_keys.flatten
37
+ i18n_keys << :"enumerize.defaults.#{@attr.name}.#{self}"
38
+ i18n_keys << :"enumerize.#{@attr.name}.#{self}"
39
+ i18n_keys << self.underscore.humanize # humanize value if there are no translations
40
+ i18n_keys
40
41
  end
41
42
  end
42
43
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Enumerize
2
- VERSION = "0.8.0"
4
+ VERSION = '2.1.2'
3
5
  end
data/lib/enumerize.rb CHANGED
@@ -13,6 +13,14 @@ module Enumerize
13
13
  autoload :ModuleAttributes, 'enumerize/module_attributes'
14
14
 
15
15
  autoload :ActiveRecordSupport, 'enumerize/activerecord'
16
+ autoload :SequelSupport, 'enumerize/sequel'
17
+ autoload :MongoidSupport, 'enumerize/mongoid'
18
+
19
+ module Scope
20
+ autoload :ActiveRecord, 'enumerize/scope/activerecord'
21
+ autoload :Sequel, 'enumerize/scope/sequel'
22
+ autoload :Mongoid, 'enumerize/scope/mongoid'
23
+ end
16
24
 
17
25
  def self.included(base)
18
26
  ActiveSupport::Deprecation.warn '`include Enumerize` was deprecated. Please use `extend Enumerize`.', caller
@@ -22,7 +30,21 @@ module Enumerize
22
30
  def self.extended(base)
23
31
  base.send :include, Enumerize::Base
24
32
  base.extend Enumerize::Predicates
25
- base.extend Enumerize::ActiveRecordSupport
33
+
34
+ if defined?(::ActiveRecord::Base)
35
+ base.extend Enumerize::ActiveRecordSupport
36
+ base.extend Enumerize::Scope::ActiveRecord
37
+ end
38
+
39
+ if defined?(::Mongoid::Document)
40
+ base.extend Enumerize::MongoidSupport
41
+ base.extend Enumerize::Scope::Mongoid
42
+ end
43
+
44
+ if defined?(::Sequel::Model)
45
+ base.extend Enumerize::SequelSupport
46
+ base.extend Enumerize::Scope::Sequel
47
+ end
26
48
 
27
49
  if defined?(::RailsAdmin)
28
50
  require 'enumerize/integrations/rails_admin'
@@ -0,0 +1,258 @@
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 an array' do
65
+ expect(subject).to enumerize(:sex).in([:male, :female])
66
+ end
67
+
68
+ it 'accepts the right params as regular params' do
69
+ expect(subject).to enumerize(:sex).in(:male, :female)
70
+ end
71
+
72
+ it 'accepts the the right params in a different order' do
73
+ expect(subject).to enumerize(:sex).in(:female, :male)
74
+ end
75
+
76
+ it 'rejects wrong params' do
77
+ message = 'Expected Model to define enumerize :sex in: "boy", "girl"'
78
+ expect do
79
+ expect(subject).to enumerize(:sex).in(:boy, :girl)
80
+ end.to fail_with(message)
81
+ end
82
+
83
+ it 'has the right message when negated' do
84
+ message = 'Did not expect Model to define enumerize :sex in: "female", "male"'
85
+ expect do
86
+ expect(subject).to_not enumerize(:sex).in(:male, :female)
87
+ end.to fail_with(message)
88
+ end
89
+ end
90
+
91
+ context 'defined as hash' do
92
+
93
+ before do
94
+ model.enumerize(:sex, :in => { male: 0, female: 1 })
95
+ end
96
+
97
+ it 'accepts the right params as an array' do
98
+ expect(subject).to enumerize(:sex).in(:male, :female)
99
+ end
100
+
101
+ it 'accepts the right params as a hash' do
102
+ expect(subject).to enumerize(:sex).in(male: 0, female: 1)
103
+ end
104
+
105
+ it 'accepts the right params as a hash in a different order' do
106
+ expect(subject).to enumerize(:sex).in(female: 1, male: 0)
107
+ end
108
+
109
+ it 'rejects wrong keys' do
110
+ message = 'Expected Model to define enumerize :sex in: "{:boy=>0, :girl=>1}"'
111
+ expect do
112
+ expect(subject).to enumerize(:sex).in(boy: 0, girl: 1)
113
+ end.to fail_with(message)
114
+ end
115
+
116
+ it 'rejects wrong values' do
117
+ message = 'Expected Model to define enumerize :sex in: "{:male=>2, :female=>3}"'
118
+ expect do
119
+ expect(subject).to enumerize(:sex).in(male: 2, female: 3)
120
+ end.to fail_with(message)
121
+ end
122
+ end
123
+
124
+ it 'has the right description' do
125
+ matcher = enumerize(:sex).in(:male, :female)
126
+ expect(matcher.description).to eq('define enumerize :sex in: "female", "male"')
127
+ end
128
+ end
129
+
130
+ describe '#with_default' do
131
+
132
+ before do
133
+ model.enumerize(:sex, :in => [:male, :female], default: :female)
134
+ end
135
+
136
+ it 'accepts the right default value' do
137
+ expect(subject).to enumerize(:sex).in(:male, :female).with_default(:female)
138
+ end
139
+
140
+ it 'rejects the wrong default value' do
141
+ message = 'Expected Model to define enumerize :sex in: "female", "male" with "male" as default value'
142
+ expect do
143
+ expect(subject).to enumerize(:sex).in(:male, :female).with_default(:male)
144
+ end.to fail_with(message)
145
+ end
146
+
147
+ it 'rejects if the `in` is wrong with a correct default value' do
148
+ message = 'Expected Model to define enumerize :sex in: "boy", "girl" with "female" as default value'
149
+ expect do
150
+ expect(subject).to enumerize(:sex).in(:boy, :girl).with_default(:female)
151
+ end.to fail_with(message)
152
+ end
153
+
154
+ it 'has the right description' do
155
+ matcher = enumerize(:sex).in(:male, :female).with_default(:female)
156
+ message = 'define enumerize :sex in: "female", "male" with "female" as default value'
157
+ expect(matcher.description).to eq(message)
158
+ end
159
+ end
160
+
161
+ describe '#with_i18n_scope' do
162
+
163
+ context 'defined as string' do
164
+
165
+ before do
166
+ model.enumerize(:sex, :in => [:male, :female], i18n_scope: 'sex')
167
+ end
168
+
169
+ it 'accepts the right i18n_scope' do
170
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope('sex')
171
+ end
172
+
173
+ it 'rejects the wrong i18n_scope' do
174
+ message = 'Expected Model to define enumerize :sex in: "female", "male" i18n_scope: "gender"'
175
+ expect do
176
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope('gender')
177
+ end.to fail_with(message)
178
+ end
179
+ end
180
+
181
+ context 'defined as array' do
182
+
183
+ before do
184
+ model.enumerize(:sex, :in => [:male, :female], i18n_scope: ['sex', 'more.sex'])
185
+ end
186
+
187
+ it 'accepts the wrong i18n_scope' do
188
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope(['sex', 'more.sex'])
189
+ end
190
+
191
+ it 'rejects the wrong i18n_scope' do
192
+ message = 'Expected Model to define enumerize :sex in: "female", "male" i18n_scope: ["sex"]'
193
+ expect do
194
+ expect(subject).to enumerize(:sex).in(:male, :female).with_i18n_scope(['sex'])
195
+ end.to fail_with(message)
196
+ end
197
+ end
198
+ end
199
+
200
+ describe '#with_predicates' do
201
+
202
+ it 'accepts when predicates is defined as a boolean' do
203
+ model.enumerize(:sex, :in => [:male, :female], predicates: true)
204
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(true)
205
+ end
206
+
207
+ it 'accepts when predicates is defined as a hash' do
208
+ model.enumerize(:sex, :in => [:male, :female], predicates: { prefix: true })
209
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(prefix: true)
210
+ end
211
+
212
+ it 'rejects when predicates is not defined' do
213
+ model.enumerize(:sex, :in => [:male, :female])
214
+ message = 'Expected Model to define enumerize :sex in: "female", "male" predicates: true'
215
+ expect do
216
+ expect(subject).to enumerize(:sex).in(:male, :female).with_predicates(true)
217
+ end.to fail_with(message)
218
+ end
219
+ end
220
+
221
+ describe '#with_multiple' do
222
+
223
+ it 'accepts when has defined the multiple' do
224
+ model.enumerize(:sex, :in => [:male, :female], multiple: true)
225
+ expect(subject).to enumerize(:sex).in(:male, :female).with_multiple(true)
226
+ end
227
+
228
+ it 'rejects when has not defined the multiple' do
229
+ model.enumerize(:sex, :in => [:male, :female])
230
+ message = 'Expected Model to define enumerize :sex in: "female", "male" multiple: true'
231
+ expect do
232
+ expect(subject).to enumerize(:sex).in(:male, :female).with_multiple(true)
233
+ end.to fail_with(message)
234
+ end
235
+ end
236
+
237
+ describe '#with_scope' do
238
+
239
+ subject do
240
+ User.new
241
+ end
242
+
243
+ it 'accepts when scope is defined as a boolean' do
244
+ expect(subject).to enumerize(:sex).in(:male, :female).with_scope(true)
245
+ end
246
+
247
+ it 'accepts when scope is defined as a hash' do
248
+ expect(subject).to enumerize(:role).in(:user, :admin).with_scope(scope: :having_role)
249
+ end
250
+
251
+ it 'rejects when scope is not defined' do
252
+ message = 'Expected User to define enumerize :account_type in: "basic", "premium" scope: true'
253
+ expect do
254
+ expect(subject).to enumerize(:account_type).in(:basic, :premium).with_scope(true)
255
+ end.to fail_with(message)
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,28 @@
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
+
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.syntax = :expect
14
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
15
+ end
16
+
17
+ config.mock_with :rspec do |mocks|
18
+ mocks.syntax = :expect
19
+ mocks.verify_partial_doubles = true
20
+ end
21
+
22
+ if config.files_to_run.one?
23
+ config.default_formatter = 'doc'
24
+ end
25
+
26
+ config.warnings = true
27
+ config.include RSpec::Matchers::FailMatchers
28
+ end