diff --git a/README.rdoc b/README.rdoc index 1b31511..d10ac96 100644 --- a/README.rdoc +++ b/README.rdoc @@ -9,7 +9,7 @@ Enum values are stored in memory at the moment but I plan to add database and po From version 0.7 onwards this plugin will only have Rails 3 support. If you need Rails 2 compatibility, {check the 0.6 branch}[https://github.com/adzap/active_enum/tree/0.6]. - gem install active_enum + gem install active_enum Put this in your Gemfile @@ -43,6 +43,17 @@ Define using implicit id values value :name => 'Female' end +Define using symbol ids + + class Sex < ActiveEnum::Base + value :id => :m, :name => 'Male' + value :id => :f, :name => 'Female' + end + +*NOTE* +If you are using ids as symbols, name cannot be used as symbols in all methods. Symbolized name using works only with +integer ids. + Beware that if you change the order of values defined in an enum which don't have explicit ids, then the ids will change. This could corrupt your data if the enum values have been stored in a model record, as they will no longer map to the original enum. @@ -64,6 +75,18 @@ Enum class usage Sex.meta(1) # => { :symbol => '♂' } Sex.to_select # => [['Male', 1], ['Female',2]] for select form helpers +When using symbol ids + + class Sex < ActiveEnum::Base + value :id => :m, :name => 'Male', :symbol => '♂' + value :id => :f, :name => 'Female', :symbol => '♀' + end + + Sex[:m] # => 'Male' + Sex["Male"] # => :m + Sex[:male] # => nil + + === Ordering To define the sorting of returned values use the order method. Which is useful for to_select method. @@ -149,6 +172,14 @@ You can check if the attribute value matches a particular enum value by passing user.sex?('Male') # => true user.sex?('Female') # => false +If you are using symbol ids + + user.sex?(:m) # => true + user.sex?('Male') # => true + user.sex?(:male) # => false + user.sex?(:f) # => false + + === Enum lookup A convenience method on the class is available to the enum class of any enumerated attribute @@ -213,7 +244,7 @@ Or, SimpleForm: The input type will be automatically detected for enumerated attributes. You can override with the :as option as normal. -== Storage Backends +== Storage Backends The design allows pluggable backends to be used for stories and retrieving the enum values. At present there are two available, memory or I18n. To change the storage engine you alter the storage config value like so: @@ -228,7 +259,7 @@ The memory store is default obviously just stores the values in memory. === I18n Storage The I18n storage backend stores the ids and names is memory still, but retrieves the name text translation any call to -the enum public methods. +the enum public methods. To set up the locale file, run @@ -237,17 +268,17 @@ To set up the locale file, run This generates the YML locale template in your default language as configured in the application.rb. Here are some examples of defining enum translations - + class Sex < ActiveEnum::Base value 1 => 'male' value 2 => 'female' end - -becomes + +becomes sex: - male: Male - female: Female + male: Male + female: Female For namesapced enums in a model @@ -259,11 +290,11 @@ For namesapced enums in a model end nest the translations under the underscored model name - + person: sex: - male: Male - female: Female + male: Male + female: Female == License diff --git a/lib/active_enum/base.rb b/lib/active_enum/base.rb index 29bf7fb..2155af6 100644 --- a/lib/active_enum/base.rb +++ b/lib/active_enum/base.rb @@ -1,6 +1,7 @@ module ActiveEnum class DuplicateValue < StandardError; end class InvalidValue < StandardError; end + class InvalidId < StandardError; end class Base @@ -22,7 +23,7 @@ def value(enum_value) store.set *id_and_name_and_meta(enum_value) end - # Specify order enum values are returned. + # Specify order enum values are returned. # Allowed values are :asc, :desc or :natural # def order(order) @@ -55,13 +56,17 @@ def to_select # Access id or name value. Pass an id number to retrieve the name or # a symbol or string to retrieve the matching id. def get(index) - if index.is_a?(Fixnum) + if index.is_a?(Fixnum) || index.is_a?(Symbol) row = store.get_by_id(index) - row[1] if row - else + value = row[1] if row + end + + if (index.is_a?(String) || (index.is_a?(Symbol) && !symbol_ids?)) && value.nil? row = store.get_by_name(index) - row[0] if row + value = row[0] if row end + + value end alias_method :[], :get @@ -71,12 +76,21 @@ def include?(value) # Access any meta data defined for a given id or name. Returns a hash. def meta(index) - row = if index.is_a?(Fixnum) - store.get_by_id(index) - else - store.get_by_name(index) + if index.is_a?(Fixnum) || index.is_a?(Symbol) + row = store.get_by_id(index) + value = row[2] if row end - row[2] || {} if row + + if (index.is_a?(String) || (index.is_a?(Symbol) && !symbol_ids?)) && value.nil? + row = store.get_by_name(index) + value = row[2] if row + end + + value || {} + end + + def symbol_ids? + store.symbol_ids? end private diff --git a/lib/active_enum/extensions.rb b/lib/active_enum/extensions.rb index 290ed7b..f50cfab 100644 --- a/lib/active_enum/extensions.rb +++ b/lib/active_enum/extensions.rb @@ -81,9 +81,13 @@ def define_active_enum_read_method(attribute) class_eval <<-DEF def #{attribute}(arg=nil) value = super() - return if value.nil? && arg.nil? - enum = self.class.active_enum_for(:#{attribute}) + + + return if arg.nil? && (value.nil? || (enum.symbol_ids? && value.empty?)) + + value = value.to_sym if enum.symbol_ids? && !value.nil? + case arg when nil #{ActiveEnum.use_name_as_value ? 'enum[value]' : 'value' } @@ -100,7 +104,7 @@ def #{attribute}(arg=nil) DEF end - # Define write method to also handle enum value + # Define write method to also handle enum value (only integer ids) # # Examples: # user.sex = 1 @@ -109,8 +113,9 @@ def #{attribute}(arg=nil) def define_active_enum_write_method(attribute) class_eval <<-DEF def #{attribute}=(arg) - if arg.is_a?(Symbol) - super self.class.active_enum_for(:#{attribute})[arg] + enum = self.class.active_enum_for(:#{attribute}) + if arg.is_a?(Symbol) and !enum.symbol_ids? + super enum[arg] else super arg end @@ -127,7 +132,12 @@ def define_active_enum_question_method(attribute) class_eval <<-DEF def #{attribute}?(arg=nil) if arg - self.#{attribute}(:id) == self.class.active_enum_for(:#{attribute})[arg] + enum = self.class.active_enum_for(:#{attribute}) + if enum.symbol_ids? and arg.is_a?(Symbol) + self.#{attribute}(:id) == arg + else + self.#{attribute}(:id) == enum[arg] + end else super() end diff --git a/lib/active_enum/storage/memory_store.rb b/lib/active_enum/storage/memory_store.rb index fd34cba..d66f4d2 100644 --- a/lib/active_enum/storage/memory_store.rb +++ b/lib/active_enum/storage/memory_store.rb @@ -1,8 +1,14 @@ module ActiveEnum module Storage class MemoryStore < AbstractStore + attr_accessor :symbol_ids def set(id, name, meta=nil) + raise ActiveEnum::InvalidId, 'Id cannot be string.' if id.is_a? String + raise ActiveEnum::InvalidId, 'Id types cannot be mixed.' unless symbol_ids.nil? or symbol_ids? == id.is_a?(Symbol) + + self.symbol_ids = id.is_a?(Symbol) if self.symbol_ids.nil? + check_duplicate id, name _values << [id, name.to_s, meta].compact sort! @@ -35,6 +41,9 @@ def _values @_values ||= [] end + def symbol_ids? + !!symbol_ids + end end end end diff --git a/spec/active_enum/base_spec.rb b/spec/active_enum/base_spec.rb index 98c3df5..dcd9815 100644 --- a/spec/active_enum/base_spec.rb +++ b/spec/active_enum/base_spec.rb @@ -60,6 +60,13 @@ class NewEnum < ActiveEnum::Base; end enum.all.should == [[1,'Name']] end + it 'should allow me to define id as a symbol' do + enum = define_enum do + value :id => :one, :name => 'Name' + end + enum.all.should == [[:one,'Name']] + end + it 'should allow to define meta data value with extra key value pairs' do enum = define_enum do value :id => 1, :name => 'Name', :description => 'extra' @@ -75,7 +82,7 @@ class NewEnum < ActiveEnum::Base; end enum.all.should == [[1,'Name 1'], [2, 'Name 2']] end - it 'should raise error if the id is a duplicate' do + it 'should raise error if the integer id is a duplicate' do expect { define_enum do value :id => 1, :name => 'Name 1' @@ -84,6 +91,15 @@ class NewEnum < ActiveEnum::Base; end }.to raise_error(ActiveEnum::DuplicateValue) end + it 'should raise error if the symbol id is a duplicate' do + expect { + define_enum do + value :id => :name, :name => 'Name 1' + value :id => :name, :name => 'Name 2' + end + }.to raise_error(ActiveEnum::DuplicateValue) + end + it 'should raise error if the name is a duplicate' do expect { define_enum do @@ -92,16 +108,64 @@ class NewEnum < ActiveEnum::Base; end end }.to raise_error(ActiveEnum::DuplicateValue) end + + it 'should raise error if the id is a string' do + expect { + define_enum do + value :id => 'bad', :name => 'Name' + end + }.to raise_error(ActiveEnum::InvalidId) + end + + it 'should raise error if mixed id types is used' do + expect { + define_enum do + value :id => :sym, :name => 'Name 1' + value :id => 2, :name => 'Name 2' + end + }.to raise_error(ActiveEnum::InvalidId) + + expect { + define_enum do + value :id => 1, :name => 'Name 1' + value :id => :two, :name => 'Name 2' + end + }.to raise_error(ActiveEnum::InvalidId) + end end describe ".meta" do - it 'should return meta values hash for a given index value' do + it 'should return meta values hash for a given integer index value' do enum = define_enum do value :id => 1, :name => 'Name', :description => 'extra' end enum.meta(1).should == {:description => 'extra'} end + it 'should return meta values hash for a given value' do + enum = define_enum do + value :id => 1, :name => 'Name 1', :description => 'extra' + end + enum.meta('Name 1').should == {:description => 'extra'} + enum.meta(:name_1).should == {:description => 'extra'} + end + + it 'should return meta values hash for a given symbol index value' do + enum = define_enum do + value :id => :one, :name => 'Name 1', :description => 'extra' + end + enum.meta(:one).should == {:description => 'extra'} + end + + it 'should not match value by symbol when symbol ids used' do + enum = define_enum do + value :id => :one, :name => 'Name 1', :description => 'extra' + end + + enum.meta('Name 1').should == {:description => 'extra'} + enum.meta(:name_1).should == {} + end + it 'should return empty hash for index with no meta defined' do enum = define_enum do value :id => 1, :name => 'Name' @@ -161,24 +225,58 @@ class NewEnum < ActiveEnum::Base; end end context "element reference method" do - let(:enum) { - define_enum do - value :id => 1, :name => 'Name 1' - value :id => 2, :name => 'Name 2' + context 'integer ids' do + let(:enum) { + define_enum do + value :id => 1, :name => 'Name 1' + value :id => 2, :name => 'Name 2' + end + } + + it 'should return name when given an id' do + enum[1].should == 'Name 1' end - } - it 'should return name when given an id' do - enum[1].should == 'Name 1' - end + it 'should return id when given a name' do + enum['Name 1'].should == 1 + end - it 'should return id when given a name' do - enum['Name 1'].should == 1 + it 'should return id when given a symbol of the name' do + enum[:Name_1].should == 1 + enum[:name_1].should == 1 + end end - it 'should return id when given a symbol of the name' do - enum[:Name_1].should == 1 - enum[:name_1].should == 1 + context 'symbol ids' do + let(:enum) { + define_enum do + value :id => :one, :name => 'Name 1' + value :id => :two, :name => 'Name 2' + end + } + + it 'should return name when given an id' do + enum[:one].should == 'Name 1' + end + + it 'should return id when given a name' do + enum['Name 1'].should == :one + end + + context 'ids and names are similar' do + let(:enum) { + define_enum do + value :id => :one, :name => 'two' + value :id => :two, :name => 'three' + end + } + + it 'should not match by symbolized value' do + enum[:one].should == 'two' + enum[:two].should == 'three' + enum[:three].should be_nil + end + end end end diff --git a/spec/active_enum/extensions_spec.rb b/spec/active_enum/extensions_spec.rb index 49f0420..d5b5741 100644 --- a/spec/active_enum/extensions_spec.rb +++ b/spec/active_enum/extensions_spec.rb @@ -1,17 +1,6 @@ require "spec_helper" describe ActiveEnum::Extensions do - class Sex < ActiveEnum::Base - value :id => 1, :name => 'Male' - value :id => 2, :name => 'Female' - end - - class Accepted < ActiveEnum::Base - value :id => 0, :name => 'No' - value :id => 1, :name => 'Definitely' - value :id => 2, :name => 'Maybe' - end - it 'should add class :enumerate method to ActiveRecord' do ActiveRecord::Base.should respond_to(:enumerate) end @@ -31,7 +20,7 @@ class Accepted < ActiveEnum::Base Person.enumerate :sex, :with => Sex Person.enumerate :attending, :with => Accepted - Person.active_enum_for(:sex).should == Sex + Person.active_enum_for(:sex).should == Sex Person.active_enum_for(:attending).should == Accepted end @@ -54,157 +43,348 @@ class Accepted < ActiveEnum::Base }.to raise_error(ActiveEnum::EnumNotFound) end - context "attribute" do - let(:person) { Person.new(:sex => 1) } - + context "integer ids" do before(:all) do - reset_class Person do - enumerate :sex, :with => Sex + reset_class Sex do + value :id => 1, :name => 'Male' + value :id => 2, :name => 'Female' end - end - context "with value" do - it 'should return value with no arg' do - person.sex.should == 1 + reset_class Accepted do + value :id => 0, :name => 'No' + value :id => 1, :name => 'Definitely' + value :id => 2, :name => 'Maybe' end + end - it 'should return enum id for value' do - person.sex(:id).should == 1 - end + context "attribute" do + let(:person) { Person.new(:sex => 1) } - it 'should return enum name for value' do - person.sex(:name).should == 'Male' + before(:all) do + reset_class Person do + enumerate :sex, :with => Sex + end end - it 'should return enum class for attribute' do - person.sex(:enum).should == Sex + context "with value" do + it 'should return value with no arg' do + person.sex.should == 1 + end + + it 'should return enum id for value' do + person.sex(:id).should == 1 + end + + it 'should return enum name for value' do + person.sex(:name).should == 'Male' + end + + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end end - end - context "with nil value" do - let(:person) { Person.new(:sex => nil) } + context "with nil value" do + let(:person) { Person.new(:sex => nil) } + + it 'should return nil with no arg' do + person.sex.should be_nil + end + + it 'should return nil enum id' do + person.sex(:id).should be_nil + end + + it 'should return nil enum name' do + person.sex(:name).should be_nil + end - it 'should return nil with no arg' do - person.sex.should be_nil + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end end - it 'should return nil enum id' do - person.sex(:id).should be_nil + context "with undefined value" do + let(:person) { Person.new(:sex => -1) } + let(:person1) { Person.new(:sex => '') } + + it 'should return value with no arg' do + person.sex.should == -1 + person1.sex.should == '' + end + + it 'should return nil enum id' do + person.sex(:id).should be_nil + person1.sex(:id).should be_nil + end + + it 'should return nil enum name' do + person.sex(:name).should be_nil + person1.sex(:name).should be_nil + end + + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + person1.sex(:enum).should == Sex + end end - it 'should return nil enum name' do - person.sex(:name).should be_nil + context "with meta data" do + let(:person) { Person.new(:sex =>1) } + + before(:all) do + reset_class Person do + enumerate :sex do + value :id => 1, :name => 'Male', :description => 'Man' + value :id => 2, :name => 'Female', :description => 'Woman' + end + end + end + + it 'should return meta value for existing key' do + person.sex(:description).should == 'Man' + end + + it 'should return nil for missing meta value' do + person.sex(:nonexistent).should be_nil + end + + it 'should return nil for missing index' do + person.sex = nil + person.sex(:description).should be_nil + end end - it 'should return enum class for attribute' do - person.sex(:enum).should == Sex + context "question method" do + it 'should return normal value without arg' do + person.sex?.should be_true + person.sex = nil + person.sex?.should be_false + end + + it 'should return true if string name matches for id value' do + person.sex?("Male").should be_true + end + + it 'should return true if symbol name matches for id value' do + person.sex?(:male).should be_true + person.sex?(:Male).should be_true + end + + it 'should return false if name does not match for id value' do + person.sex?("Female").should be_false + person.sex?(:female).should be_false + person.sex?(:Female).should be_false + end end - end - context "with undefined value" do - let(:person) { Person.new(:sex => -1) } + context "with value as enum name symbol" do + + it 'should store id value when valid enum name' do + person.sex = :female + person.sex.should == 2 + end + + it 'should store nil value when invalid enum name' do + person.sex = :invalid + person.sex.should == nil + end - it 'should return value with no arg' do - person.sex.should == -1 end - it 'should return nil enum id' do - person.sex(:id).should be_nil + context "with value as enum name" do + before(:all) { ActiveEnum.use_name_as_value = true } + let(:person) { Person.new(:sex =>1) } + + before do + reset_class Person do + enumerate :sex, :with => Sex + end + end + + it 'should return text name value for attribute' do + person.sex.should == 'Male' + end + + it 'should return true for boolean match' do + person.sex?(:male).should be_true + end + + after(:all) { ActiveEnum.use_name_as_value = false } end - it 'should return nil enum name' do - person.sex(:name).should be_nil + end + end + + context 'string ids' do + before(:all) do + reset_class Sex do + value :id => :m, :name => 'Male' + value :id => :f, :name => 'Female' end - it 'should return enum class for attribute' do - person.sex(:enum).should == Sex + reset_class Accepted do + value :id => :no, :name => 'No' + value :id => :yes, :name => 'Definitely' + value :id => :mb, :name => 'Maybe' end end - context "with meta data" do - let(:person) { Person.new(:sex =>1) } + context "attribute" do + let(:person) { Person.new(:sex => :m) } before(:all) do reset_class Person do - enumerate :sex do - value :id => 1, :name => 'Male', :description => 'Man' - value :id => 2, :name => 'Female', :description => 'Woman' - end + enumerate :sex, :with => Sex end end - it 'should return meta value for existing key' do - person.sex(:description).should == 'Man' - end + context "with value" do + it 'should return value with no arg' do + person.sex.should == :m + end - it 'should return nil for missing meta value' do - person.sex(:nonexistent).should be_nil - end + it 'should return enum id for value' do + person.sex(:id).should == :m + end - it 'should return nil for missing index' do - person.sex = nil - person.sex(:description).should be_nil - end - end + it 'should return enum name for value' do + person.sex(:name).should == 'Male' + end - context "question method" do - it 'should return normal value without arg' do - person.sex?.should be_true - person.sex = nil - person.sex?.should be_false + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end end - it 'should return true if string name matches for id value' do - person.sex?("Male").should be_true - end + context "with nil value" do + let(:person) { Person.new(:sex => nil) } - it 'should return true if symbol name matches for id value' do - person.sex?(:male).should be_true - person.sex?(:Male).should be_true - end + it 'should return nil with no arg' do + person.sex.should be_nil + end + + it 'should return nil enum id' do + person.sex(:id).should be_nil + end + + it 'should return nil enum name' do + person.sex(:name).should be_nil + end + + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end + end + + context "with emty string value" do + let(:person) { Person.new(:sex => '') } - it 'should return false if name does not match for id value' do - person.sex?("Female").should be_false - person.sex?(:female).should be_false - person.sex?(:Female).should be_false + it 'should return value with no arg' do + person.sex.should be_nil + end + + it 'should return nil enum id' do + person.sex(:id).should be_nil + end + + it 'should return nil enum name' do + person.sex(:name).should be_nil + end + + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end end - end - context "with value as enum name symbol" do + context "with undefined value" do + let(:person) { Person.new(:sex => :a) } - it 'should store id value when valid enum name' do - person.sex = :female - person.sex.should == 2 + it 'should return value with no arg' do + person.sex.should == :a + end + + it 'should return nil enum id' do + person.sex(:id).should be_nil + end + + it 'should return nil enum name' do + person.sex(:name).should be_nil + end + + it 'should return enum class for attribute' do + person.sex(:enum).should == Sex + end end - it 'should store nil value when invalid enum name' do - person.sex = :invalid - person.sex.should == nil + context "with meta data" do + let(:person) { Person.new(:sex => :m) } + + before(:all) do + reset_class Person do + enumerate :sex do + value :id => :m, :name => 'Male', :description => 'Man' + value :id => :f, :name => 'Female', :description => 'Woman' + end + end + end + + it 'should return meta value for existing key' do + person.sex(:description).should == 'Man' + end + + it 'should return nil for missing meta value' do + person.sex(:nonexistent).should be_nil + end + + it 'should return nil for missing index' do + person.sex = nil + person.sex(:description).should be_nil + end end - end + context "question method" do + it 'should return normal value without arg' do + person.sex?.should be_true + person.sex = nil + person.sex?.should be_false + end - context "with value as enum name" do - before(:all) { ActiveEnum.use_name_as_value = true } - let(:person) { Person.new(:sex =>1) } + it 'should return true if arg matches for id value' do + person.sex?(:m).should be_true + end - before do - reset_class Person do - enumerate :sex, :with => Sex + it 'should return false if arg does not match for id value' do + person.sex?(:f).should be_false end - end - it 'should return text name value for attribute' do - person.sex.should == 'Male' + it 'should return true on matching value only by string' do + person.sex?('Male').should be_true + person.sex?(:male).should be_false + end end - it 'should return true for boolean match' do - person.sex?(:male).should be_true + context "with value as enum name" do + before(:all) { ActiveEnum.use_name_as_value = true } + let(:person) { Person.new(:sex => :m) } + + before do + reset_class Person do + enumerate :sex, :with => Sex + end + end + + it 'should return text name value for attribute' do + person.sex.should == 'Male' + end + + it 'should return true for boolean match' do + person.sex?(:m).should be_true + end + + after(:all) { ActiveEnum.use_name_as_value = false } end - after(:all) { ActiveEnum.use_name_as_value = false } end - end - end diff --git a/spec/active_enum/form_helpers/formtastic2_spec.rb b/spec/active_enum/form_helpers/formtastic2_spec.rb index 7ef3b79..7373b6f 100644 --- a/spec/active_enum/form_helpers/formtastic2_spec.rb +++ b/spec/active_enum/form_helpers/formtastic2_spec.rb @@ -36,6 +36,35 @@ output.should have_xpath('//option[@value=2]', :content => 'Female') end + context 'with ids as symbols' do + before do + reset_class Person do + enumerate :sex do + value :id => :m, :name => 'Male' + value :id => :f, :name => 'Female' + end + end + end + + it "should use enum input type for enumerated attribute" do + output = semantic_form_for(Person.new, :url => people_path) do |f| + concat f.input(:sex) + end + output.should have_selector('select#person_sex') + output.should have_xpath("//option[@value='m']", :content => 'Male') + output.should have_xpath("//option[@value='f']", :content => 'Female') + end + + it "should use explicit :enum input type" do + output = semantic_form_for(Person.new, :url => people_path) do |f| + concat f.input(:sex, :as => :enum) + end + output.should have_selector('select#person_sex') + output.should have_xpath("//option[@value='m']", :content => 'Male') + output.should have_xpath("//option[@value='f']", :content => 'Female') + end + end + it "should not use enum input type if :as option indicates other type" do output = semantic_form_for(Person.new, :url => people_path) do |f| concat f.input(:sex, :as => :string) diff --git a/spec/active_enum/form_helpers/simple_form_spec.rb b/spec/active_enum/form_helpers/simple_form_spec.rb index fd19fb4..eb988fd 100644 --- a/spec/active_enum/form_helpers/simple_form_spec.rb +++ b/spec/active_enum/form_helpers/simple_form_spec.rb @@ -34,6 +34,36 @@ output.should have_xpath('//option[@value=2]', :content => 'Female') end + context 'with ids as symbols' do + before do + controller.stub!(:action_name).and_return('new') + reset_class Person do + enumerate :sex do + value :id => :m, :name => 'Male' + value :id => :f, :name => 'Female' + end + end + end + + it "should use enum input type for enumerated attribute" do + output = simple_form_for(Person.new, :url => people_path) do |f| + concat f.input(:sex) + end + output.should have_selector('select#person_sex') + output.should have_xpath("//option[@value='m']", :content => 'Male') + output.should have_xpath("//option[@value='f']", :content => 'Female') + end + + it "should use explicit :enum input type" do + output = simple_form_for(Person.new, :url => people_path) do |f| + concat f.input(:sex, :as => :enum) + end + output.should have_selector('select#person_sex') + output.should have_xpath("//option[@value='m']", :content => 'Male') + output.should have_xpath("//option[@value='f']", :content => 'Female') + end + end + it "should not use enum input type if :as option indicates other type" do output = simple_form_for(Person.new, :url => people_path) do |f| concat f.input(:sex, :as => :string) diff --git a/spec/active_enum/storage/i18n_store_spec.rb b/spec/active_enum/storage/i18n_store_spec.rb index d4f7033..578eb13 100644 --- a/spec/active_enum/storage/i18n_store_spec.rb +++ b/spec/active_enum/storage/i18n_store_spec.rb @@ -81,11 +81,16 @@ class TestI18nStoreEnum < ActiveEnum::Base; end I18n.locale = :en end - it 'should return the value for a given id' do + it 'should return the value for a given integer id' do store.set 1, 'test' store.get_by_id(1).should == [1, 'Testing'] end + it 'should return the value for a given symbol id' do + store.set :abc, 'test' + store.get_by_id(:abc).should == [:abc, 'Testing'] + end + it 'should return the value with meta for a given id' do store.set 1, 'test', :description => 'meta' store.get_by_id(1).should == [1, 'Testing', { :description => 'meta' }] @@ -93,6 +98,7 @@ class TestI18nStoreEnum < ActiveEnum::Base; end it 'should return nil when id not found' do store.get_by_id(1).should be_nil + store.get_by_id(:abc).should be_nil end it 'should return key when translation missing' do diff --git a/spec/active_enum/storage/memory_store_spec.rb b/spec/active_enum/storage/memory_store_spec.rb index 638f962..13948df 100644 --- a/spec/active_enum/storage/memory_store_spec.rb +++ b/spec/active_enum/storage/memory_store_spec.rb @@ -37,6 +37,25 @@ class TestOtherAREnum < ActiveEnum::Base; end store.set 2, 'name 1' }.to raise_error(ActiveEnum::DuplicateValue) end + + it 'should raise error if string id used' do + expect { + store.set '1', 'Name 1' + store.set '2', 'name 1' + }.should raise_error(ActiveEnum::InvalidId) + end + + it 'should raise error if mixed id types used' do + expect { + store.set :one, 'Name 1' + store.set 2, 'name 2' + }.should raise_error(ActiveEnum::InvalidId) + + expect { + store.set 1, 'Name 1' + store.set :two, 'name 2' + }.should raise_error(ActiveEnum::InvalidId) + end end describe "#values" do @@ -53,13 +72,19 @@ class TestOtherAREnum < ActiveEnum::Base; end end describe "#get_by_id" do - it 'should return the value for a given id' do + it 'should return the value for a given integer id' do store.set 1, 'test name', :description => 'meta' store.get_by_id(1).should == [1, 'test name', {:description => "meta"}] end + it 'should return the value for a given symbol id' do + store.set :abc, 'test name', :description => 'meta' + store.get_by_id(:abc).should == [:abc, 'test name', {:description => "meta"}] + end + it 'should return nil when id not found' do store.get_by_id(1).should be_nil + store.get_by_id(:abc).should be_nil end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c92897..fecbd48 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,13 +10,14 @@ require 'active_enum/acts_as_enum' require 'securerandom' -module ActiveEnum +module ActiveEnum class Application < Rails::Application config.generators do |g| g.orm :active_record g.test_framework :rspec, :fixture => false end config.active_support.deprecation = :notify + I18n.enforce_available_locales = false config.eager_load = false if Rails.version >= "4.0" config.secret_key_base = SecureRandom.hex(10) if Rails.version >= "4.0" end @@ -30,6 +31,9 @@ class Application < Rails::Application require 'support/schema' +class Sex < ActiveEnum::Base; end +class Accepted < ActiveEnum::Base; end + class Person < ActiveRecord::Base; end class NoEnumPerson < ActiveRecord::Base self.table_name = 'people' diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 68c73d3..da43b0f 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -1,10 +1,10 @@ ActiveRecord::Schema.define(:version => 1) do - create_table :people, :force => true do |t| + create_table :people, :force => true do |t| t.string :first_name t.string :last_name - t.integer :sex - t.integer :attending - t.integer :staying + t.string :sex + t.string :attending + t.string :staying end create_table :sorted_people, :force => true do |t|