enumerize 0.1.1 → 0.2.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.
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  before_install:
3
- - gem install bundler --pre
3
+ - gem install bundler
4
4
  rvm:
5
5
  - 1.9.2
6
6
  - 1.9.3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.2.0 (March 29, 2012) ##
2
+
3
+ ### enhancements
4
+ * Ability to enumerize attributes in a module and then include it into classes (by [@lest](https://github.com/lest))
5
+ * Add error to a model when attribute value is not included in allowed list (by [@lest](https://github.com/lest))
6
+
7
+ ### bug fix
8
+ * Inheriting enumerized attributes (by [@cgunther](https://github.com/cgunther) and [@nashby](https://github.com/nashby))
9
+ * Don't cast nil to string (by [@jimryan](https://github.com/jimryan))
10
+
1
11
  ## 0.1.1 (March 6, 2012) ##
2
12
 
3
13
  ### bug fix
data/README.md CHANGED
@@ -79,6 +79,12 @@ get attribute value:
79
79
  @user.sex_text # or @user.sex.text
80
80
  ```
81
81
 
82
+ get all values for enumerized attribute:
83
+
84
+ ```ruby
85
+ User.sex.values # or User.enumerized_attributes[:sex].values
86
+ ```
87
+
82
88
  use it with forms:
83
89
 
84
90
  ```erb
@@ -127,6 +133,23 @@ user.sex.male? #=> true
127
133
  user.sex.female? #=> false
128
134
  ```
129
135
 
136
+ To make some attributes shared across different classes it's possible to define them in a separate module and then include it into classes:
137
+
138
+ ```ruby
139
+ module PersonEnumerations
140
+ include Enumerize
141
+
142
+ enumerize :sex, :in => %w[male female]
143
+ end
144
+
145
+ class Person
146
+ include PersonEnumerations
147
+ end
148
+
149
+ class User
150
+ include PersonEnumerations
151
+ end
152
+ ```
130
153
 
131
154
 
132
155
  ## Contributing
data/lib/enumerize.rb CHANGED
@@ -3,18 +3,24 @@ require 'enumerize/version'
3
3
 
4
4
  module Enumerize
5
5
  autoload :Attribute, 'enumerize/attribute'
6
+ autoload :AttributeMap, 'enumerize/attribute_map'
6
7
  autoload :Value, 'enumerize/value'
7
8
  autoload :Base, 'enumerize/base'
8
9
  autoload :ActiveRecord, 'enumerize/activerecord'
10
+ autoload :ModuleAttributes, 'enumerize/module_attributes'
9
11
 
10
- extend ActiveSupport::Concern
11
-
12
- include Enumerize::Base
12
+ def self.included(base)
13
+ base.send :include, Enumerize::Base
14
+ if defined?(::ActiveRecord::Base) && base < ::ActiveRecord::Base
15
+ base.extend Enumerize::ActiveRecord
16
+ end
13
17
 
14
- included do
15
- if defined?(::ActiveRecord::Base) && self < ::ActiveRecord::Base
16
- include Enumerize::ActiveRecord
18
+ if Module === base
19
+ base.extend Enumerize::Base::ClassMethods
20
+ base.extend Enumerize::ModuleAttributes
17
21
  end
22
+
23
+ super
18
24
  end
19
25
 
20
26
  begin
@@ -2,6 +2,6 @@ require 'active_support/concern'
2
2
 
3
3
  module Enumerize
4
4
  module ActiveRecord
5
- extend ActiveSupport::Concern
5
+
6
6
  end
7
7
  end
@@ -0,0 +1,36 @@
1
+ module Enumerize
2
+ class AttributeMap
3
+ def initialize
4
+ @attributes = {}
5
+ @dependants = []
6
+ end
7
+
8
+ def [](name)
9
+ @attributes[name.to_s]
10
+ end
11
+
12
+ def <<(attr)
13
+ @attributes[attr.name.to_s] = attr
14
+ @dependants.each do |dependant|
15
+ dependant << attr
16
+ end
17
+ end
18
+
19
+ def each
20
+ @attributes.each_pair do |_name, attr|
21
+ yield attr
22
+ end
23
+ end
24
+
25
+ def empty?
26
+ @attributes.empty?
27
+ end
28
+
29
+ def add_dependant(dependant)
30
+ @dependants << dependant
31
+ each do |attr|
32
+ dependant << attr
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,48 +4,59 @@ module Enumerize
4
4
  module Base
5
5
  extend ActiveSupport::Concern
6
6
 
7
- included do
8
- @enumerized_attributes = {}
9
- class << self
10
- attr_reader :enumerized_attributes
11
- end
12
- end
13
-
14
7
  module ClassMethods
15
- def enumerize(*args, &block)
16
- attr = Attribute.new(self, *args, &block)
17
- enumerized_attributes[attr.name] = attr
8
+ def enumerize(name, options={})
9
+ attr = Attribute.new(self, name, options)
10
+ enumerized_attributes << attr
18
11
 
19
12
  unless methods.include?(attr.name)
20
- singleton_class.class_eval do
21
- define_method(attr.name) { attr }
22
- end
13
+ _enumerize_module._class_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ def #{attr.name}
15
+ enumerized_attributes[:#{attr.name}]
16
+ end
17
+ RUBY
23
18
  end
24
19
 
25
- mod = Module.new
26
-
27
- mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
28
- def initialize(*, &_)
29
- super
30
- self.#{attr.name} = self.class.enumerized_attributes[:#{attr.name}].default_value if #{attr.name}.nil?
31
- end
32
- RUBY
20
+ _define_enumerize_attribute(attr)
33
21
 
34
- _define_enumerize_attribute(mod, attr)
35
-
36
- mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
22
+ _enumerize_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
37
23
  def #{attr.name}_text
38
24
  #{attr.name} && #{attr.name}.text
39
25
  end
40
26
  RUBY
41
27
 
42
- include mod
28
+ if respond_to?(:validates)
29
+ validates name, :inclusion => {:in => enumerized_attributes[name].values.map(&:to_s), :allow_nil => true, :allow_blank => true}
30
+ end
31
+ end
32
+
33
+ def enumerized_attributes
34
+ @enumerized_attributes ||= AttributeMap.new
35
+ end
36
+
37
+ def inherited(subclass)
38
+ enumerized_attributes.add_dependant subclass.enumerized_attributes
39
+ super
43
40
  end
44
41
 
45
42
  private
46
43
 
47
- def _define_enumerize_attribute(mod, attr)
48
- mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
44
+ def _enumerize_module
45
+ @_enumerize_module ||= begin
46
+ mod = Module.new do
47
+ @_class_methods = Module.new
48
+ class << self
49
+ attr_reader :_class_methods
50
+ end
51
+ end
52
+ include mod
53
+ extend mod._class_methods
54
+ mod
55
+ end
56
+ end
57
+
58
+ def _define_enumerize_attribute(attr)
59
+ _enumerize_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
49
60
  def #{attr.name}
50
61
  if respond_to?(:read_attribute, true)
51
62
  self.class.enumerized_attributes[:#{attr.name}].find_value(read_attribute(:#{attr.name}))
@@ -59,14 +70,40 @@ module Enumerize
59
70
  end
60
71
 
61
72
  def #{attr.name}=(new_value)
73
+ _enumerized_values_for_validation[:#{attr.name}] = new_value.nil? ? nil : new_value.to_s
74
+
75
+ allowed_value_or_nil = self.class.enumerized_attributes[:#{attr.name}].find_value(new_value)
76
+ allowed_value_or_nil = allowed_value_or_nil.to_s unless allowed_value_or_nil.nil?
77
+
62
78
  if respond_to?(:write_attribute, true)
63
- write_attribute :#{attr.name}, self.class.enumerized_attributes[:#{attr.name}].find_value(new_value).to_s
79
+ write_attribute :#{attr.name}, allowed_value_or_nil
64
80
  else
65
- @#{attr.name} = self.class.enumerized_attributes[:#{attr.name}].find_value(new_value).to_s
81
+ @#{attr.name} = allowed_value_or_nil
66
82
  end
67
83
  end
68
84
  RUBY
69
85
  end
70
86
  end
87
+
88
+ def initialize(*)
89
+ super
90
+ self.class.enumerized_attributes.each do |attr|
91
+ public_send("#{attr.name}=", attr.default_value) if public_send(attr.name).nil?
92
+ end
93
+ end
94
+
95
+ def read_attribute_for_validation(key)
96
+ if self.class.enumerized_attributes[key].instance_of?(Enumerize::Attribute) && _enumerized_values_for_validation.has_key?(key)
97
+ _enumerized_values_for_validation[key]
98
+ else
99
+ super
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def _enumerized_values_for_validation
106
+ @_enumerized_values_for_validation ||= {}
107
+ end
71
108
  end
72
109
  end
@@ -0,0 +1,11 @@
1
+ module Enumerize
2
+ module ModuleAttributes
3
+ def included(base)
4
+ base.send :include, Enumerize
5
+ base.send :include, _enumerize_module
6
+ base.extend _enumerize_module._class_methods
7
+ enumerized_attributes.add_dependant base.enumerized_attributes
8
+ super
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Enumerize
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -53,4 +53,27 @@ describe Enumerize::ActiveRecord do
53
53
  User.new.role.must_equal 'user'
54
54
  User.new.attributes['role'].must_equal 'user'
55
55
  end
56
+
57
+ it 'validates inclusion' do
58
+ user = User.new
59
+ user.role = 'wrong'
60
+ user.wont_be :valid?
61
+ end
62
+
63
+ it "uses persisted value for validation if it hasn't been set" do
64
+ user = User.create! :sex => :male
65
+ User.find(user).read_attribute_for_validation(:sex).must_equal 'male'
66
+ end
67
+
68
+ it 'is valid with empty string assigned' do
69
+ user = User.new
70
+ user.role = ''
71
+ user.must_be :valid?
72
+ end
73
+
74
+ it 'stores nil when empty string assigned' do
75
+ user = User.new
76
+ user.role = ''
77
+ user.read_attribute(:role).must_equal nil
78
+ end
56
79
  end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ module Enumerize
4
+ class AttributeMapTest < MiniTest::Spec
5
+ subject { AttributeMap.new }
6
+
7
+ def make_attr(name)
8
+ Attribute.new(nil, name, :in => %[a b])
9
+ end
10
+
11
+ it 'empty when no attrs' do
12
+ subject.must_be_empty
13
+ end
14
+
15
+ it 'not empty when attr added' do
16
+ subject << make_attr(:a)
17
+ subject.wont_be_empty
18
+ end
19
+
20
+ it 'iterates over added attrs' do
21
+ attr_1 = make_attr(:a)
22
+ attr_2 = make_attr(:b)
23
+
24
+ subject << attr_1
25
+ subject << attr_2
26
+
27
+ count = 0
28
+ actual = []
29
+
30
+ subject.each do |element|
31
+ count += 1
32
+ actual << element
33
+ end
34
+
35
+ count.must_equal 2
36
+ actual.must_equal [attr_1, attr_2]
37
+ end
38
+
39
+ it 'reads attribute by name' do
40
+ attr = make_attr(:a)
41
+ subject << attr
42
+ subject[:a].must_equal attr
43
+ end
44
+
45
+ it 'reads attribute by name using string' do
46
+ attr = make_attr(:a)
47
+ subject << attr
48
+ subject['a'].must_equal attr
49
+ end
50
+
51
+ it 'updates dependants' do
52
+ attr = make_attr(:a)
53
+ dependant = mock
54
+ dependant.expects(:<<).with(attr)
55
+ subject.add_dependant dependant
56
+ subject << attr
57
+ end
58
+
59
+ it 'adds attrs to dependant' do
60
+ attr = make_attr(:a)
61
+ subject << attr
62
+ dependant = AttributeMap.new
63
+ subject.add_dependant dependant
64
+ dependant[:a].must_equal attr
65
+ end
66
+ end
67
+ end
data/test/base_test.rb CHANGED
@@ -7,6 +7,10 @@ describe Enumerize::Base do
7
7
  end
8
8
  end
9
9
 
10
+ let(:subklass) do
11
+ Class.new(klass)
12
+ end
13
+
10
14
  let(:object) { klass.new }
11
15
 
12
16
  it 'returns nil when not set' do
@@ -81,7 +85,7 @@ describe Enumerize::Base do
81
85
  end
82
86
 
83
87
  it 'has enumerized attributes' do
84
- klass.enumerized_attributes.must_equal({})
88
+ klass.enumerized_attributes.must_be_empty
85
89
  klass.enumerize(:foo, :in => %w[a b])
86
90
  klass.enumerized_attributes[:foo].must_be_instance_of Enumerize::Attribute
87
91
  end
@@ -91,4 +95,45 @@ describe Enumerize::Base do
91
95
  klass.enumerize(:name, :in => %w[a b], :default => 'a')
92
96
  klass.method(:name).must_equal method
93
97
  end
98
+
99
+ it "inherits enumerized attributes from a parent class" do
100
+ klass.enumerize(:foo, :in => %w[a b])
101
+ subklass.enumerized_attributes[:foo].must_be_instance_of Enumerize::Attribute
102
+ end
103
+
104
+ it "inherits enumerized attributes from a grandparent class" do
105
+ klass.enumerize(:foo, :in => %w[a b])
106
+ Class.new(subklass).enumerized_attributes[:foo].must_be_instance_of Enumerize::Attribute
107
+ end
108
+
109
+ it "doesn't add enumerized attributes to parent class" do
110
+ klass.enumerize(:foo, :in => %w[a b])
111
+ subklass.enumerize(:bar, :in => %w[c d])
112
+
113
+ klass.enumerized_attributes[:bar].must_equal nil
114
+ end
115
+
116
+ it 'adds new parent class attributes to subclass' do
117
+ subklass = Class.new(klass)
118
+ klass.enumerize :foo, :in => %w[a b]
119
+ subklass.enumerized_attributes[:foo].must_be_instance_of Enumerize::Attribute
120
+ end
121
+
122
+ it 'stores nil value' do
123
+ klass.enumerize(:foo, :in => [:a, :b])
124
+ object.foo = nil
125
+ object.instance_variable_get(:@foo).must_equal nil
126
+ end
127
+
128
+ it 'casts value to string for validation' do
129
+ klass.enumerize(:foo, :in => [:a, :b])
130
+ object.foo = :c
131
+ object.read_attribute_for_validation(:foo).must_equal 'c'
132
+ end
133
+
134
+ it "doesn't cast nil to string for validation" do
135
+ klass.enumerize(:foo, :in => [:a, :b])
136
+ object.foo = nil
137
+ object.read_attribute_for_validation(:foo).must_equal nil
138
+ end
94
139
  end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class ModuleAttributesSpec < MiniTest::Spec
4
+ it 'inherits attribute from the module' do
5
+ mod = Module.new do
6
+ include Enumerize
7
+ enumerize :sex, :in => %w[male female], :default => 'male'
8
+ end
9
+
10
+ klass = Class.new
11
+ klass.send :include, mod
12
+ klass.enumerized_attributes[:sex].must_be_instance_of Enumerize::Attribute
13
+ klass.new.sex.must_equal 'male'
14
+ klass.sex.must_be_instance_of Enumerize::Attribute
15
+ end
16
+
17
+ it 'uses new attributes from the module' do
18
+ mod = Module.new do
19
+ include Enumerize
20
+ end
21
+
22
+ klass = Class.new
23
+ klass.send :include, mod
24
+ mod.enumerize :sex, :in => %w[male female], :default => 'male'
25
+ klass.enumerized_attributes[:sex].must_be_instance_of Enumerize::Attribute
26
+ klass.new.sex.must_equal 'male'
27
+ klass.sex.must_be_instance_of Enumerize::Attribute
28
+ end
29
+ end
data/test/mongoid_test.rb CHANGED
@@ -53,4 +53,18 @@ describe Enumerize do
53
53
  it 'has default value' do
54
54
  model.new.role.must_equal 'user'
55
55
  end
56
+
57
+ it 'validates inclusion' do
58
+ user = model.new
59
+ user.role = 'wrong'
60
+ user.wont_be :valid?
61
+ end
62
+
63
+ it 'assigns value on loaded record' do
64
+ model.delete_all
65
+ model.create!(:sex => :male)
66
+ user = model.first
67
+ user.sex = :female
68
+ user.sex.must_equal 'female'
69
+ end
56
70
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enumerize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-06 00:00:00.000000000 Z
12
+ date: 2012-03-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &14589640 !ruby/object:Gem::Requirement
16
+ requirement: &6744160 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 3.1.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *14589640
24
+ version_requirements: *6744160
25
25
  description: Enumerated attributes with I18n and ActiveRecord/Mongoid support
26
26
  email: info@twinslash.com
27
27
  executables: []
@@ -40,15 +40,19 @@ files:
40
40
  - lib/enumerize.rb
41
41
  - lib/enumerize/activerecord.rb
42
42
  - lib/enumerize/attribute.rb
43
+ - lib/enumerize/attribute_map.rb
43
44
  - lib/enumerize/base.rb
44
45
  - lib/enumerize/hooks/formtastic.rb
45
46
  - lib/enumerize/hooks/simple_form.rb
47
+ - lib/enumerize/module_attributes.rb
46
48
  - lib/enumerize/value.rb
47
49
  - lib/enumerize/version.rb
48
50
  - test/activerecord_test.rb
51
+ - test/attribute_map_test.rb
49
52
  - test/attribute_test.rb
50
53
  - test/base_test.rb
51
54
  - test/formtastic_test.rb
55
+ - test/module_attributes_test.rb
52
56
  - test/mongoid_test.rb
53
57
  - test/simple_form_test.rb
54
58
  - test/support/mock_controller.rb
@@ -69,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
73
  version: '0'
70
74
  segments:
71
75
  - 0
72
- hash: 25298246185228879
76
+ hash: -1144009341206974926
73
77
  required_rubygems_version: !ruby/object:Gem::Requirement
74
78
  none: false
75
79
  requirements:
@@ -78,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
82
  version: '0'
79
83
  segments:
80
84
  - 0
81
- hash: 25298246185228879
85
+ hash: -1144009341206974926
82
86
  requirements: []
83
87
  rubyforge_project:
84
88
  rubygems_version: 1.8.11
@@ -87,9 +91,11 @@ specification_version: 3
87
91
  summary: Enumerated attributes with I18n and ActiveRecord/Mongoid support
88
92
  test_files:
89
93
  - test/activerecord_test.rb
94
+ - test/attribute_map_test.rb
90
95
  - test/attribute_test.rb
91
96
  - test/base_test.rb
92
97
  - test/formtastic_test.rb
98
+ - test/module_attributes_test.rb
93
99
  - test/mongoid_test.rb
94
100
  - test/simple_form_test.rb
95
101
  - test/support/mock_controller.rb