enumerize 0.8.0 → 1.1.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -2
  5. data/CHANGELOG.md +91 -0
  6. data/Gemfile +4 -22
  7. data/{Gemfile.rails4 → Gemfile.global} +2 -5
  8. data/Gemfile.mongo_mapper +5 -0
  9. data/Gemfile.rails40 +5 -0
  10. data/README.md +171 -8
  11. data/Rakefile +2 -4
  12. data/lib/enumerize/activerecord.rb +3 -29
  13. data/lib/enumerize/attribute.rb +41 -9
  14. data/lib/enumerize/base.rb +25 -10
  15. data/lib/enumerize/hooks/sequel_dataset.rb +19 -0
  16. data/lib/enumerize/integrations/rspec/matcher.rb +107 -26
  17. data/lib/enumerize/mongoid.rb +13 -0
  18. data/lib/enumerize/predicatable.rb +7 -16
  19. data/lib/enumerize/predicates.rb +3 -1
  20. data/lib/enumerize/scope/activerecord.rb +37 -0
  21. data/lib/enumerize/scope/mongoid.rb +35 -0
  22. data/lib/enumerize/scope/sequel.rb +40 -0
  23. data/lib/enumerize/sequel.rb +57 -0
  24. data/lib/enumerize/set.rb +18 -8
  25. data/lib/enumerize/value.rb +8 -7
  26. data/lib/enumerize/version.rb +1 -1
  27. data/lib/enumerize.rb +23 -1
  28. data/spec/enumerize/integrations/rspec/matcher_spec.rb +258 -0
  29. data/spec/spec_helper.rb +28 -0
  30. data/test/activerecord_test.rb +58 -7
  31. data/test/attribute_test.rb +22 -0
  32. data/test/formtastic_test.rb +3 -12
  33. data/test/mongo_mapper_test.rb +6 -0
  34. data/test/mongoid_test.rb +55 -6
  35. data/test/multiple_test.rb +21 -0
  36. data/test/predicates_test.rb +6 -0
  37. data/test/sequel_test.rb +291 -0
  38. data/test/set_test.rb +14 -0
  39. data/test/simple_form_test.rb +0 -1
  40. data/test/support/view_test_helper.rb +4 -0
  41. data/test/test_helper.rb +23 -2
  42. data/test/value_test.rb +51 -21
  43. metadata +37 -8
  44. data/lib/enumerize/form_helper.rb +0 -23
  45. data/test/rspec_matcher_test.rb +0 -76
  46. data/test/rspec_spec.rb +0 -13
@@ -0,0 +1,19 @@
1
+ module Enumerize
2
+ module Hooks
3
+ module SequelDataset
4
+ def self.included(klass)
5
+ klass.alias_method_chain :literal_append, :enumerize
6
+ end
7
+
8
+ def literal_append_with_enumerize(sql, v)
9
+ if v.is_a?(Enumerize::Value)
10
+ literal_append(sql, v.value)
11
+ else
12
+ literal_append_without_enumerize(sql, v)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ::Sequel::Dataset.send :include, Enumerize::Hooks::SequelDataset
@@ -2,37 +2,57 @@ module Enumerize
2
2
  module Integrations
3
3
  module RSpec
4
4
  class Matcher
5
- attr_accessor :attr, :values, :subject, :default
6
5
 
7
- def initialize(attr)
8
- self.attr = attr
6
+ def initialize(expected_attr)
7
+ self.expected_attr = expected_attr
9
8
  end
10
9
 
11
- def in(*values)
12
- self.values = values.map(&:to_s).sort
10
+ def in(*expected_values)
11
+ self.expected_values = expected_values.flatten
13
12
  self
14
13
  end
15
14
 
16
- def with_default(default)
17
- self.default = default.to_s
15
+ def with_default(expected_default)
16
+ self.expected_default = expected_default.to_s
18
17
  self
19
18
  end
20
19
 
21
- def failure_message
22
- message = " expected :#{attr} to allow value#{values.size == 1 ? nil : 's'}: #{quote_values(values)},"
23
- message += " but it allows #{quote_values(enumerized_values)} instead"
20
+ def with_i18n_scope(expected_i18n_scope)
21
+ self.expected_i18n_scope = expected_i18n_scope
22
+ self
23
+ end
24
24
 
25
- if default && !matches_default_value?
26
- message = " expected :#{attr} to have #{default.inspect} as default value,"
27
- message += " but it sets #{enumerized_default.inspect} instead"
28
- end
25
+ def with_predicates(expected_predicates)
26
+ self.expected_predicates = expected_predicates
27
+ self
28
+ end
29
+
30
+ def with_multiple(expected_multiple)
31
+ self.expected_multiple = expected_multiple
32
+ self
33
+ end
34
+
35
+ def with_scope(expected_scope)
36
+ self.expected_scope = expected_scope
37
+ self
38
+ end
39
+
40
+ def failure_message
41
+ "Expected #{expectation}"
42
+ end
29
43
 
30
- message
44
+ def failure_message_when_negated
45
+ "Did not expect #{expectation}"
31
46
  end
32
47
 
33
48
  def description
34
- description = "enumerize :#{attr} in: #{quote_values(values)}"
35
- description += " with #{default.inspect} as default value" if default
49
+ description = "define enumerize :#{expected_attr}"
50
+ description += " in: #{quote_values(expected_values)}" if expected_values
51
+ description += " with #{expected_default.inspect} as default value" if expected_default
52
+ description += " i18n_scope: #{expected_i18n_scope.inspect}" if expected_i18n_scope
53
+ description += " predicates: #{expected_predicates.inspect}" if expected_predicates
54
+ description += " multiple: #{expected_multiple.inspect}" if expected_multiple
55
+ description += " scope: #{expected_scope.inspect}" if expected_scope
36
56
 
37
57
  description
38
58
  end
@@ -41,36 +61,97 @@ module Enumerize
41
61
  self.subject = subject
42
62
  matches = true
43
63
 
44
- matches &= matches_attributes?
45
- matches &= matches_default_value? if default
64
+ matches &= matches_attribute?
65
+ matches &= matches_values? if expected_values
66
+ matches &= matches_default_value? if expected_default
67
+ matches &= matches_i18n_scope? if expected_i18n_scope
68
+ matches &= matches_predicates? if expected_predicates
69
+ matches &= matches_multiple? if expected_multiple
70
+ matches &= matches_scope? if expected_scope
46
71
 
47
72
  matches
48
73
  end
49
74
 
50
75
  private
76
+ attr_accessor :expected_attr, :expected_values, :subject, :expected_default,
77
+ :expected_i18n_scope, :expected_predicates, :expected_multiple,
78
+ :expected_scope
51
79
 
52
- def matches_attributes?
53
- values == enumerized_values
80
+ def expectation
81
+ "#{subject.class.name} to #{description}"
82
+ end
83
+
84
+ def matches_attribute?
85
+ attributes.present?
86
+ end
87
+
88
+ def matches_values?
89
+ matches_array_values? || matches_hash_values?
90
+ end
91
+
92
+ def matches_array_values?
93
+ sorted_values == enumerized_values
94
+ end
95
+
96
+ def matches_hash_values?
97
+ return unless expected_values.first.is_a?(Hash)
98
+ expected_values.first.all? { |k, v| enumerized_value_hash[k.to_s] == v; }
54
99
  end
55
100
 
56
101
  def matches_default_value?
57
- default == enumerized_default
102
+ expected_default == enumerized_default
103
+ end
104
+
105
+ def matches_i18n_scope?
106
+ attributes.i18n_scope == expected_i18n_scope
107
+ end
108
+
109
+ def matches_predicates?
110
+ if expected_predicates.is_a?(TrueClass)
111
+ subject.respond_to?("#{sorted_values.first}?")
112
+ else
113
+ subject.respond_to?("#{expected_attr}_#{attributes.values.first}?")
114
+ end
115
+ end
116
+
117
+ def matches_multiple?
118
+ subject.instance_variable_defined?("@_#{expected_attr}_enumerized_set")
119
+ end
120
+
121
+ def matches_scope?
122
+ if expected_scope.is_a?(TrueClass)
123
+ subject_class.respond_to?("with_#{expected_attr}")
124
+ else
125
+ subject_class.respond_to?(expected_scope[:scope])
126
+ end
127
+ end
128
+
129
+ def sorted_values
130
+ @sorted_values ||=expected_values.map(&:to_s).sort
58
131
  end
59
132
 
60
133
  def enumerized_values
61
- @enumerized_values ||= attributes[attr.to_s].values.sort
134
+ @enumerized_values ||= attributes.values.sort
62
135
  end
63
136
 
64
137
  def enumerized_default
65
- @enumerized_default ||= attributes[attr.to_s].default_value
138
+ @enumerized_default ||= attributes.default_value
139
+ end
140
+
141
+ def enumerized_value_hash
142
+ @enumerized_value_hash ||= attributes.instance_variable_get('@value_hash')
66
143
  end
67
144
 
68
145
  def attributes
69
- subject.class.enumerized_attributes.attributes
146
+ subject_class.enumerized_attributes.attributes[expected_attr.to_s]
147
+ end
148
+
149
+ def subject_class
150
+ @subject_class ||= subject.class
70
151
  end
71
152
 
72
153
  def quote_values(values)
73
- values.map(&:inspect).join(', ')
154
+ sorted_values.map(&:inspect).join(', ')
74
155
  end
75
156
  end
76
157
  end
@@ -0,0 +1,13 @@
1
+ module Enumerize
2
+ module MongoidSupport
3
+ def enumerize(name, options={})
4
+ super
5
+
6
+ _enumerize_module.dependent_eval do
7
+ if self < ::Mongoid::Document
8
+ after_initialize :_set_default_value_for_enumerized_attributes
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,29 +1,20 @@
1
1
  module Enumerize
2
2
  module Predicatable
3
- def method_missing(method, *args, &block)
4
- if boolean_method?(method)
5
- define_query_methods
6
- send(method, *args, &block)
7
- else
8
- super
9
- end
10
- end
11
-
12
3
  def respond_to_missing?(method, include_private=false)
13
- boolean_method?(method)
4
+ predicate_method?(method) || super
14
5
  end
15
6
 
16
7
  private
17
8
 
18
- def define_query_methods
19
- @attr.values.each do |value|
20
- unless singleton_methods.include?(:"#{value}?")
21
- define_query_method(value)
22
- end
9
+ def method_missing(method, *args, &block)
10
+ if predicate_method?(method)
11
+ predicate_call(method[0..-2], *args, &block)
12
+ else
13
+ super
23
14
  end
24
15
  end
25
16
 
26
- def boolean_method?(method)
17
+ def predicate_method?(method)
27
18
  method[-1] == '?' && @attr.values.include?(method[0..-2])
28
19
  end
29
20
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
1
3
  module Enumerize
2
4
  # Predicate methods.
3
5
  #
@@ -61,7 +63,7 @@ module Enumerize
61
63
  end
62
64
 
63
65
  def names
64
- values.map { |v| "#{v}?" }
66
+ values.map { |v| "#{v.tr('-', '_')}?" }
65
67
  end
66
68
 
67
69
  def build(klass)
@@ -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,25 +17,26 @@ 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.defaults.#{@attr.name}.#{self}"]
37
38
  i18n_keys << [:"enumerize.#{@attr.name}.#{self}"]
38
- i18n_keys << self.humanize # humanize value if there are no translations
39
+ i18n_keys << self.underscore.humanize # humanize value if there are no translations
39
40
  i18n_keys.flatten
40
41
  end
41
42
  end
@@ -1,3 +1,3 @@
1
1
  module Enumerize
2
- VERSION = "0.8.0"
2
+ VERSION = "1.1.1"
3
3
  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'