power_enum 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +19 -6
- data/lib/power_enum/enumerated.rb +41 -26
- data/lib/power_enum/has_enumerated.rb +89 -59
- metadata +2 -5
data/README.markdown
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
https://github.com/albertosaurus/power_enum
|
4
4
|
|
5
|
+
[![Build Status](https://travis-ci.org/albertosaurus/power_enum.png)](https://travis-ci.org/albertosaurus/power_enum)
|
6
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/albertosaurus/power_enum)
|
7
|
+
|
5
8
|
Enumerations for Rails 3.1/3.2 Done Right.
|
6
9
|
|
7
10
|
NOTICE: Version 1.0 removes support from Rails 3.0, as that version of Rails has no longer been supported for
|
@@ -726,15 +729,25 @@ Of course `Booking.new.status.should === :received` still works, but is liable t
|
|
726
729
|
|
727
730
|
## How to run tests
|
728
731
|
|
732
|
+
### Prepare the test database
|
733
|
+
|
734
|
+
#### Automatically (preferred)
|
735
|
+
|
736
|
+
Execute the test setup script:
|
737
|
+
|
738
|
+
script/test_setup.sh
|
739
|
+
|
740
|
+
#### Manually (if required)
|
741
|
+
|
729
742
|
Go to the 'dummy' project:
|
730
743
|
|
731
744
|
cd ./spec/dummy
|
732
745
|
|
733
|
-
|
746
|
+
If this is your first time, create the test database
|
734
747
|
|
735
|
-
RAILS_ENV=test rake db:
|
748
|
+
RAILS_ENV=test bundle exec rake db:create
|
736
749
|
|
737
|
-
|
750
|
+
Run migrations for test environment:
|
738
751
|
|
739
752
|
RAILS_ENV=test bundle exec rake db:migrate
|
740
753
|
|
@@ -742,9 +755,9 @@ Go back to gem root directory:
|
|
742
755
|
|
743
756
|
cd ../../
|
744
757
|
|
745
|
-
|
758
|
+
### Run tests
|
746
759
|
|
747
|
-
rake spec
|
760
|
+
bundle exec rake spec
|
748
761
|
|
749
762
|
## Copyrights and License
|
750
763
|
|
@@ -760,5 +773,5 @@ Released under the MIT License. See the LICENSE file for more details.
|
|
760
773
|
Contributions are welcome. However, please make sure of the following before issuing a pull request:
|
761
774
|
|
762
775
|
* All specs are passing.
|
763
|
-
* Any new features have test coverage.
|
776
|
+
* Any new features have test coverage. Use the SimpleCov report to confirm.
|
764
777
|
* Anything that breaks backward compatibility has a very good reason for doing so.
|
@@ -77,41 +77,56 @@ module PowerEnum::Enumerated
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
name_column = if options.has_key?(:name_column) && !options[:name_column].blank? then
|
81
|
-
options[:name_column].to_s.to_sym
|
82
|
-
else
|
83
|
-
:name
|
84
|
-
end
|
85
|
-
|
86
|
-
alias_name = if options.has_key?(:alias_name) then
|
87
|
-
options[:alias_name]
|
88
|
-
else
|
89
|
-
true
|
90
|
-
end
|
91
|
-
|
92
80
|
class_attribute :acts_enumerated_name_column
|
93
|
-
self.acts_enumerated_name_column =
|
81
|
+
self.acts_enumerated_name_column = get_name_column(options)
|
94
82
|
|
95
83
|
unless self.is_a? PowerEnum::Enumerated::EnumClassMethods
|
96
|
-
|
84
|
+
extend_enum_class_methods( options )
|
85
|
+
end
|
86
|
+
end
|
97
87
|
|
98
|
-
|
99
|
-
|
88
|
+
# Injects the class methods into model
|
89
|
+
def extend_enum_class_methods(options) #:nodoc:
|
90
|
+
extend PowerEnum::Enumerated::EnumClassMethods
|
100
91
|
|
101
|
-
|
102
|
-
|
103
|
-
validates name_column, :presence => true, :uniqueness => true
|
92
|
+
class_eval do
|
93
|
+
include PowerEnum::Enumerated::EnumInstanceMethods
|
104
94
|
|
105
|
-
|
106
|
-
|
107
|
-
|
95
|
+
before_save :enumeration_model_update
|
96
|
+
before_destroy :enumeration_model_update
|
97
|
+
validates acts_enumerated_name_column, :presence => true, :uniqueness => true
|
108
98
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
99
|
+
define_method :__enum_name__ do
|
100
|
+
read_attribute(acts_enumerated_name_column).to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
if should_alias_name?(options) && acts_enumerated_name_column != :name
|
104
|
+
alias_method :name, :__enum_name__
|
105
|
+
end
|
106
|
+
end # class_eval
|
107
|
+
|
108
|
+
end
|
109
|
+
private :extend_enum_class_methods
|
110
|
+
|
111
|
+
# Determines if the name column should be explicitly aliased
|
112
|
+
def should_alias_name?(options) #:nodoc:
|
113
|
+
if options.has_key?(:alias_name) then
|
114
|
+
options[:alias_name]
|
115
|
+
else
|
116
|
+
true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
private :should_alias_name?
|
120
|
+
|
121
|
+
# Extracts the name column from options or gives the default
|
122
|
+
def get_name_column(options) #:nodoc:
|
123
|
+
if options.has_key?(:name_column) && !options[:name_column].blank? then
|
124
|
+
options[:name_column].to_s.to_sym
|
125
|
+
else
|
126
|
+
:name
|
113
127
|
end
|
114
128
|
end
|
129
|
+
private :get_name_column
|
115
130
|
end
|
116
131
|
|
117
132
|
module EnumClassMethods
|
@@ -19,7 +19,7 @@ module PowerEnum::HasEnumerated # :nodoc:
|
|
19
19
|
enumerated_attributes.include? attribute.to_s
|
20
20
|
end
|
21
21
|
|
22
|
-
# Defines an enumerated attribute with the given
|
22
|
+
# Defines an enumerated attribute with the given attribute_name on the model. Also accepts a hash of options as an
|
23
23
|
# optional second argument.
|
24
24
|
#
|
25
25
|
# === Supported options
|
@@ -37,7 +37,7 @@ module PowerEnum::HasEnumerated # :nodoc:
|
|
37
37
|
# subsequent lookups, but the model will fail validation.
|
38
38
|
# 2) You can specify an instance method to be called in the case of a lookup failure. The method signature is
|
39
39
|
# as follows:
|
40
|
-
# <tt>your_lookup_handler(operation,
|
40
|
+
# <tt>your_lookup_handler(operation, attribute_name, name_foreign_key, acts_enumerated_class_name, lookup_value)</tt>
|
41
41
|
# The 'operation' arg will be either :read or :write. In the case of :read you are expected to return
|
42
42
|
# something or raise an exception, while in the case of a :write you don't have to return anything. Note that
|
43
43
|
# there's enough information in the method signature that you can specify one method to handle all lookup
|
@@ -89,35 +89,62 @@ module PowerEnum::HasEnumerated # :nodoc:
|
|
89
89
|
reflection = PowerEnum::Reflection::EnumerationReflection.new(part_id, options, self)
|
90
90
|
self.reflections = self.reflections.merge(part_id => reflection)
|
91
91
|
|
92
|
-
|
93
|
-
class_name
|
94
|
-
foreign_key
|
95
|
-
failure_opt
|
96
|
-
|
97
|
-
create_scope
|
92
|
+
attribute_name = part_id.to_s
|
93
|
+
class_name = reflection.class_name
|
94
|
+
foreign_key = reflection.foreign_key
|
95
|
+
failure_opt = options[:on_lookup_failure]
|
96
|
+
allow_empty_name = options[:permit_empty_name]
|
97
|
+
create_scope = options[:create_scope]
|
98
98
|
|
99
99
|
failure_handler = get_lookup_failure_handler(failure_opt)
|
100
100
|
|
101
|
-
class_attribute "has_enumerated_#{
|
102
|
-
self.send("has_enumerated_#{
|
101
|
+
class_attribute "has_enumerated_#{attribute_name}_error_handler"
|
102
|
+
self.send( "has_enumerated_#{attribute_name}_error_handler=", failure_handler )
|
103
103
|
|
104
|
+
define_enum_accessor attribute_name, class_name, foreign_key, failure_handler
|
105
|
+
define_enum_writer attribute_name, class_name, foreign_key, failure_handler, allow_empty_name
|
106
|
+
|
107
|
+
if failure_opt.to_s == 'validation_error'
|
108
|
+
define_validation_error( attribute_name )
|
109
|
+
end
|
110
|
+
|
111
|
+
enumerated_attributes << attribute_name
|
112
|
+
|
113
|
+
if options.has_key?(:default)
|
114
|
+
define_default_enum_value( attribute_name, options[:default] )
|
115
|
+
end
|
116
|
+
|
117
|
+
unless create_scope == false
|
118
|
+
define_enum_scope( attribute_name, class_name, foreign_key )
|
119
|
+
end
|
120
|
+
|
121
|
+
end # has_enumerated
|
122
|
+
|
123
|
+
# Defines the accessor method
|
124
|
+
def define_enum_accessor(attribute_name, class_name, foreign_key, failure_handler) #:nodoc:
|
104
125
|
module_eval( <<-end_eval, __FILE__, __LINE__ )
|
105
|
-
def #{
|
106
|
-
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{
|
107
|
-
return @invalid_enum_values[:#{
|
126
|
+
def #{attribute_name}
|
127
|
+
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{attribute_name})
|
128
|
+
return @invalid_enum_values[:#{attribute_name}]
|
108
129
|
end
|
109
130
|
rval = #{class_name}.lookup_id(self.#{foreign_key})
|
110
131
|
if rval.nil? && #{!failure_handler.nil?}
|
111
|
-
self.class.has_enumerated_#{
|
132
|
+
self.class.has_enumerated_#{attribute_name}_error_handler.call(self, :read, #{attribute_name.inspect}, #{foreign_key.inspect}, #{class_name.inspect}, self.#{foreign_key})
|
112
133
|
else
|
113
134
|
rval
|
114
135
|
end
|
115
136
|
end
|
137
|
+
end_eval
|
138
|
+
end
|
139
|
+
private :define_enum_accessor
|
116
140
|
|
117
|
-
|
141
|
+
# Defines the enum attribute writer method
|
142
|
+
def define_enum_writer(attribute_name, class_name, foreign_key, failure_handler, allow_empty_name) #:nodoc:
|
143
|
+
module_eval( <<-end_eval, __FILE__, __LINE__ )
|
144
|
+
def #{attribute_name}=(arg)
|
118
145
|
@invalid_enum_values ||= {}
|
119
146
|
|
120
|
-
#{!
|
147
|
+
#{!allow_empty_name ? 'arg = nil if arg.blank?' : ''}
|
121
148
|
case arg
|
122
149
|
when #{class_name}
|
123
150
|
val = #{class_name}.lookup_id(arg.id)
|
@@ -129,91 +156,94 @@ module PowerEnum::HasEnumerated # :nodoc:
|
|
129
156
|
val = #{class_name}.lookup_id(arg)
|
130
157
|
when nil
|
131
158
|
self.#{foreign_key} = nil
|
132
|
-
@invalid_enum_values.delete :#{
|
159
|
+
@invalid_enum_values.delete :#{attribute_name}
|
133
160
|
return nil
|
134
161
|
else
|
135
|
-
raise TypeError, "#{self.name}: #{
|
162
|
+
raise TypeError, "#{self.name}: #{attribute_name}= argument must be a #{class_name}, String, Symbol or Fixnum but got a: \#{arg.class.attribute_name}"
|
136
163
|
end
|
137
164
|
|
138
165
|
if val.nil?
|
139
166
|
if #{failure_handler.nil?}
|
140
|
-
raise ArgumentError, "#{self.name}: #{
|
167
|
+
raise ArgumentError, "#{self.name}: #{attribute_name}= can't assign a #{class_name} for a value of (\#{arg.inspect})"
|
141
168
|
else
|
142
|
-
@invalid_enum_values.delete :#{
|
143
|
-
self.class.has_enumerated_#{
|
169
|
+
@invalid_enum_values.delete :#{attribute_name}
|
170
|
+
self.class.has_enumerated_#{attribute_name}_error_handler.call(self, :write, #{attribute_name.inspect}, #{foreign_key.inspect}, #{class_name.inspect}, arg)
|
144
171
|
end
|
145
172
|
else
|
146
|
-
@invalid_enum_values.delete :#{
|
173
|
+
@invalid_enum_values.delete :#{attribute_name}
|
147
174
|
self.#{foreign_key} = val.id
|
148
175
|
end
|
149
176
|
end
|
150
177
|
|
151
|
-
alias_method :'#{
|
178
|
+
alias_method :'#{attribute_name}_bak=', :'#{attribute_name}='
|
152
179
|
end_eval
|
180
|
+
end
|
181
|
+
private :define_enum_writer
|
153
182
|
|
154
|
-
|
155
|
-
|
183
|
+
# Defines the default value for the enumerated attribute.
|
184
|
+
def define_default_enum_value(attribute_name, default) #:nodoc:
|
185
|
+
set_default_method = "set_default_value_for_#{attribute_name}".to_sym
|
186
|
+
|
187
|
+
after_initialize set_default_method
|
188
|
+
|
189
|
+
define_method set_default_method do
|
190
|
+
self.send("#{attribute_name}=", default) if self.send(attribute_name).nil?
|
191
|
+
end
|
192
|
+
private set_default_method
|
193
|
+
end
|
194
|
+
private :define_default_enum_value
|
195
|
+
|
196
|
+
# Defines validation_error handling mechanism
|
197
|
+
def define_validation_error(attribute_name) #:nodoc:
|
198
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
156
199
|
validate do
|
157
|
-
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{
|
158
|
-
errors.add(:#{
|
200
|
+
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{attribute_name})
|
201
|
+
errors.add(:#{attribute_name}, "is invalid")
|
159
202
|
end
|
160
203
|
end
|
161
204
|
|
162
|
-
def validation_error(operation,
|
205
|
+
def validation_error(operation, attribute_name, name_foreign_key, acts_enumerated_class_name, lookup_value)
|
163
206
|
@invalid_enum_values ||= {}
|
164
207
|
if operation == :write
|
165
|
-
@invalid_enum_values[
|
208
|
+
@invalid_enum_values[attribute_name.to_sym] = lookup_value
|
166
209
|
else
|
167
210
|
nil
|
168
211
|
end
|
169
212
|
end
|
170
213
|
private :validation_error
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
enumerated_attributes << name
|
175
|
-
|
176
|
-
if options.has_key?(:default)
|
177
|
-
default = options[:default]
|
178
|
-
set_default_method = "set_default_value_for_#{name}".to_sym
|
179
|
-
|
180
|
-
after_initialize set_default_method
|
181
|
-
|
182
|
-
define_method set_default_method do
|
183
|
-
self.send("#{name}=", default) if self.send(name).nil?
|
184
|
-
end
|
185
|
-
private set_default_method
|
186
|
-
end
|
214
|
+
end_eval
|
215
|
+
end
|
216
|
+
private :define_validation_error
|
187
217
|
|
188
|
-
|
189
|
-
|
190
|
-
|
218
|
+
# Defines the enum scopes on the model
|
219
|
+
def define_enum_scope(attribute_name, class_name, foreign_key) #:nodoc:
|
220
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
221
|
+
scope :with_#{attribute_name}, lambda { |*args|
|
191
222
|
ids = args.map{ |arg|
|
192
223
|
n = #{class_name}[arg]
|
193
224
|
}
|
194
225
|
where(:#{foreign_key} => ids)
|
195
226
|
}
|
196
|
-
scope :exclude_#{
|
227
|
+
scope :exclude_#{attribute_name}, lambda {|*args|
|
197
228
|
ids = #{class_name}.all - args.map{ |arg|
|
198
229
|
n = #{class_name}[arg]
|
199
230
|
}
|
200
231
|
where(:#{foreign_key} => ids)
|
201
232
|
}
|
202
|
-
|
233
|
+
end_eval
|
203
234
|
|
204
|
-
|
205
|
-
|
235
|
+
if (name_p = attribute_name.pluralize) != attribute_name
|
236
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
206
237
|
class << self
|
207
|
-
alias_method :with_#{name_p}, :with_#{
|
208
|
-
alias_method :exclude_#{name_p}, :exclude_#{
|
238
|
+
alias_method :with_#{name_p}, :with_#{attribute_name}
|
239
|
+
alias_method :exclude_#{name_p}, :exclude_#{attribute_name}
|
209
240
|
end
|
210
|
-
|
211
|
-
end
|
241
|
+
end_eval
|
212
242
|
end
|
243
|
+
end
|
244
|
+
private :define_enum_scope
|
213
245
|
|
214
|
-
|
215
|
-
|
216
|
-
# If the lookup failure handler is a method name, wraps it in a lambda.
|
246
|
+
# If the lookup failure handler is a method attribute_name, wraps it in a lambda.
|
217
247
|
def get_lookup_failure_handler(failure_opt) # :nodoc:
|
218
248
|
if failure_opt.nil?
|
219
249
|
nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: power_enum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-01-
|
15
|
+
date: 2013-01-28 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rails
|
@@ -142,9 +142,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
142
|
- - ! '>='
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0'
|
145
|
-
segments:
|
146
|
-
- 0
|
147
|
-
hash: 1934880016458665867
|
148
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
146
|
none: false
|
150
147
|
requirements:
|