enumerize 0.8.0 → 2.1.2

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