enumerated_field 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +94 -0
- data/Rakefile +8 -0
- data/enumerated_field.gemspec +7 -0
- data/lib/enumerated_field/version.rb +1 -1
- data/lib/enumerated_field.rb +30 -7
- data/test/enumerated_field_test.rb +107 -30
- data/test/test_helper.rb +5 -2
- metadata +76 -7
- data/README +0 -37
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
EnumeratedField is a library that provides some nice methods when a string column is used like an enumeration, meaning there is a list of allowable values for the string column. Typically you want the display value as seen by the end user to differ from the stored value, allowing you to easily change the display value at anytime without migrating data, and this little gem helps you with that.
|
2
|
+
|
3
|
+
## Usage
|
4
|
+
|
5
|
+
enum_field(field_name, choices, options = {})
|
6
|
+
|
7
|
+
Available options are:
|
8
|
+
|
9
|
+
* `:validate`, whether to validate that the value is in the given list
|
10
|
+
of choices. Defaults to true.
|
11
|
+
* `:allow_nil`, whether a nil value passes validation. Defaults to
|
12
|
+
false.
|
13
|
+
* `:allow_blank`, whether a blank (nil, "") value passes validation.
|
14
|
+
Defaults to false.
|
15
|
+
|
16
|
+
The default validation uses ActiveModel's inclusion validations. If using on a
|
17
|
+
class without ActiveModel use `:validate => false` to disable
|
18
|
+
these.
|
19
|
+
|
20
|
+
## Example
|
21
|
+
|
22
|
+
class Hike < ActiveRecord::Base
|
23
|
+
include EnumeratedField
|
24
|
+
|
25
|
+
# default form
|
26
|
+
enum_field :duration, [
|
27
|
+
['Short', 'short'],
|
28
|
+
['Really, really long', 'long']
|
29
|
+
]
|
30
|
+
# disable default validation
|
31
|
+
enum_field :trail, [
|
32
|
+
['Pacific Crest Trail', 'pct'],
|
33
|
+
['Continental Divide Trail', 'cdt'],
|
34
|
+
['Superior Hiking Trail', 'sht']
|
35
|
+
], :validate => false
|
36
|
+
end
|
37
|
+
|
38
|
+
> hike = Hike.create(:trail => 'pct', :duration => 'long')
|
39
|
+
|
40
|
+
### Confirm Values
|
41
|
+
|
42
|
+
> hike.trail_sht?
|
43
|
+
=> false
|
44
|
+
|
45
|
+
> hike.trail_pct?
|
46
|
+
=> true
|
47
|
+
|
48
|
+
> hike.duration_long?
|
49
|
+
=> true
|
50
|
+
|
51
|
+
> hike.duration_short?
|
52
|
+
=> false
|
53
|
+
|
54
|
+
### Display Selected Value
|
55
|
+
|
56
|
+
> hike.trail_display
|
57
|
+
=> "Pacific Crest Trail"
|
58
|
+
|
59
|
+
> hike.duration_display
|
60
|
+
=> "Really, really long"
|
61
|
+
|
62
|
+
### Validation
|
63
|
+
|
64
|
+
> hike.valid?
|
65
|
+
=> true
|
66
|
+
|
67
|
+
> hike.duration = 'forever'
|
68
|
+
> hike.valid?
|
69
|
+
=> false
|
70
|
+
|
71
|
+
### List Available Values
|
72
|
+
|
73
|
+
> hike.trail_values # useful to provide to options_for_select when constructing forms
|
74
|
+
=> [['Pacific Crest Trail', 'pct'], ['Continental Divide Trail', 'cdt'], ['Superior Hiking Trail', 'sht']]
|
75
|
+
> Hike.trail_values # or get it from the class instead of the instance, if you like
|
76
|
+
=> [['Pacific Crest Trail', 'pct'], ['Continental Divide Trail', 'cdt'], ['Superior Hiking Trail', 'sht']]
|
77
|
+
|
78
|
+
### Use Constants for Keys
|
79
|
+
|
80
|
+
> Hike::TRAIL_PCT
|
81
|
+
=> :pct
|
82
|
+
> Hike::TRAIL_SHT
|
83
|
+
=> :sht
|
84
|
+
|
85
|
+
These methods are all prefixed with the field name by design, which allows multiple fields on a model to exist which potentially have the same values.
|
86
|
+
|
87
|
+
## TESTS
|
88
|
+
|
89
|
+
Run tests with `bundle exec rake test` (or just `rake test` if you're
|
90
|
+
daring).
|
91
|
+
|
92
|
+
## TODO
|
93
|
+
|
94
|
+
* Provide any support needed for defining columns on MySQL databases as enum columns instead of string columns.
|
data/Rakefile
CHANGED
data/enumerated_field.gemspec
CHANGED
@@ -18,4 +18,11 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency('activemodel')
|
23
|
+
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
s.add_development_dependency 'bundler'
|
26
|
+
s.add_development_dependency 'turn'
|
27
|
+
s.add_development_dependency 'shoulda'
|
21
28
|
end
|
data/lib/enumerated_field.rb
CHANGED
@@ -9,14 +9,20 @@ module EnumeratedField
|
|
9
9
|
# ex. enum_field(:league, [['National Football League', :nfl], ['Major League Baseball', :mlb]])
|
10
10
|
# field_name typically corresponds to the database column name
|
11
11
|
# values_array is a double array (not a hash to preserve order for when order matters.. ie select options)
|
12
|
-
def enum_field(field_name, values_array)
|
12
|
+
def enum_field(field_name, values_array, options = {})
|
13
13
|
values_hash = {}
|
14
14
|
values_array.each { |value, key| values_hash[key] = value }
|
15
|
-
|
16
|
-
|
15
|
+
default_options = {
|
16
|
+
:validate => true,
|
17
|
+
:allow_nil => false,
|
18
|
+
:allow_blank => false,
|
19
|
+
}
|
20
|
+
options = default_options.merge(options)
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
# returns the values_array for this field, useful for providing to
|
23
|
+
# options_for_select when constructing forms
|
24
|
+
enumerated_class = class << self; self; end
|
25
|
+
enumerated_class.instance_eval do
|
20
26
|
define_method("#{field_name}_values") do |*options|
|
21
27
|
options = options.first || {}
|
22
28
|
if options[:first_option]
|
@@ -25,6 +31,23 @@ module EnumeratedField
|
|
25
31
|
values_array
|
26
32
|
end
|
27
33
|
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class_eval do
|
37
|
+
|
38
|
+
unless options[:validate] == false
|
39
|
+
validates field_name, :inclusion => values_hash.keys,
|
40
|
+
:allow_nil => options[:allow_nil], :allow_blank => options[:allow_blank]
|
41
|
+
end
|
42
|
+
|
43
|
+
values_hash.each do |key, value|
|
44
|
+
const_name = "#{field_name}_#{key}".upcase.gsub(/[^\w_]/, "_").to_sym
|
45
|
+
const_set(const_name, key)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method("#{field_name}_values") do |*options|
|
49
|
+
self.class.send("#{field_name}_values", *options)
|
50
|
+
end
|
28
51
|
|
29
52
|
# returns display value for the current value of the field
|
30
53
|
define_method("#{field_name}_display") do
|
@@ -35,7 +58,7 @@ module EnumeratedField
|
|
35
58
|
define_method("#{field_name}_display_for") do |key|
|
36
59
|
values_hash[key]
|
37
60
|
end
|
38
|
-
|
61
|
+
|
39
62
|
define_method("#{field_name}_value_for") do |key|
|
40
63
|
values_hash.invert[key]
|
41
64
|
end
|
@@ -51,6 +74,6 @@ module EnumeratedField
|
|
51
74
|
end
|
52
75
|
|
53
76
|
end
|
54
|
-
|
77
|
+
|
55
78
|
end
|
56
79
|
|
@@ -5,50 +5,127 @@ class Apple
|
|
5
5
|
|
6
6
|
attr_accessor :color, :kind
|
7
7
|
|
8
|
-
enum_field :color, [['Red', :red], ['Green', :green]]
|
9
|
-
enum_field :kind, [['Fuji Apple', :fuji], ['Delicious Red Apple', :delicious]]
|
8
|
+
enum_field :color, [['Red', :red], ['Green', :green]], :validate => false
|
9
|
+
enum_field :kind, [['Fuji Apple', :fuji], ['Delicious Red Apple', :delicious]], :validate => false
|
10
10
|
|
11
11
|
def initialize(color, kind)
|
12
12
|
self.color = color
|
13
13
|
self.kind = kind
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
end
|
17
17
|
|
18
|
-
class
|
18
|
+
class Banana
|
19
|
+
include EnumeratedField
|
20
|
+
include ActiveModel::Validations
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
22
|
+
attr_accessor :brand
|
23
|
+
attr_accessor :color
|
24
|
+
attr_accessor :tastiness
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
enum_field :brand, [["Chiquita", :chiquita], ["Del Monte", :delmonte]]
|
27
|
+
enum_field :color, [["Awesome Yellow", :yellow], ["Icky Green", :green]], :allow_nil => true
|
28
|
+
# stressing the constantizing of the keys
|
29
|
+
enum_field :tastiness, [
|
30
|
+
["Great", "great!"],
|
31
|
+
["Good", "it's good"],
|
32
|
+
["Bad", "hate-hate"],
|
33
|
+
], :validate => false
|
29
34
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
assert apple.kind_display, 'Delicious Red Apple'
|
35
|
+
def initialize(brand, color)
|
36
|
+
self.brand = brand
|
37
|
+
self.color = color
|
34
38
|
end
|
39
|
+
end
|
35
40
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
class EnumeratedFieldTest < Test::Unit::TestCase
|
42
|
+
|
43
|
+
context 'EnumeratedField class' do
|
44
|
+
|
45
|
+
should 'have the color_values method' do
|
46
|
+
assert_equal Apple.color_values.length, 2
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'have 2 values without first option' do
|
50
|
+
assert_equal Apple.color_values.length, 2
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'have 3 values with first option' do
|
54
|
+
assert_equal Apple.color_values(:first_option => "Select Color").length, 3
|
55
|
+
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
57
|
+
should 'create contstants for the field keys' do
|
58
|
+
assert_equal :chiquita, Banana::BRAND_CHIQUITA
|
59
|
+
assert_equal :delmonte, Banana::BRAND_DELMONTE
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'create underscored constants from field keys which contain invalid constant name characters' do
|
63
|
+
assert_equal "great!", Banana::TASTINESS_GREAT_
|
64
|
+
assert_equal "it's good", Banana::TASTINESS_IT_S_GOOD
|
65
|
+
assert_equal "hate-hate", Banana::TASTINESS_HATE_HATE
|
66
|
+
end
|
47
67
|
end
|
48
68
|
|
49
|
-
|
50
|
-
|
51
|
-
|
69
|
+
context 'EnumeratedField instance' do
|
70
|
+
|
71
|
+
setup do
|
72
|
+
@red_apple = Apple.new(:red, :fuji)
|
73
|
+
@green_apple = Apple.new(:green, :delicious)
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'have color_display method' do
|
77
|
+
assert_equal @red_apple.color_display, 'Red'
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'show Green for color_display of green' do
|
81
|
+
assert_equal @red_apple.color_display_for(:green), 'Green'
|
82
|
+
end
|
83
|
+
|
84
|
+
should 'have two enum fields in one class' do
|
85
|
+
assert_equal @green_apple.color_display, 'Green'
|
86
|
+
assert_equal @green_apple.kind_display, 'Delicious Red Apple'
|
87
|
+
end
|
88
|
+
|
89
|
+
should 'have valid question methods' do
|
90
|
+
assert @green_apple.color_green?
|
91
|
+
assert !@green_apple.color_red?
|
92
|
+
assert @green_apple.kind_delicious?
|
93
|
+
assert !@green_apple.kind_fuji?
|
94
|
+
end
|
95
|
+
|
96
|
+
should 'have 2 values without first option' do
|
97
|
+
assert_equal @red_apple.color_values.length, 2
|
98
|
+
end
|
99
|
+
|
100
|
+
should 'have 3 values with first option' do
|
101
|
+
assert_equal @red_apple.color_values(:first_option => "Select Color").length, 3
|
102
|
+
end
|
103
|
+
|
52
104
|
end
|
53
105
|
|
54
|
-
|
106
|
+
context 'Validation' do
|
107
|
+
should 'occur by default' do
|
108
|
+
# valid choice
|
109
|
+
banana = Banana.new(:chiquita, :green)
|
110
|
+
assert banana.valid?
|
111
|
+
|
112
|
+
# invalid choice
|
113
|
+
bad_banana = Banana.new(:penzoil, :orange)
|
114
|
+
assert !bad_banana.valid?
|
115
|
+
assert_equal ["is not included in the list"], bad_banana.errors[:brand]
|
116
|
+
assert_equal ["is not included in the list"], bad_banana.errors[:color]
|
117
|
+
|
118
|
+
# invalid choice (brand doesn't allow nil, color does)
|
119
|
+
nil_banana = Banana.new(nil, nil)
|
120
|
+
assert !nil_banana.valid?
|
121
|
+
assert_equal ["is not included in the list"], nil_banana.errors[:brand]
|
122
|
+
assert_equal [], nil_banana.errors[:color]
|
123
|
+
end
|
124
|
+
|
125
|
+
should 'not occur if passed :validate => false' do
|
126
|
+
# no validations, accepts any choice
|
127
|
+
apple = Apple.new(:orange, :macintosh)
|
128
|
+
assert !apple.respond_to?(:valid)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enumerated_field
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Luke Ludwig
|
@@ -15,10 +15,79 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-07-20 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
name: activemodel
|
32
|
+
type: :runtime
|
33
|
+
prerelease: false
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
name: rake
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
requirement: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
name: bundler
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
requirement: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
name: turn
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
requirement: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
name: shoulda
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
requirement: *id005
|
22
91
|
description: EnumeratedField is a library that provides some nice methods when a string column is used like an enumeration, meaning there is a list of allowable values for the string column.
|
23
92
|
email:
|
24
93
|
- luke.ludwig@tstmedia.com
|
@@ -31,7 +100,7 @@ extra_rdoc_files: []
|
|
31
100
|
files:
|
32
101
|
- .gitignore
|
33
102
|
- Gemfile
|
34
|
-
- README
|
103
|
+
- README.md
|
35
104
|
- Rakefile
|
36
105
|
- enumerated_field.gemspec
|
37
106
|
- lib/enumerated_field.rb
|
data/README
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
EnumeratedField is a library that provides some nice methods when a string column is used like an enumeration, meaning there is a list of allowable values for the string column. Typically you want the display value as seen by the end user to differ from the stored value, allowing you to easily change the display value at anytime without migrating data, and this little gem helps you with that.
|
2
|
-
|
3
|
-
Example Usage:
|
4
|
-
This example demonstrates usage within an ActiveRecord::Base object, however this gem does not depend on ActiveRecord.
|
5
|
-
|
6
|
-
class Hike < ActiveRecord::Base
|
7
|
-
|
8
|
-
include EnumeratedField
|
9
|
-
|
10
|
-
enum_field :trail, [['Pacific Crest Trail', 'pct'],
|
11
|
-
['Continental Divide Trail', 'cdt'],
|
12
|
-
['Superior Hiking Trail', 'sht']]
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
> hike = Hike.create(:trail => 'pct')
|
17
|
-
|
18
|
-
> hike.trail_sht?
|
19
|
-
=> false
|
20
|
-
|
21
|
-
> hike.trail_pct?
|
22
|
-
=> true
|
23
|
-
|
24
|
-
> hike.trail_display
|
25
|
-
=> "Pacific Crest Trail"
|
26
|
-
|
27
|
-
> hike.trail_values # useful to provide to options_for_select when constructing forms
|
28
|
-
=> [['Pacific Crest Trail', 'pct'], ['Continental Divide Trail', 'cdt'], ['Superior Hiking Trail', 'sht']]
|
29
|
-
|
30
|
-
|
31
|
-
These methods are all prefixed with the field name by design, which allows multiple fields on a model to exist which potentially have the same values.
|
32
|
-
|
33
|
-
Run tests by: ruby test/enumerated_field_test.rb
|
34
|
-
|
35
|
-
TODO:
|
36
|
-
* Provide option to enum_field to setup validation methods that validate the value of the field against the allowed values.
|
37
|
-
* Provide any support needed for defining columns on MySQL databases as enum columns instead of string columns.
|