simple_enum 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/.travis.yml +7 -3
- data/Appraisals +14 -0
- data/Gemfile +7 -1
- data/Gemfile.lock +57 -0
- data/README.rdoc +48 -4
- data/Rakefile +3 -3
- data/gemfiles/rails-3.0.gemfile +11 -0
- data/gemfiles/rails-3.0.gemfile.lock +56 -0
- data/gemfiles/rails-3.1.gemfile +11 -0
- data/gemfiles/rails-3.1.gemfile.lock +58 -0
- data/gemfiles/rails-3.2.gemfile +11 -0
- data/gemfiles/rails-3.2.gemfile.lock +58 -0
- data/lib/simple_enum.rb +35 -16
- data/lib/simple_enum/enum_hash.rb +24 -14
- data/lib/simple_enum/mongoid.rb +52 -0
- data/lib/simple_enum/validation.rb +1 -1
- data/lib/simple_enum/version.rb +1 -1
- data/locales/en.yml +4 -0
- data/simple_enum.gemspec +8 -6
- data/test/array_conversions_test.rb +9 -11
- data/test/class_methods_test.rb +20 -34
- data/test/dirty_attributes_test.rb +4 -5
- data/test/enum_hash_test.rb +16 -21
- data/test/finders_test.rb +13 -9
- data/test/locales.yml +17 -9
- data/test/mongoid_test.rb +38 -0
- data/test/object_backed_test.rb +17 -23
- data/test/orm/active_record.rb +99 -0
- data/test/orm/common.rb +23 -0
- data/test/orm/mongoid.rb +101 -0
- data/test/poro_test.rb +20 -0
- data/test/prefixes_test.rb +5 -7
- data/test/simple_enum_test.rb +68 -55
- data/test/test_helper.rb +22 -49
- data/test/without_shortcuts_test.rb +8 -9
- metadata +101 -39
- data/lib/simple_enum/object_support.rb +0 -37
- data/test/object_support_test.rb +0 -29
data/lib/simple_enum.rb
CHANGED
@@ -8,11 +8,11 @@
|
|
8
8
|
#
|
9
9
|
# See the +as_enum+ documentation for more details.
|
10
10
|
|
11
|
-
# because we depend on
|
11
|
+
# because we depend on i18n and activesupport
|
12
12
|
require 'i18n'
|
13
|
+
require 'active_support'
|
13
14
|
|
14
15
|
require 'simple_enum/enum_hash'
|
15
|
-
require 'simple_enum/object_support'
|
16
16
|
require 'simple_enum/validation'
|
17
17
|
|
18
18
|
require 'active_support/deprecation'
|
@@ -174,7 +174,7 @@ module SimpleEnum
|
|
174
174
|
|
175
175
|
# generate getter
|
176
176
|
define_method("#{enum_cd}") do
|
177
|
-
id = read_attribute options[:column]
|
177
|
+
id = respond_to?(:read_attribute) ? read_attribute(options[:column]) : send(options[:column])
|
178
178
|
values_inverted[id]
|
179
179
|
end
|
180
180
|
|
@@ -182,7 +182,15 @@ module SimpleEnum
|
|
182
182
|
define_method("#{enum_cd}=") do |new_value|
|
183
183
|
v = new_value.blank? ? nil : values[new_value.to_sym]
|
184
184
|
raise(ArgumentError, "Invalid enumeration value: #{new_value}") if (options[:whiny] and v.nil? and !new_value.blank?)
|
185
|
-
write_attribute options[:column], v
|
185
|
+
respond_to?(:write_attribute) ? write_attribute(options[:column], v) : send("#{options[:column]}=", v)
|
186
|
+
end
|
187
|
+
|
188
|
+
# generate checker
|
189
|
+
define_method("#{enum_cd}?") do |*args|
|
190
|
+
current = send(enum_cd)
|
191
|
+
return current == EnumHash.symbolize(args.first) if args.length > 0
|
192
|
+
|
193
|
+
!!current
|
186
194
|
end
|
187
195
|
|
188
196
|
# support dirty attributes by delegating to column, currently opt-in
|
@@ -200,6 +208,10 @@ module SimpleEnum
|
|
200
208
|
attr_name = enum_cd.to_s.pluralize
|
201
209
|
enum_attr = :"#{attr_name.downcase}_enum_hash"
|
202
210
|
|
211
|
+
define_method("human_#{enum_cd}") do
|
212
|
+
self.class.human_enum_name(attr_name, self.send(enum_cd))
|
213
|
+
end
|
214
|
+
|
203
215
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
204
216
|
class_attribute #{enum_attr.inspect}, :instance_write => false, :instance_reader => false
|
205
217
|
|
@@ -226,13 +238,14 @@ module SimpleEnum
|
|
226
238
|
prefix = options[:prefix] && "#{options[:prefix] == true ? enum_cd : options[:prefix]}_"
|
227
239
|
|
228
240
|
values.each do |k,code|
|
229
|
-
sym = k
|
241
|
+
sym = EnumHash.symbolize(k)
|
230
242
|
|
231
243
|
define_method("#{prefix}#{sym}?") do
|
232
|
-
|
244
|
+
current = respond_to?(:read_attribute) ? read_attribute(options[:column]) : send(options[:column])
|
245
|
+
code == current
|
233
246
|
end
|
234
247
|
define_method("#{prefix}#{sym}!") do
|
235
|
-
write_attribute options[:column], code
|
248
|
+
respond_to?(:write_attribute) ? write_attribute(options[:column], code) : send("#{options[:column]}=", code)
|
236
249
|
sym
|
237
250
|
end
|
238
251
|
|
@@ -247,19 +260,25 @@ module SimpleEnum
|
|
247
260
|
include Validation
|
248
261
|
|
249
262
|
def human_enum_name(enum, key, options = {})
|
250
|
-
|
251
|
-
|
252
|
-
|
263
|
+
defaults = lookup_ancestors.map do |klass|
|
264
|
+
:"#{self.i18n_scope}.enums.#{klass.model_name.i18n_key}.#{enum}.#{key}"
|
265
|
+
end
|
266
|
+
|
267
|
+
defaults << :"enums.#{self.model_name.i18n_key}.#{enum}.#{key}"
|
268
|
+
defaults << :"enums.#{enum}.#{key}"
|
253
269
|
defaults << options.delete(:default) if options[:default]
|
254
|
-
defaults <<
|
255
|
-
|
256
|
-
|
270
|
+
defaults << key.to_s.humanize
|
271
|
+
|
272
|
+
options.reverse_merge! :count => 1, :default => defaults
|
273
|
+
I18n.translate(defaults.shift, options)
|
257
274
|
end
|
258
275
|
end
|
259
276
|
end
|
260
277
|
|
261
|
-
#
|
262
|
-
|
278
|
+
# include in AR
|
279
|
+
ActiveSupport.on_load(:active_record) do
|
280
|
+
ActiveRecord::Base.send(:include, SimpleEnum)
|
281
|
+
end
|
263
282
|
|
264
|
-
|
283
|
+
# setup i18n load path...
|
265
284
|
I18n.load_path << File.join(File.dirname(__FILE__), '..', 'locales', 'en.yml')
|
@@ -1,46 +1,56 @@
|
|
1
1
|
module SimpleEnum
|
2
|
-
|
2
|
+
|
3
3
|
# Internal hash class, used to handle the enumerations et al.
|
4
4
|
# Works like to original +Hash+ class, but with some added value,
|
5
|
-
# like access to
|
5
|
+
# like access to
|
6
|
+
#
|
6
7
|
#
|
7
|
-
#
|
8
8
|
class EnumHash < ::ActiveSupport::OrderedHash
|
9
|
+
|
10
|
+
# Converts an entity to a symbol, uses to_enum_sym, if possible.
|
11
|
+
def self.symbolize(sym)
|
12
|
+
return sym.to_enum_sym if sym.respond_to?(:to_enum_sym)
|
13
|
+
return sym.to_sym if sym.respond_to?(:to_sym)
|
14
|
+
return sym.name.to_s.parameterize('_').to_sym if sym.respond_to?(:name)
|
15
|
+
sym.to_param.to_sym unless sym.blank?
|
16
|
+
end
|
17
|
+
|
9
18
|
def initialize(args = [])
|
10
19
|
super()
|
11
|
-
|
20
|
+
|
12
21
|
@reverse_sym_lookup = {}
|
13
22
|
@sym_value_lookup = {}
|
14
23
|
|
15
24
|
if args.is_a?(Hash)
|
16
25
|
args.each { |k,v| set_value_for_reverse_lookup(k, v) }
|
17
26
|
else
|
18
|
-
ary = args.send(args.respond_to?(:enum_with_index) ? :enum_with_index : :each_with_index).to_a unless args.first.
|
19
|
-
ary = args.map { |e| [e, e.id] } if args.first.is_a?(
|
27
|
+
ary = args.send(args.respond_to?(:enum_with_index) ? :enum_with_index : :each_with_index).to_a unless args.first.respond_to?(:map)
|
28
|
+
ary = args.map { |e| [e, e.id] } if args.first.respond_to?(:map) && !args.first.is_a?(Array)
|
20
29
|
ary ||= args
|
21
30
|
ary.each { |e| set_value_for_reverse_lookup(e[0], e[1]) }
|
22
31
|
end
|
23
32
|
end
|
24
|
-
|
33
|
+
|
25
34
|
def default(k = nil)
|
26
|
-
@sym_value_lookup[k
|
35
|
+
@sym_value_lookup[EnumHash.symbolize(k)] if k
|
27
36
|
end
|
28
|
-
|
37
|
+
|
29
38
|
def method_missing(symbol, *args)
|
30
|
-
|
31
|
-
|
39
|
+
sym = EnumHash.symbolize(symbol)
|
40
|
+
if @sym_value_lookup.has_key?(sym)
|
41
|
+
return @reverse_sym_lookup[sym] if args.first
|
32
42
|
self[symbol]
|
33
43
|
else
|
34
44
|
super
|
35
45
|
end
|
36
46
|
end
|
37
|
-
|
47
|
+
|
38
48
|
private
|
39
49
|
def set_value_for_reverse_lookup(key, value)
|
40
|
-
sym = key
|
50
|
+
sym = EnumHash.symbolize(key)
|
41
51
|
self[key] = value
|
42
52
|
@reverse_sym_lookup[sym] = key
|
43
53
|
@sym_value_lookup[sym] = value
|
44
|
-
end
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'simple_enum'
|
2
|
+
|
3
|
+
module SimpleEnum
|
4
|
+
|
5
|
+
# Enables support for mongoid, also automatically creates the
|
6
|
+
# requested field.
|
7
|
+
#
|
8
|
+
# class Person
|
9
|
+
# include Mongoid::Document
|
10
|
+
# include SimpleEnum::Mongoid
|
11
|
+
#
|
12
|
+
# field :name
|
13
|
+
# as_enum :gender, [:female, :male]
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# When no field is requested:
|
17
|
+
#
|
18
|
+
# field :gender_cd, :type => Integer
|
19
|
+
# as_enum :gender, [:female, :male], :field => false
|
20
|
+
#
|
21
|
+
# or custom field options (like e.g. type want to be passed):
|
22
|
+
#
|
23
|
+
# as_enum :gender, [:female, :male], :field => { :type => Integer }
|
24
|
+
#
|
25
|
+
module Mongoid
|
26
|
+
extend ActiveSupport::Concern
|
27
|
+
|
28
|
+
included do
|
29
|
+
# create class level methods
|
30
|
+
class_attribute :enum_definitions, :instance_write => false, :instance_reader => false
|
31
|
+
enum_definitions = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
include SimpleEnum::ClassMethods
|
36
|
+
|
37
|
+
# Wrap method chain to create mongoid field and additional
|
38
|
+
# column options
|
39
|
+
def as_enum_with_mongoid(enum_cd, values, options = {})
|
40
|
+
options = SimpleEnum.default_options.merge({ :column => "#{enum_cd}_cd" }).merge(options)
|
41
|
+
|
42
|
+
# forward custom field options
|
43
|
+
field_options = options.delete(:field)
|
44
|
+
field(options[:column], field_options.is_a?(Hash) ? field_options : {}) unless field_options === false
|
45
|
+
|
46
|
+
# call original as_enum method
|
47
|
+
as_enum_without_mongoid(enum_cd, values, options)
|
48
|
+
end
|
49
|
+
alias_method_chain :as_enum, :mongoid
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -29,7 +29,7 @@ module SimpleEnum
|
|
29
29
|
enum_def = enum_definitions[attr_name]
|
30
30
|
unless send(enum_def[:name].to_s.pluralize).values.include?(value)
|
31
31
|
params = { :value => value}
|
32
|
-
params[:
|
32
|
+
params[:message] = configuration[:message] if configuration[:message].present?
|
33
33
|
record.errors.add(enum_def[:name], :invalid_enum, params)
|
34
34
|
end
|
35
35
|
end
|
data/lib/simple_enum/version.rb
CHANGED
data/locales/en.yml
CHANGED
data/simple_enum.gemspec
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "simple_enum"
|
7
7
|
s.version = SimpleEnum::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.summary = "Simple enum-like field support for
|
10
|
-
s.description = ""
|
9
|
+
s.summary = "Simple enum-like field support for models."
|
10
|
+
s.description = "Provides enum-like fields for ActiveRecord, ActiveModel and Mongoid models."
|
11
11
|
|
12
12
|
s.required_ruby_version = ">= 1.8.7"
|
13
13
|
s.required_rubygems_version = ">= 1.3.6"
|
@@ -22,10 +22,12 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.license = 'MIT'
|
24
24
|
|
25
|
-
s.add_dependency "activesupport",
|
26
|
-
|
27
|
-
s.add_development_dependency 'rake', '>= 0.
|
28
|
-
s.add_development_dependency '
|
25
|
+
s.add_dependency "activesupport", '>= 3.0.0'
|
26
|
+
|
27
|
+
s.add_development_dependency 'rake', '>= 0.9.2'
|
28
|
+
s.add_development_dependency 'appraisal', '>= 0.4'
|
29
|
+
s.add_development_dependency 'minitest', '>= 2.3.0'
|
30
|
+
s.add_development_dependency 'activerecord', '>= 3.0.0'
|
29
31
|
s.add_development_dependency 'mongoid', '~> 2.0'
|
30
32
|
|
31
33
|
unless RUBY_PLATFORM =~ /java/
|
@@ -1,23 +1,21 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class ArrayConversionsTest <
|
3
|
+
class ArrayConversionsTest < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
5
5
|
reload_db :genders => true
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
with_enum = DummyArrayTest1
|
8
|
+
def test_conversion_to_enumartions
|
9
|
+
with_enum = named_dummy('DummyArrayTest1') do
|
10
|
+
as_enum :gender, Gender.all.map { |g| [g.name.to_sym, g.id] }
|
11
|
+
end
|
14
12
|
|
15
|
-
assert_equal
|
16
|
-
assert_equal
|
17
|
-
assert_equal
|
13
|
+
assert_equal @male.id, with_enum.male
|
14
|
+
assert_equal @female.id, with_enum.female
|
15
|
+
assert_equal @female.id, with_enum.genders(:female)
|
18
16
|
|
19
17
|
jane = with_enum.new :gender => :female
|
20
18
|
assert_equal :female, jane.gender
|
21
|
-
assert_equal
|
19
|
+
assert_equal @female.id, jane.gender_cd
|
22
20
|
end
|
23
21
|
end
|
data/test/class_methods_test.rb
CHANGED
@@ -1,38 +1,40 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class ClassMethodsTest <
|
3
|
+
class ClassMethodsTest < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
5
5
|
reload_db
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
def test_that_klass_genders_array_accessor_equal_to_attr_accessor
|
9
9
|
assert_equal 0, Dummy.genders(:male)
|
10
10
|
assert_equal Dummy.genders(:male), Dummy.genders[:male]
|
11
11
|
assert_nil Dummy.genders(:inexistent)
|
12
12
|
assert_nil Dummy.genders[:inexistent]
|
13
|
-
|
13
|
+
refute_nil Dummy.genders[:female]
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
def test_inheritance_of_genders_to_subclasses
|
17
|
+
# issue#3
|
17
18
|
assert_equal({ :female => 1, :male => 0}, SpecificDummy.genders)
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
def test_genders_reader_created
|
21
22
|
assert_equal [0, 1], Dummy.genders.values.sort
|
22
23
|
assert_equal %w{female male}, Dummy.genders.keys.map(&:to_s).sort
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
+
def test_that_klass_genders_return_array_of_values
|
27
|
+
# usefuled for IN clauses
|
26
28
|
assert_equal [0, 1], Dummy.genders(:male, :female)
|
27
29
|
assert_equal [1, 0], Dummy.genders(:female, :male)
|
28
30
|
end
|
29
31
|
|
30
|
-
|
32
|
+
def test_inverted_hash_returns_synonym_by_code
|
31
33
|
assert_equal :male, Dummy.genders.invert[0]
|
32
34
|
assert_equal :female, Dummy.genders.invert[1]
|
33
35
|
end
|
34
36
|
|
35
|
-
|
37
|
+
def test_generation_of_value_shortcuts_on_class
|
36
38
|
g = Dummy.new
|
37
39
|
|
38
40
|
assert_equal 0, Dummy.male
|
@@ -44,24 +46,20 @@ class ClassMethodsTest < ActiveSupport::TestCase
|
|
44
46
|
assert_respond_to Dummy, :foobar
|
45
47
|
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
set_table_name 'dummies'
|
49
|
+
def test_that_no_klass_shortcuts_are_created_if_slim_true
|
50
|
+
with_slim = named_dummy('Dummy1') do
|
50
51
|
as_enum :gender, [:male, :female], :slim => true
|
51
52
|
end
|
52
|
-
with_slim = Dummy1
|
53
53
|
|
54
54
|
assert !with_slim.respond_to?(:male)
|
55
55
|
assert !with_slim.respond_to?(:female)
|
56
56
|
assert_respond_to with_slim, :genders
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
set_table_name 'dummies'
|
59
|
+
def test_that_no_klass_shortcuts_are_created_if_slim_class_though_instance_shortcuts_are
|
60
|
+
with_slim_class = named_dummy('Dummy2') do
|
62
61
|
as_enum :gender, [:male, :female], :slim => :class
|
63
62
|
end
|
64
|
-
with_slim_class = Dummy2
|
65
63
|
|
66
64
|
jane = with_slim_class.new
|
67
65
|
|
@@ -74,12 +72,10 @@ class ClassMethodsTest < ActiveSupport::TestCase
|
|
74
72
|
assert_same 1, with_slim_class.genders[:female]
|
75
73
|
end
|
76
74
|
|
77
|
-
|
78
|
-
|
79
|
-
set_table_name 'dummies'
|
75
|
+
def test_that_klass_shortcuts_respect_prefix_true_and_are_prefixed_by_enum_cd
|
76
|
+
with_prefix = named_dummy('Dummy3') do
|
80
77
|
as_enum :gender, [:male, :female], :prefix => true
|
81
78
|
end
|
82
|
-
with_prefix = Dummy3
|
83
79
|
|
84
80
|
assert !with_prefix.respond_to?(:male)
|
85
81
|
assert !with_prefix.respond_to?(:female)
|
@@ -89,12 +85,10 @@ class ClassMethodsTest < ActiveSupport::TestCase
|
|
89
85
|
assert_respond_to with_prefix, :genders
|
90
86
|
end
|
91
87
|
|
92
|
-
|
93
|
-
|
94
|
-
set_table_name 'dummies'
|
88
|
+
def test_to_ensure_that_klass_shortcut_also_work_with_custom_prefixes
|
89
|
+
with_custom_prefix = named_dummy('Dummy4') do
|
95
90
|
as_enum :gender, [:male, :female], :prefix => :g
|
96
91
|
end
|
97
|
-
with_custom_prefix = Dummy4
|
98
92
|
|
99
93
|
assert !with_custom_prefix.respond_to?(:male)
|
100
94
|
assert !with_custom_prefix.respond_to?(:female)
|
@@ -105,22 +99,14 @@ class ClassMethodsTest < ActiveSupport::TestCase
|
|
105
99
|
assert_respond_to with_custom_prefix, :genders
|
106
100
|
end
|
107
101
|
|
108
|
-
|
102
|
+
def test_that_the_human_enum_name_method_returns_translated_humanized_values
|
109
103
|
assert_equal :male.to_s.humanize, Dummy.human_enum_name(:genders, :male)
|
110
104
|
assert_equal "Girl", Dummy.human_enum_name(:genders, :female)
|
111
105
|
assert_equal "Foo", Dummy.human_enum_name(:didums, :foo)
|
112
106
|
assert_equal "Foos", Dummy.human_enum_name(:didums, :foo, :count => 5)
|
113
107
|
end
|
114
108
|
|
115
|
-
|
116
|
-
for_select = Dummy.genders_for_select
|
117
|
-
genders = Dummy.genders
|
118
|
-
assert_equal genders.first.first, for_select.first.second
|
119
|
-
assert_equal ["Male", :male], for_select.first
|
120
|
-
assert_equal ["Girl", :female], for_select.last
|
121
|
-
end
|
122
|
-
|
123
|
-
test "enum_for_select(:value) class method" do
|
109
|
+
def test_enum_for_select_value_class_method
|
124
110
|
for_select = Dummy.genders_for_select(:value)
|
125
111
|
assert_equal ["Male", 0], for_select.first
|
126
112
|
assert_equal ["Girl", 1], for_select.last
|