enumerize 0.1.1 → 0.2.0

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