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 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
@@ -1,2 +1,10 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.test_files = FileList['test/*_test.rb']
8
+ test.verbose = true
9
+ # test.warning = true
10
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module EnumeratedField
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
- class_eval do
15
+ default_options = {
16
+ :validate => true,
17
+ :allow_nil => false,
18
+ :allow_blank => false,
19
+ }
20
+ options = default_options.merge(options)
17
21
 
18
- # returns the values_array for this field, useful for providing to
19
- # options_for_select when constructing forms
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 EnumeratedFieldTest < Test::Unit::TestCase
18
+ class Banana
19
+ include EnumeratedField
20
+ include ActiveModel::Validations
19
21
 
20
- def test_color_display
21
- apple = Apple.new(:red, :fuji)
22
- assert apple.color_display, 'Red'
23
- end
22
+ attr_accessor :brand
23
+ attr_accessor :color
24
+ attr_accessor :tastiness
24
25
 
25
- def test_color_display_for
26
- apple = Apple.new(:red, :fuji)
27
- assert apple.color_display_for(:green), 'Green'
28
- end
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 test_two_enum_fields_in_one_class
31
- apple = Apple.new(:green, :delicious)
32
- assert apple.color_display, 'Green'
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
- def test_question_methods
37
- apple = Apple.new(:green, :delicious)
38
- assert apple.color_green?
39
- assert !apple.color_red?
40
- assert apple.kind_delicious?
41
- assert !apple.kind_fuji?
42
- end
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
- def test_values_without_first_option
45
- apple = Apple.new(:red, :fuji)
46
- assert apple.color_values.length, 2
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
- def test_values_with_first_option
50
- apple = Apple.new(:red, :fuji)
51
- assert apple.color_values(:first_option => "Select Color").length, 3
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
- end
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
@@ -1,2 +1,5 @@
1
- require "test/unit"
2
- require "lib/enumerated_field"
1
+ require 'test/unit'
2
+ require 'turn'
3
+ require 'shoulda'
4
+ require 'lib/enumerated_field'
5
+ require "active_model"
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: 29
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
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-06-21 00:00:00 -05:00
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.