├── .gitignore ├── MIT-LICENSE ├── README.rdoc ├── Rakefile ├── VERSION.yml ├── enum_field.gemspec ├── init.rb ├── lib └── enum_field.rb ├── rails └── init.rb └── test └── enum_field_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .DS_Store 3 | coverage 4 | pkg 5 | rdoc 6 | 7 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 James Golick 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = EnumField 2 | 3 | Macro to emulate a MySQL enum_field type thing. 4 | 5 | == Usage 6 | 7 | This plugin encapsulates a validates_inclusion_of and automatically gives you a 8 | few more goodies automatically. That's it! 9 | 10 | class Computer < ActiveRecord:Base 11 | enum_field :status, ['on', 'off', 'standby', 'sleep', 'out of this world'] 12 | 13 | # Optionally with a message to replace the default one 14 | # enum_field :status, ['on', 'off', 'standby', 'sleep', 'out of this world'], :message => "incorrect status" 15 | 16 | #... 17 | end 18 | 19 | This will give you a few things: 20 | 21 | - add a validates_inclusion_of with a simple error message ("invalid #{field}") or your custom message 22 | - define the following query methods, in the name of expressive code: 23 | - on? 24 | - off? 25 | - standby? 26 | - sleep? 27 | - out_of_this_world? 28 | - define the STATUSES constant, which contains the acceptable values 29 | 30 | = License 31 | 32 | Copyright (c) 2008 James Golick, released under the MIT license 33 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/rdoctask' 4 | 5 | desc 'Default: run unit tests.' 6 | task :default => :test 7 | 8 | desc 'Test the enum_field plugin.' 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << 'lib' 11 | t.pattern = 'test/**/*_test.rb' 12 | t.verbose = true 13 | end 14 | 15 | begin 16 | require 'jeweler' 17 | Jeweler::Tasks.new do |s| 18 | s.name = "enum_field" 19 | s.summary = %Q{ActiveRecord enum fields on steroid} 20 | s.email = "james@giraffesoft.ca" 21 | s.homepage = "http://github.com/giraffesoft/enum_field" 22 | s.description = "enum_field encapsulates a bunch of common idioms around ActiveRecord validates_inclusion_of" 23 | s.authors = ["James Golick", "Mathieu Martin"] 24 | end 25 | rescue LoadError 26 | puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" 27 | end 28 | 29 | desc 'Generate documentation for the enum_field plugin.' 30 | Rake::RDocTask.new(:rdoc) do |rdoc| 31 | rdoc.rdoc_dir = 'rdoc' 32 | rdoc.title = 'enum_field' 33 | rdoc.options << '--line-numbers' << '--inline-source' 34 | rdoc.rdoc_files.include('README*') 35 | rdoc.rdoc_files.include('lib/**/*.rb') 36 | end 37 | 38 | begin 39 | require 'rcov/rcovtask' 40 | Rcov::RcovTask.new do |t| 41 | t.libs << 'test' 42 | t.test_files = FileList['test/**/*_test.rb'] 43 | t.verbose = true 44 | end 45 | rescue LoadError 46 | puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" 47 | end 48 | 49 | -------------------------------------------------------------------------------- /VERSION.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :patch: 0 3 | :major: 0 4 | :minor: 2 5 | -------------------------------------------------------------------------------- /enum_field.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{enum_field} 5 | s.version = "0.2.0" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["James Golick", "Mathieu Martin"] 9 | s.date = %q{2009-06-26} 10 | s.description = %q{enum_field encapsulates a bunch of common idioms around ActiveRecord validates_inclusion_of} 11 | s.email = %q{james@giraffesoft.ca} 12 | s.extra_rdoc_files = [ 13 | "README.rdoc" 14 | ] 15 | s.files = [ 16 | ".gitignore", 17 | "MIT-LICENSE", 18 | "README.rdoc", 19 | "Rakefile", 20 | "VERSION.yml", 21 | "enum_field.gemspec", 22 | "init.rb", 23 | "lib/enum_field.rb", 24 | "rails/init.rb", 25 | "test/enum_field_test.rb" 26 | ] 27 | s.has_rdoc = true 28 | s.homepage = %q{http://github.com/giraffesoft/enum_field} 29 | s.rdoc_options = ["--charset=UTF-8"] 30 | s.require_paths = ["lib"] 31 | s.rubygems_version = %q{1.3.1} 32 | s.summary = %q{ActiveRecord enum fields on steroid} 33 | s.test_files = [ 34 | "test/enum_field_test.rb" 35 | ] 36 | 37 | if s.respond_to? :specification_version then 38 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 39 | s.specification_version = 2 40 | 41 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 42 | else 43 | end 44 | else 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Base.class_eval { include EnumField } 2 | -------------------------------------------------------------------------------- /lib/enum_field.rb: -------------------------------------------------------------------------------- 1 | module EnumField 2 | def self.included(klass) 3 | klass.class_eval { extend EnumField::ClassMethods } 4 | end 5 | 6 | module ClassMethods 7 | # enum_field encapsulates a validates_inclusion_of and automatically gives you a 8 | # few more goodies automatically. 9 | # 10 | # class Computer < ActiveRecord:Base 11 | # enum_field :status, ['on', 'off', 'standby', 'sleep', 'out of this world'] 12 | # 13 | # # Optionally with a message to replace the default one 14 | # # enum_field :status, ['on', 'off', 'standby', 'sleep'], :message => "incorrect status" 15 | # 16 | # #... 17 | # end 18 | # 19 | # This will give you a few things: 20 | # 21 | # - add a validates_inclusion_of with a simple error message ("invalid #{field}") or your custom message 22 | # - define the following query methods, in the name of expressive code: 23 | # - on? 24 | # - off? 25 | # - standby? 26 | # - sleep? 27 | # - out_of_this_world? 28 | # - define the STATUSES constant, which contains the acceptable values 29 | def enum_field(field, possible_values, options={}) 30 | message = options[:message] || "invalid #{field}" 31 | const_set field.to_s.pluralize.upcase, possible_values unless const_defined?(field.to_s.pluralize.upcase) 32 | 33 | possible_values.each do |current_value| 34 | method_name = current_value.downcase.gsub(/[-\s]/, '_') 35 | define_method("#{method_name}?") do 36 | self.send(field) == current_value 37 | end 38 | end 39 | 40 | validates_inclusion_of field, :in => possible_values, :message => message 41 | end 42 | end 43 | end 44 | 45 | ActiveRecord::Base.class_eval { include EnumField } -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Base.class_eval { include EnumField } 2 | 3 | -------------------------------------------------------------------------------- /test/enum_field_test.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.reject! { |path| path.include?('TextMate') } 2 | require 'test/unit' 3 | require 'rubygems' 4 | require 'active_support' 5 | require 'mocha' 6 | require 'shoulda' 7 | require 'active_record' 8 | require File.dirname(__FILE__)+'/../lib/enum_field' 9 | require File.dirname(__FILE__)+'/../init' 10 | 11 | class MockedModel; include EnumField; end; 12 | 13 | class EnumFieldTest < Test::Unit::TestCase 14 | context "with a simple gender enum" do 15 | setup do 16 | @possible_values = %w( male female ) 17 | MockedModel.expects(:validates_inclusion_of).with(:gender, :in => @possible_values, :message => "invalid gender") 18 | MockedModel.send(:enum_field, :gender, @possible_values) 19 | end 20 | 21 | should "create constant with possible values named as pluralized field" do 22 | assert_equal @possible_values, MockedModel::GENDERS 23 | end 24 | 25 | should "create query methods for each enum type" do 26 | model = MockedModel.new 27 | 28 | model.stubs(:gender).returns("male") 29 | assert model.male? 30 | assert !model.female? 31 | model.stubs(:gender).returns("female") 32 | assert !model.male? 33 | assert model.female? 34 | end 35 | 36 | should "extend active record base with method" do 37 | assert_respond_to ActiveRecord::Base, :enum_field 38 | end 39 | end 40 | 41 | context "Specifying a message" do 42 | setup do 43 | @possible_values = %w(on off) 44 | MockedModel.expects(:validates_inclusion_of).with(:status, :in => @possible_values, :message => "custom insult") 45 | end 46 | 47 | should "override the default message" do 48 | MockedModel.send(:enum_field, :status, @possible_values, :message => 'custom insult') 49 | end 50 | end 51 | 52 | context "With an enum containing multiple word choices" do 53 | setup do 54 | MockedModel.stubs(:validates_inclusion_of) 55 | MockedModel.send :enum_field, :field, ['choice one', 'choice-two', 'other'] 56 | @model = MockedModel.new 57 | end 58 | 59 | should "define an underscored query method for the multiple word choice" do 60 | assert_respond_to @model, :choice_one? 61 | end 62 | 63 | should "define an underscored query method for the dasherized choice" do 64 | assert_respond_to @model, :choice_two? 65 | end 66 | end 67 | 68 | context "With an enum containing mixed case choices" do 69 | setup do 70 | MockedModel.stubs(:validates_inclusion_of) 71 | MockedModel.send :enum_field, :field, ['Choice One', 'ChoiceTwo', 'Other'] 72 | @model = MockedModel.new 73 | end 74 | 75 | should "define a lowercase, underscored query method for the multiple word choice" do 76 | assert_respond_to @model, :choice_one? 77 | end 78 | 79 | should "define a lowercase query method for the camelcase choice" do 80 | assert_respond_to @model, :choicetwo? 81 | end 82 | end 83 | 84 | context "With an enum containing strange characters" do 85 | setup do 86 | MockedModel.stubs(:validates_inclusion_of) 87 | MockedModel.send :enum_field, :field, ['choice%one', 'choice☺two', 'other.'] 88 | @model = MockedModel.new 89 | end 90 | 91 | should "define a normal query method for the unicode choice" do 92 | assert_respond_to @model, :choice_two? 93 | end 94 | 95 | should "define a normal query method for the % choice" do 96 | assert_respond_to @model, :choice_one? 97 | end 98 | 99 | should "define a query method without the trailing punctuation for the other choice" do 100 | assert_respond_to @model, :other? 101 | end 102 | end 103 | end 104 | 105 | --------------------------------------------------------------------------------