classy_enum 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # ClassyEnum Changelog
2
2
 
3
+ ## 3.1.0
4
+
5
+ * ClassyEnum::Base now extends Enumerable to provide enum collection
6
+ methods. All objects in the collection are instances of the enum
7
+ members. .find is overridden to provide custom find functionality.
8
+ * ClassyEnum::Base.find has been reintroduced, with aliases of .detect
9
+ and [].
10
+ * Introducing I18n support and providing a ClassyEnum::Base#text method
11
+ that will automatically translate text values.
12
+ * Translation support was added to ClassyEnum::Base.select_options.
13
+ * Equality can now be determined using strings and symbols. The
14
+ following will return true:
15
+
16
+ Priority::Low.new == :low # => true
17
+ Priority::Low.new == 'low' # => true
18
+
3
19
  ## 3.0.0
4
20
 
5
21
  * Removing ClassyEnum::Base.enum_classes in favor of using enum
data/README.md CHANGED
@@ -4,6 +4,18 @@
4
4
 
5
5
  ClassyEnum is a Ruby on Rails gem that adds class-based enumerator functionality to ActiveRecord attributes.
6
6
 
7
+ ## README Topics
8
+
9
+ * [Example Usage](https://github.com/beerlington/classy_enum#example-usage)
10
+ * [Internationalization](https://github.com/beerlington/classy_enum#internationalization)
11
+ * [Using Enum as a Collection](https://github.com/beerlington/classy_enum#using-enum-as-a-collection)
12
+ * [Reference to Owning Object](https://github.com/beerlington/classy_enum#back-reference-to-owning-object)
13
+ * [Serializing as JSON](https://github.com/beerlington/classy_enum#serializing-as-json)
14
+ * [Special Cases](https://github.com/beerlington/classy_enum#special-cases)
15
+ * [Built-in Model Validation](https://github.com/beerlington/classy_enum#model-validation)
16
+ * [Using Enums Outside of ActiveRecord](https://github.com/beerlington/classy_enum#working-with-classyenum-outside-of-activerecord)
17
+ * [Formtastic Support](https://github.com/beerlington/classy_enum#formtastic-support)
18
+
7
19
  ## Rails & Ruby Versions Supported
8
20
 
9
21
  *Rails:* 3.0.x - 3.2.x
@@ -17,8 +29,6 @@ Note: This branch is no longer maintained and will not get bug fixes or new feat
17
29
 
18
30
  The gem is hosted at [rubygems.org](https://rubygems.org/gems/classy_enum)
19
31
 
20
- You will also need to add `app/enums` as an autoloadable path. This configuration will depend on which version of rails you are using.
21
-
22
32
  ## Upgrading?
23
33
 
24
34
  See the [wiki](https://github.com/beerlington/classy_enum/wiki/Upgrading) for notes about upgrading from previous versions.
@@ -59,7 +69,7 @@ The generator creates a default setup, but each enum member can be changed to fi
59
69
 
60
70
  I have defined three priority levels: low, medium, and high. Each priority level can have different properties and methods associated with it.
61
71
 
62
- I would like to add a method called `send_email?` that all member subclasses respond to. By default this method will return false, but will be overridden for high priority alarms to return true.
72
+ I would like to add a method called `#send_email?` that all member subclasses respond to. By default this method will return false, but will be overridden for high priority alarms to return true.
63
73
 
64
74
  ```ruby
65
75
  class Priority < ClassyEnum::Base
@@ -95,7 +105,7 @@ end
95
105
  Note: Alternatively, you may use an enum type if your database supports it. See
96
106
  [this issue](https://github.com/beerlington/classy_enum/issues/12) for more information.
97
107
 
98
- Then in my model I've added a line that calls `classy_enum_attr` with a single argument representing the enum I want to associate with my model. I am also delegating the send_email? method to my Priority enum class.
108
+ Then in my model I've added a line that calls `classy_enum_attr` with a single argument representing the enum I want to associate with my model. I am also delegating the `#send_email?` method to my Priority enum class.
99
109
 
100
110
  ```ruby
101
111
  class Alarm < ActiveRecord::Base
@@ -121,7 +131,62 @@ With this setup, I can now do the following:
121
131
  @alarm.send_email? # => true
122
132
  ```
123
133
 
124
- The enum field works like any other model attribute. It can be mass-assigned using `update_attribute(s)`.
134
+ The enum field works like any other model attribute. It can be mass-assigned using `#update_attributes`.
135
+
136
+ ## Internationalization
137
+
138
+ ClassyEnum provides built-in support for translations using Ruby's I18n
139
+ library. The translated values are provided via a `#text` method on each
140
+ enum object. Translations are automatically applied when a key is found
141
+ at `locale.classy_enum.enum_parent_class.enum_value`, or a default value
142
+ is used that is equivalent to `#to_s.titleize`.
143
+
144
+ Given the following file *config/locales/es.yml*
145
+
146
+ ```yml
147
+ es:
148
+ classy_enum:
149
+ priority:
150
+ low: 'Bajo'
151
+ medium: 'Medio'
152
+ high: 'Alto'
153
+ ```
154
+
155
+ You can now do the following:
156
+
157
+ ```ruby
158
+ @alarm.priority = :low
159
+ @alarm.priority.text # => 'Low'
160
+
161
+ I18n.locale = :es
162
+
163
+ @alarm.priority.text # => 'Bajo'
164
+ ```
165
+
166
+ ## Using Enum as a Collection
167
+
168
+ ClassyEnum::Base extends the [Enumerable module](http://ruby-doc.org/core-1.9.3/Enumerable.html)
169
+ which provides several traversal and searching methods. This can
170
+ be useful for situations where you are working with the collection,
171
+ as opposed to the attributes on an ActiveRecord object.
172
+
173
+ ```ruby
174
+ # Find the priority based on string or symbol:
175
+ Priority.find(:low) # => Priority::Low.new
176
+ Priority.find('medium') # => Priority::Medium.new
177
+
178
+ # Find the lowest priority that can send email:
179
+ Priority.find(&:send_email?) # => Priority::High.new
180
+
181
+ # Find the priorities that are lower than Priority::High
182
+ high_priority = Priority::High.new
183
+ Priority.select {|p| p < high_priority } # => [Priority::Low.new, Priority::Medium.new]
184
+
185
+ # Iterate over each priority:
186
+ Priority.each do |priority|
187
+ puts priority.send_email?
188
+ end
189
+ ```
125
190
 
126
191
  ## Back reference to owning object
127
192
 
@@ -129,11 +194,11 @@ In some cases you may want an enum class to reference the owning object
129
194
  (an instance of the active record model). Think of it as a `belongs_to`
130
195
  relationship, where the enum belongs to the model.
131
196
 
132
- By default, the back reference can be called using `owner`.
197
+ By default, the back reference can be called using `#owner`.
133
198
  If you want to refer to the owner by a different name, you must explicitly declare
134
- the owner name in the classy_enum parent class using the `owner` class method.
199
+ the owner name in the classy_enum parent class using the `.owner` class method.
135
200
 
136
- Example using the default `owner` method:
201
+ Example using the default `#owner` method:
137
202
 
138
203
  ```ruby
139
204
  class Priority < ClassyEnum::Base
@@ -210,7 +275,7 @@ end
210
275
 
211
276
  ## Model Validation
212
277
 
213
- An ActiveRecord validator `validates_inclusion_of :field, :in => ENUM.all` is automatically added to your model when you use `classy_enum_attr`.
278
+ An ActiveRecord validator `validates_inclusion_of :field, :in => ENUM` is automatically added to your model when you use `classy_enum_attr`.
214
279
 
215
280
  If your enum only has members low, medium, and high, then the following validation behavior would be expected:
216
281
 
@@ -240,8 +305,8 @@ Instantiate an enum member subclass *Priority::Low*
240
305
 
241
306
  ```ruby
242
307
  # These statements are all equivalent
243
- low = Priority.build(:low)
244
- low = Priority.build('low')
308
+ low = Priority.find(:low)
309
+ low = Priority.find('low')
245
310
  low = Priority::Low.new
246
311
  ```
247
312
 
@@ -27,12 +27,9 @@ module ClassyEnum
27
27
  allow_nil = options[:allow_nil] || false
28
28
  serialize_as_json = options[:serialize_as_json] || false
29
29
 
30
- error_message = "must be #{enum.all.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ')}"
31
-
32
30
  # Add ActiveRecord validation to ensure it won't be saved unless it's an option
33
31
  validates_inclusion_of attribute,
34
- :in => enum.all,
35
- :message => error_message,
32
+ :in => enum,
36
33
  :allow_blank => allow_blank,
37
34
  :allow_nil => allow_nil
38
35
 
@@ -5,6 +5,7 @@ module ClassyEnum
5
5
  include Comparable
6
6
  include Conversion
7
7
  include Predicate
8
+ include Translation
8
9
  include Collection
9
10
 
10
11
  class_attribute :base_class
@@ -56,12 +57,10 @@ module ClassyEnum
56
57
  # Priority.build(:low) # => Priority::Low.new
57
58
  # Priority.build(:invalid_option) # => :invalid_option
58
59
  def build(value, options={})
59
- return value if value.blank? && options[:allow_blank]
60
+ object = find(value)
60
61
 
61
- # Return the value if it is not a valid member
62
- return value unless all.map(&:to_s).include? value.to_s
62
+ return value if object.nil? || (options[:allow_blank] && object.nil?)
63
63
 
64
- object = "#{base_class}::#{value.to_s.camelize}".constantize.new
65
64
  object.owner = options[:owner]
66
65
  object.serialize_as_json = options[:serialize_as_json]
67
66
  object.allow_blank = options[:allow_blank]
@@ -19,6 +19,10 @@ module ClassyEnum
19
19
  # priorities.max # => @high
20
20
  # priorities.min # => @low
21
21
  def <=> other
22
+ if other.is_a?(Symbol) || other.is_a?(String)
23
+ other = self.class.find(other)
24
+ end
25
+
22
26
  index <=> other.index
23
27
  end
24
28
 
@@ -27,6 +31,9 @@ module ClassyEnum
27
31
  end
28
32
 
29
33
  module ClassMethods
34
+ include Enumerable
35
+ alias all to_a
36
+
30
37
  def inherited(klass)
31
38
  if self == ClassyEnum::Base
32
39
  klass.class_attribute :enum_options
@@ -39,7 +46,7 @@ module ClassyEnum
39
46
  super
40
47
  end
41
48
 
42
- # Returns an array of all instantiated enums
49
+ # Iterates over instances of each enum in the collection
43
50
  #
44
51
  # ==== Example
45
52
  # # Create an Enum with some elements
@@ -50,11 +57,42 @@ module ClassyEnum
50
57
  # class Priority::Medium < Priority; end
51
58
  # class Priority::High < Priority; end
52
59
  #
53
- # Priority.all # => [Priority::Low.new, Priority::Medium.new, Priority::High.new]
54
- def all
55
- enum_options.map(&:new)
60
+ # Priority.each do |priority|
61
+ # puts priority # => 'Low', 'Medium', 'High'
62
+ # end
63
+ def each
64
+ enum_options.each {|e| yield e.new }
56
65
  end
57
66
 
67
+ # Finds an enum instance by symbol, string, or block.
68
+ #
69
+ # If a block is given, it passes each entry in enum to block, and returns
70
+ # the first enum for which block is not false. If no enum matches, it
71
+ # returns nil.
72
+ #
73
+ # ==== Example
74
+ # # Create an Enum with some elements
75
+ # class Priority < ClassyEnum::Base
76
+ # end
77
+ #
78
+ # class Priority::Low < Priority; end
79
+ # class Priority::Medium < Priority; end
80
+ # class Priority::High < Priority; end
81
+ #
82
+ # Priority.find(:high) # => Priority::High.new
83
+ # Priority.find('high') # => Priority::High.new
84
+ # Priority.find {|e| e.to_sym == :high } # => Priority::High.new
85
+ def find(key=nil)
86
+ if block_given?
87
+ super
88
+ elsif map(&:to_s).include? key.to_s
89
+ super { |e| e.to_s == key.to_s }
90
+ end
91
+ end
92
+
93
+ alias detect find
94
+ alias [] find
95
+
58
96
  # Returns a 2D array for Rails select helper options.
59
97
  # Also used internally for Formtastic support
60
98
  #
@@ -68,7 +106,7 @@ module ClassyEnum
68
106
  #
69
107
  # Priority.select_options # => [["Low", "low"], ["Really High", "really_high"]]
70
108
  def select_options
71
- all.map {|e| [e.to_s.titleize, e.to_s] }
109
+ map {|e| [e.text, e.to_s] }
72
110
  end
73
111
  end
74
112
 
@@ -4,7 +4,7 @@ module ClassyEnum
4
4
  # Define attribute methods like two?
5
5
  def self.define_predicate_method(klass, enum)
6
6
  klass.base_class.class_eval do
7
- define_method "#{enum}?", lambda { attribute?(enum.to_s) }
7
+ define_method "#{enum}?", lambda { attribute?(enum) }
8
8
  end
9
9
  end
10
10
 
@@ -29,7 +29,7 @@ module ClassyEnum
29
29
  # @dog.breed.snoop? # => true
30
30
  # @dog.breed.golden_retriever? # => false
31
31
  def attribute?(attribute)
32
- to_s == attribute
32
+ self == attribute
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,38 @@
1
+ require 'i18n'
2
+
3
+ module ClassyEnum
4
+ module Translation
5
+
6
+ # Returns a translated string of the enum type. Used internally to create
7
+ # the select_options array.
8
+ #
9
+ # Translation location is:
10
+ # locale.classy_enum.base_class.enum_string
11
+ #
12
+ # ==== Example
13
+ # # Create an Enum with some elements
14
+ # class Priority < ClassyEnum::Base
15
+ # end
16
+ #
17
+ # class Priority::Low < Priority; end
18
+ # class Priority::ReallyHigh < Priority; end
19
+ #
20
+ # # Default translations are `to_s.titlieze`
21
+ # Priority::Low.new.text # => 'Low'
22
+ # Priority::ReallyHigh.new.text # => 'Really High'
23
+ #
24
+ # # Assuming we have a translation defined for:
25
+ # # es.classy_enum.priority.low # => 'Bajo'
26
+ #
27
+ # Priority::Low.new.text # => 'Bajo'
28
+ def text
29
+ I18n.translate to_s, :scope => i18n_scope, :default => to_s.titleize
30
+ end
31
+
32
+ private
33
+
34
+ def i18n_scope
35
+ [:classy_enum, base_class.name.underscore]
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module ClassyEnum
2
- VERSION = "3.0.1"
2
+ VERSION = "3.1.0"
3
3
  end
data/lib/classy_enum.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'classy_enum/translation'
1
2
  require 'classy_enum/collection'
2
3
  require 'classy_enum/conversion'
3
4
  require 'classy_enum/predicate'
@@ -43,10 +43,12 @@ describe Dog do
43
43
  specify { Dog.new(:breed => '').should_not be_valid }
44
44
 
45
45
  context "with valid breed options" do
46
- subject { Dog.new(:breed => :golden_retriever) }
47
- it { should be_valid }
48
- its(:breed) { should be_a(Breed::GoldenRetriever) }
49
- its('breed.allow_blank') { should be_false }
46
+ [:golden_retriever, 'golden_retriever', Breed::GoldenRetriever.new].each do |option|
47
+ subject { Dog.new(:breed => option) }
48
+ it { should be_valid }
49
+ its(:breed) { should be_a(Breed::GoldenRetriever) }
50
+ its('breed.allow_blank') { should be_false }
51
+ end
50
52
  end
51
53
 
52
54
  context "with invalid breed options" do
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  class ClassyEnumCollection < ClassyEnum::Base
4
+ delegate :odd?, :to => :to_i
4
5
  end
5
6
 
6
7
  class ClassyEnumCollection::One < ClassyEnumCollection
@@ -13,11 +14,57 @@ class ClassyEnumCollection::Three < ClassyEnumCollection
13
14
  end
14
15
 
15
16
  describe ClassyEnum::Collection do
16
- subject { ClassyEnumCollection }
17
+ subject(:enum) { ClassyEnumCollection }
17
18
 
18
19
  its(:enum_options) { should == [ClassyEnumCollection::One, ClassyEnumCollection::Two, ClassyEnumCollection::Three] }
19
20
  its(:all) { should == [ClassyEnumCollection::One.new, ClassyEnumCollection::Two.new, ClassyEnumCollection::Three.new] }
20
21
  its(:select_options) { should == [['One', 'one'],['Two', 'two'], ['Three', 'three']] }
22
+
23
+ context '.map' do
24
+ it 'should behave like an enumerable' do
25
+ enum.map(&:to_s).should == %w(one two three)
26
+ end
27
+ end
28
+
29
+ context '#<=> (equality)' do
30
+ its(:first) { should == ClassyEnumCollection::One.new }
31
+ its(:first) { should == :one }
32
+ its(:first) { should == 'one' }
33
+ its(:first) { should_not == :two }
34
+ its(:first) { should_not == :not_found }
35
+
36
+ its(:max) { should == :three }
37
+ end
38
+
39
+ context '.find, .detect, []' do
40
+ let(:expected_enum) { ClassyEnumCollection::Two.new }
41
+
42
+ [:find, :detect, :[]].each do |method|
43
+ it 'should return an instance when searching by symbol' do
44
+ enum.send(method, :two).should == expected_enum
45
+ end
46
+
47
+ it 'should return an instance when searching by string' do
48
+ enum.send(method, 'two').should == expected_enum
49
+ end
50
+
51
+ it 'should behave like an enumerable when using a block' do
52
+ enum.send(method) {|e| e.to_s == 'two'}.should == expected_enum
53
+ end
54
+
55
+ it 'should return nil if item cannot be found' do
56
+ enum.send(method, :not_found).should be_nil
57
+ end
58
+ end
59
+ end
60
+
61
+ context '.select' do
62
+ let(:expected_enum) { ClassyEnumCollection::Two.new }
63
+
64
+ it 'returns an array with each item where the block returns true' do
65
+ enum.select(&:odd?).should == [ClassyEnumCollection::One.new, ClassyEnumCollection::Three.new]
66
+ end
67
+ end
21
68
  end
22
69
 
23
70
  describe ClassyEnum::Collection, Comparable do
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class ClassyEnumTranslation < ClassyEnum::Base
4
+ end
5
+
6
+ class ClassyEnumTranslation::One < ClassyEnumTranslation
7
+ end
8
+
9
+ class ClassyEnumTranslation::Two < ClassyEnumTranslation
10
+ end
11
+
12
+ describe ClassyEnum::Translation do
13
+
14
+ before do
15
+ I18n.reload!
16
+ I18n.backend.store_translations :en, :classy_enum => {:classy_enum_translation => {:one => 'One!', :two => 'Two!' } }
17
+ I18n.backend.store_translations :es, :classy_enum => {:classy_enum_translation => {:one => 'Uno', :two => 'Dos' } }
18
+ end
19
+
20
+ context '#text' do
21
+ subject { ClassyEnumTranslation::One.new }
22
+
23
+ context 'default' do
24
+ before { I18n.reload! }
25
+ its(:text) { should == 'One' }
26
+ end
27
+
28
+ context 'en' do
29
+ before { I18n.locale = :en }
30
+ its(:text) { should == 'One!' }
31
+ end
32
+
33
+ context 'es' do
34
+ before { I18n.locale = :es }
35
+ its(:text) { should == 'Uno' }
36
+ end
37
+ end
38
+
39
+ context '.select_options' do
40
+ subject { ClassyEnumTranslation }
41
+
42
+ context 'default' do
43
+ before { I18n.reload! }
44
+ its(:select_options) { should == [["One", "one"], ["Two", "two"]] }
45
+ end
46
+
47
+ context 'en' do
48
+ before { I18n.locale = :en }
49
+ its(:select_options) { should == [["One!", "one"], ["Two!", "two"]] }
50
+ end
51
+
52
+ context 'es' do
53
+ before { I18n.locale = :es }
54
+ its(:select_options) { should == [["Uno", "one"], ["Dos", "two"]] }
55
+ end
56
+ end
57
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: classy_enum
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-03 00:00:00.000000000 Z
12
+ date: 2012-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -100,6 +100,7 @@ files:
100
100
  - lib/classy_enum/collection.rb
101
101
  - lib/classy_enum/conversion.rb
102
102
  - lib/classy_enum/predicate.rb
103
+ - lib/classy_enum/translation.rb
103
104
  - lib/classy_enum/version.rb
104
105
  - lib/generators/classy_enum/classy_enum_generator.rb
105
106
  - lib/generators/classy_enum/templates/enum.rb
@@ -108,6 +109,7 @@ files:
108
109
  - spec/classy_enum/collection_spec.rb
109
110
  - spec/classy_enum/conversion_spec.rb
110
111
  - spec/classy_enum/predicate_spec.rb
112
+ - spec/classy_enum/translation_spec.rb
111
113
  - spec/classy_enum_inheritance_spec.rb
112
114
  - spec/spec_helper.rb
113
115
  homepage: http://github.com/beerlington/classy_enum
@@ -124,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
126
  version: '0'
125
127
  segments:
126
128
  - 0
127
- hash: 4077796099842311388
129
+ hash: 4485240533001760978
128
130
  required_rubygems_version: !ruby/object:Gem::Requirement
129
131
  none: false
130
132
  requirements:
@@ -133,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
135
  version: '0'
134
136
  segments:
135
137
  - 0
136
- hash: 4077796099842311388
138
+ hash: 4485240533001760978
137
139
  requirements: []
138
140
  rubyforge_project:
139
141
  rubygems_version: 1.8.24
@@ -146,5 +148,6 @@ test_files:
146
148
  - spec/classy_enum/collection_spec.rb
147
149
  - spec/classy_enum/conversion_spec.rb
148
150
  - spec/classy_enum/predicate_spec.rb
151
+ - spec/classy_enum/translation_spec.rb
149
152
  - spec/classy_enum_inheritance_spec.rb
150
153
  - spec/spec_helper.rb