lwe-simple_enum 0.3.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.
@@ -0,0 +1,15 @@
1
+ module SimpleEnum
2
+ module ArraySupport
3
+
4
+ # Magically convert an array to a hash, has some neat features
5
+ # for active record models and similar.
6
+ #
7
+ # TODO: add more documentation; allow block to be passed to customize key/value pairs
8
+ def to_hash_magic
9
+ v = enum_with_index.to_a unless first.is_a?(ActiveRecord::Base) or first.is_a?(Array)
10
+ v = map { |e| [e, e.id] } if first.is_a?(ActiveRecord::Base)
11
+ v ||= self
12
+ Hash[*v.flatten]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module SimpleEnum
2
+
3
+ # Internal hash class, used to handle the enumerations et al.
4
+ # Works like to original +Hash+ class, but with some added value,
5
+ # like access to
6
+ #
7
+ #
8
+ class EnumHash < ::Hash
9
+ def initialize(hsh)
10
+ hsh = hsh.to_hash_magic unless hsh.is_a?(Hash)
11
+
12
+ @reverse_sym_lookup = {}
13
+ @sym_value_lookup = {}
14
+
15
+ hsh.each do |k,v|
16
+ sym = k.to_enum_sym
17
+ self[k] = v
18
+ @reverse_sym_lookup[sym] = k
19
+ @sym_value_lookup[sym] = v
20
+ end
21
+ end
22
+
23
+ def default(k = nil)
24
+ @sym_value_lookup[k.to_enum_sym] if k
25
+ end
26
+
27
+ def method_missing(symbol, *args)
28
+ if @sym_value_lookup.has_key?(symbol.to_enum_sym)
29
+ return @reverse_sym_lookup[symbol.to_enum_sym] if args.first
30
+ self[symbol]
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ module SimpleEnum
2
+ module ObjectSupport
3
+
4
+ # Convert object to symbol for use in symbolized enum
5
+ # methods. Return value is supposed to be a symbol,
6
+ # though strings should work as well.
7
+ #
8
+ # The default behaviour is to try +to_sym+ first, then
9
+ # checks if a field named +name+ exists or finally falls
10
+ # back to +to_param+ method as provided by ActiveSupport.
11
+ #
12
+ # It's perfectly for subclasses to override this method,
13
+ # to provide custom +to_enum_sym+ behaviour, e.g. if
14
+ # the symbolized value is in e.g. +title+:
15
+ #
16
+ # class FormOfAddress < ActiveRecord::Base
17
+ # attr_accessor :title
18
+ # def to_enum_sym; title; end
19
+ # end
20
+ #
21
+ # *Note*: to provide better looking methods values for +name+
22
+ # are <tt>parametereize('_')</tt>'d, so it might be a good idea to do
23
+ # the same thing in a custom +to_enum_sym+ method, like (for the
24
+ # example above):
25
+ #
26
+ # def to_enum_sym; title.parameterize('_').to_sym; end
27
+ #
28
+ # *TODO*: The current implementation does not handle nil values very
29
+ # gracefully, so if +name+ returns +nil+, it should be handled
30
+ # a bit better I suppose...
31
+ def to_enum_sym
32
+ return to_sym if respond_to?(:to_sym)
33
+ return name.to_s.parameterize('_').to_sym if respond_to?(:name)
34
+ to_param.to_sym unless blank? # fallback, unless empty...
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module SimpleEnum
2
+ module Validation
3
+
4
+ # Validates an +as_enum+ field based on the value of it's column.
5
+ #
6
+ # Model:
7
+ # class User < ActiveRecord::Base
8
+ # as_enum :gender, [ :male, :female ]
9
+ # validates_as_enum :gender
10
+ # end
11
+ #
12
+ # View:
13
+ # <%= select(:user, :gender, User.genders.keys) %>
14
+ #
15
+ # Configuration options:
16
+ # * <tt>:message</tt> - A custom error message (default: is <tt>[:activerecord, :errors, :messages, :invalid_enum]</tt>).
17
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
18
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
19
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
20
+ # method, proc or string should return or evaluate to a true or false value.
21
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
22
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
23
+ # method, proc or string should return or evaluate to a true or false value.
24
+ def validates_as_enum(*attr_names)
25
+ configuration = { :on => :save }
26
+ configuration.update(attr_names.extract_options!)
27
+ attr_names.map! { |e| enum_definitions[e][:column] } # map to column name
28
+
29
+ validates_each(attr_names, configuration) do |record, attr_name, value|
30
+ enum_def = enum_definitions[attr_name]
31
+ unless send(enum_def[:name].to_s.pluralize).values.include?(value)
32
+ record.errors.add(enum_def[:name], :invalid_enum, :default => configuration[:message], :value => value)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleEnum
2
+ module Version #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 5
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.').freeze
8
+
9
+ def self.to_s; STRING; end
10
+ end
11
+
12
+ NAME = "simple_enum".freeze
13
+ ABOUT = "#{NAME} #{Version}".freeze
14
+ end
@@ -0,0 +1,6 @@
1
+ # english translation
2
+ en:
3
+ activerecord:
4
+ errors:
5
+ messages:
6
+ invalid_enum: invalid option supplied.
@@ -0,0 +1,72 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{simple_enum}
5
+ s.version = "0.3.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Lukas Westermann"]
9
+ s.date = %q{2009-07-07}
10
+ s.email = %q{lukas.westermann@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "README.rdoc"
13
+ ]
14
+ s.files = [
15
+ ".gitignore",
16
+ "LICENCE",
17
+ "README.rdoc",
18
+ "Rakefile",
19
+ "VERSION.yml",
20
+ "init.rb",
21
+ "lib/simple_enum.rb",
22
+ "lib/simple_enum/array_support.rb",
23
+ "lib/simple_enum/enum_hash.rb",
24
+ "lib/simple_enum/object_support.rb",
25
+ "lib/simple_enum/validation.rb",
26
+ "lib/simple_enum/version.rb",
27
+ "locales/en.yml",
28
+ "simple_enum.gemspec",
29
+ "test/array_conversions_test.rb",
30
+ "test/class_methods_test.rb",
31
+ "test/enum_hash_test.rb",
32
+ "test/finders_test.rb",
33
+ "test/models.rb",
34
+ "test/object_backed_test.rb",
35
+ "test/object_support_test.rb",
36
+ "test/prefixes_test.rb",
37
+ "test/se_array_support_test.rb",
38
+ "test/simple_enum_test.rb",
39
+ "test/test_helper.rb",
40
+ "test/without_shortcuts_test.rb"
41
+ ]
42
+ s.has_rdoc = true
43
+ s.homepage = %q{http://github.com/lwe/simple_enum}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.2}
47
+ s.summary = %q{Simple enum-like field support for ActiveRecord (including validations and i18n)}
48
+ s.test_files = [
49
+ "test/array_conversions_test.rb",
50
+ "test/class_methods_test.rb",
51
+ "test/enum_hash_test.rb",
52
+ "test/finders_test.rb",
53
+ "test/models.rb",
54
+ "test/object_backed_test.rb",
55
+ "test/object_support_test.rb",
56
+ "test/prefixes_test.rb",
57
+ "test/se_array_support_test.rb",
58
+ "test/simple_enum_test.rb",
59
+ "test/test_helper.rb",
60
+ "test/without_shortcuts_test.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ else
69
+ end
70
+ else
71
+ end
72
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class ArrayConversionsTest < ActiveSupport::TestCase
4
+ def setup
5
+ reload_db :genders => true
6
+ end
7
+
8
+ test "that conversion of Gender.find(:all).map {...} to enumeration values as symbols works the same as [:male,:female]" do
9
+ with_enum = Class.new(ActiveRecord::Base) do
10
+ set_table_name 'dummies'
11
+ as_enum :gender, Gender.find(:all).map { |g| [g.name.to_sym, g.id] }
12
+ end
13
+
14
+ assert_equal 0, with_enum.male
15
+ assert_equal 1, with_enum.female
16
+ assert_equal 1, with_enum.genders(:female)
17
+
18
+ jane = with_enum.new :gender => :female
19
+ assert_equal :female, jane.gender
20
+ assert_equal 1, jane.gender_cd
21
+ end
22
+ end
@@ -0,0 +1,108 @@
1
+ require 'test_helper'
2
+
3
+ class ClassMethodsTest < ActiveSupport::TestCase
4
+ def setup
5
+ reload_db
6
+ end
7
+
8
+ test "that Klass.genders[:sym] == Klass.genders(:sym)" do
9
+ assert_equal 0, Dummy.genders(:male)
10
+ assert_equal Dummy.genders(:male), Dummy.genders[:male]
11
+ assert_nil Dummy.genders(:inexistent)
12
+ assert_nil Dummy.genders[:inexistent]
13
+ end
14
+
15
+ test "that inst.values_for_... is deprecated (by trapping Kernel\#warn)" do
16
+ # ensure that warn() is trapped
17
+ trapped_warn_dummy = Class.new(Dummy) do
18
+ @@LAST_WARNING = nil
19
+ def warn(msg); @@LAST_WARNING = msg; end;
20
+ def self.last_warning; @@LAST_WARNING; end
21
+ end
22
+
23
+ g = trapped_warn_dummy.new
24
+ g.values_for_gender
25
+
26
+ assert_match /\ADEPRECATION WARNING.*values_for_gender.*genders/, trapped_warn_dummy.last_warning
27
+ end
28
+
29
+ test "generation of value shortcuts on class" do
30
+ g = Dummy.new
31
+
32
+ assert_equal 0, Dummy.male
33
+ assert_equal 1, Dummy.female
34
+ assert_equal 'alpha', Dummy.alpha
35
+ assert_respond_to Dummy, :male
36
+ assert_respond_to Dummy, :female
37
+ assert_respond_to Dummy, :beta
38
+ assert_respond_to Dummy, :foobar
39
+ end
40
+
41
+ test "that no Klass.shortcut are created if :slim => true" do
42
+ with_slim = Class.new(ActiveRecord::Base) do
43
+ set_table_name 'dummies'
44
+ as_enum :gender, [:male, :female], :slim => true
45
+ end
46
+
47
+ assert !with_slim.respond_to?(:male)
48
+ assert !with_slim.respond_to?(:female)
49
+ assert_respond_to with_slim, :genders
50
+ end
51
+
52
+ test "that no Klass.shortcut's are created if :slim => :class, though instance shortcuts are" do
53
+ with_slim_class = Class.new(ActiveRecord::Base) do
54
+ set_table_name 'dummies'
55
+ as_enum :gender, [:male, :female], :slim => :class
56
+ end
57
+
58
+ jane = with_slim_class.new
59
+
60
+ assert_respond_to jane, :male!
61
+ assert_respond_to jane, :female!
62
+ assert !with_slim_class.respond_to?(:male)
63
+ assert !with_slim_class.respond_to?(:female)
64
+ assert_respond_to with_slim_class, :genders
65
+ assert_same 0, with_slim_class.genders.male
66
+ assert_same 1, with_slim_class.genders[:female]
67
+ end
68
+
69
+ test "that Klass.shortcut respect :prefix => true and are prefixed by \#{enum_cd}" do
70
+ with_prefix = Class.new(ActiveRecord::Base) do
71
+ set_table_name 'dummies'
72
+ as_enum :gender, [:male, :female], :prefix => true
73
+ end
74
+
75
+ assert !with_prefix.respond_to?(:male)
76
+ assert !with_prefix.respond_to?(:female)
77
+ assert_respond_to with_prefix, :gender_male
78
+ assert_respond_to with_prefix, :gender_female
79
+ assert_equal 0, with_prefix.gender_male
80
+ assert_respond_to with_prefix, :genders
81
+ end
82
+
83
+ test "to ensure that Klass.shortcut also work with custom prefixes" do
84
+ with_custom_prefix = Class.new(ActiveRecord::Base) do
85
+ set_table_name 'dummies'
86
+ as_enum :gender, [:male, :female], :prefix => :g
87
+ end
88
+
89
+ assert !with_custom_prefix.respond_to?(:male)
90
+ assert !with_custom_prefix.respond_to?(:female)
91
+ assert !with_custom_prefix.respond_to?(:gender_female)
92
+ assert_respond_to with_custom_prefix, :g_male
93
+ assert_respond_to with_custom_prefix, :g_female
94
+ assert_equal 1, with_custom_prefix.g_female
95
+ assert_respond_to with_custom_prefix, :genders
96
+ end
97
+
98
+ test "new :upcase option for those guys picky with coding guidelines etc." do
99
+ with_upcase = Class.new(ActiveRecord::Base) do
100
+ set_table_name 'dummies'
101
+ as_enum :gender, [:male, :female], :upcase => true
102
+ end
103
+
104
+ assert_respond_to with_upcase, :GENDERS
105
+ assert_same 0, with_upcase.GENDERS.male
106
+ assert_same 1, with_upcase.GENDERS[:female]
107
+ end
108
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+ require 'simple_enum/enum_hash'
3
+
4
+ class EnumHashTest < ActiveSupport::TestCase
5
+
6
+ test "create new EnumHash instance from array of symbols" do
7
+ genders = SimpleEnum::EnumHash.new [:male, :female]
8
+
9
+ assert_same 0, genders[:male]
10
+ assert_same 1, genders[:female]
11
+ assert_same 0, genders.male
12
+ assert_same :female, genders.female(true)
13
+ end
14
+
15
+ test "create new EnumHash instance from Hash" do
16
+ status = SimpleEnum::EnumHash.new :inactive => 0, :active => 1, :archived => 99
17
+
18
+ assert_same 0, status.inactive
19
+ assert_same 1, status[:active]
20
+ end
21
+
22
+ test "create new EnumHash instance from ActiveRecord results" do
23
+ reload_db :genders => true
24
+ genders = SimpleEnum::EnumHash.new Gender.find(:all)
25
+
26
+ male = Gender.find(0)
27
+
28
+ assert_same 0, genders[male]
29
+ assert_same genders[male], genders[:male]
30
+ assert_same 1, genders.female
31
+ assert_equal male, genders.send(:male, true)
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+
3
+ class FindersTest < ActiveSupport::TestCase
4
+ def setup
5
+ reload_db
6
+ end
7
+
8
+ test "find all where :gender = :female" do
9
+ girls = Dummy.find :all, :conditions => { :gender_cd => Dummy.genders[:female] }, :order => 'name ASC'
10
+
11
+ assert_equal 2, girls.length
12
+
13
+ assert_equal 'Anna', girls.first.name
14
+ assert_equal :female, girls.first.gender
15
+ assert_equal true, girls.first.female?
16
+ end
17
+
18
+ test "find all where :word is 'gamma'" do
19
+ gammas = Dummy.find :all, :conditions => { :word_cd => Dummy.words(:gamma) }
20
+
21
+ assert_equal 1, gammas.length
22
+ assert_equal 'Chris', gammas.first.name
23
+ assert_equal true, gammas.first.male?
24
+ assert_equal 'gamma', gammas.first.word_cd
25
+ assert_equal :gamma, gammas.first.word
26
+ end
27
+
28
+ test "find with string conditions for all :didum = :foo" do
29
+ foos = Dummy.find :all, :conditions => ['other = ?', Dummy.didums(:foo)]
30
+
31
+ assert_equal 1, foos.length
32
+ assert_equal false, foos.first.foobar?
33
+ end
34
+
35
+ test "find using insecure inline string conditions" do
36
+ men = Dummy.find :all, :conditions => "gender_cd = #{Dummy.genders(:male)}"
37
+
38
+ assert_equal 1, men.length
39
+ assert_equal true, men.first.male?
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ class Dummy < ActiveRecord::Base
2
+ as_enum :gender, [:male, :female]
3
+ as_enum :word, { :alpha => 'alpha', :beta => 'beta', :gamma => 'gamma'}
4
+ as_enum :didum, [ :foo, :bar, :foobar ], :column => 'other'
5
+ end
6
+
7
+ class Gender < ActiveRecord::Base
8
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class ObjectBackedTest < ActiveSupport::TestCase
4
+ def setup
5
+ reload_db :genders => true
6
+ end
7
+
8
+ test "how working with object backed columns work..." do
9
+ # simple object -> not db backed instance
10
+ simple_obj = Class.new do
11
+ attr_accessor :name
12
+ def initialize(name)
13
+ @name = name
14
+ end
15
+ end
16
+
17
+ # create new class by using simple_obj
18
+ with_object = Class.new(ActiveRecord::Base) do
19
+ set_table_name 'dummies'
20
+ as_enum :gender, { simple_obj.new('Male') => 0, simple_obj.new('Female') => 1 }
21
+ end
22
+
23
+ d = with_object.find_by_name('Anna')
24
+
25
+ assert_same simple_obj, d.gender.class
26
+ assert_equal 'Female', d.gender.name
27
+ assert_same true, d.female?
28
+ assert_same false, d.male?
29
+ assert_same 0, with_object.male
30
+ end
31
+
32
+ test "db backed objects, using method described in 'Advanced Rails Recipes - Recipe 61: Look Up Constant Data Efficiently'" do
33
+ # "cache" as defined in ARR#61
34
+ genders = Gender.find(:all)
35
+ # works without mapping... .map { |g| [g, g.id] }
36
+
37
+ # use cached array of values
38
+ with_db_obj = Class.new(ActiveRecord::Base) do
39
+ set_table_name 'dummies'
40
+ as_enum :gender, genders
41
+ end
42
+
43
+ d = with_db_obj.find_by_name('Bella');
44
+
45
+ assert_respond_to with_db_obj, :female
46
+ assert_respond_to with_db_obj, :male
47
+ assert_equal 0, with_db_obj.male
48
+ end
49
+
50
+ test "that accessing keys and values of each enumeration value works as expected" do
51
+ genders = Gender.find(:all, :order => :id)
52
+
53
+ male = genders.first
54
+ female = genders.last
55
+
56
+ with_db_obj = Class.new(ActiveRecord::Base) do
57
+ set_table_name 'dummies'
58
+ as_enum :gender, genders
59
+ end
60
+
61
+ assert_same male.id, with_db_obj.male
62
+ assert_same male, with_db_obj.male(true)
63
+
64
+ assert_same :male, Dummy.male(true)
65
+ assert_same 0, Dummy.male
66
+ end
67
+ end