From b89c8235b3910ed704d35d3e763272cdda7ed99c Mon Sep 17 00:00:00 2001 From: Nicholas Chmielewski Date: Thu, 16 May 2013 16:05:37 +1000 Subject: [PATCH 1/9] Test that get_by_id methods support ids as symbols --- spec/active_enum/storage/i18n_store_spec.rb | 8 +++++++- spec/active_enum/storage/memory_store_spec.rb | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/active_enum/storage/i18n_store_spec.rb b/spec/active_enum/storage/i18n_store_spec.rb index 9912a56..7d78944 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 e309a99..bf365c8 100644 --- a/spec/active_enum/storage/memory_store_spec.rb +++ b/spec/active_enum/storage/memory_store_spec.rb @@ -53,13 +53,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 From fa6dbbd119c892052c696a33678be7ba6f51d10f Mon Sep 17 00:00:00 2001 From: Nicholas Chmielewski Date: Thu, 16 May 2013 16:10:01 +1000 Subject: [PATCH 2/9] Add context to spec in preparation for new tests --- spec/active_enum/base_spec.rb | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/spec/active_enum/base_spec.rb b/spec/active_enum/base_spec.rb index 98c3df5..52cb427 100644 --- a/spec/active_enum/base_spec.rb +++ b/spec/active_enum/base_spec.rb @@ -161,24 +161,26 @@ 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' - end - } + 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 + end - it 'should return id when given a symbol of the name' do - enum[:Name_1].should == 1 - 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 end From 382ea366d7a2ff0ac84ae12a88adc66c4869825e Mon Sep 17 00:00:00 2001 From: Nicholas Chmielewski Date: Thu, 16 May 2013 16:16:14 +1000 Subject: [PATCH 3/9] Add support for ids as symbols --- README.rdoc | 7 +++++ lib/active_enum/base.rb | 27 ++++++++++++------ spec/active_enum/base_spec.rb | 54 ++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/README.rdoc b/README.rdoc index 1b31511..d4a80da 100644 --- a/README.rdoc +++ b/README.rdoc @@ -43,6 +43,13 @@ 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 + 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. diff --git a/lib/active_enum/base.rb b/lib/active_enum/base.rb index 29bf7fb..7af39d4 100644 --- a/lib/active_enum/base.rb +++ b/lib/active_enum/base.rb @@ -55,13 +55,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)) && 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 +75,17 @@ 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)) && value.nil? + row = store.get_by_name(index) + value = row[2] if row + end + + value || {} end private diff --git a/spec/active_enum/base_spec.rb b/spec/active_enum/base_spec.rb index 52cb427..4d9da79 100644 --- a/spec/active_enum/base_spec.rb +++ b/spec/active_enum/base_spec.rb @@ -95,13 +95,28 @@ class NewEnum < ActiveEnum::Base; 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 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 match on id before name and return meta values hash' do + enum = define_enum do + value :id => :one, :name => 'Name 1', :description => 'extra' + value :id => :name_1, :name => 'Name 2', :description => 'extra2' + end + enum.meta(:name_1).should == {:description => 'extra2'} + end + it 'should return empty hash for index with no meta defined' do enum = define_enum do value :id => 1, :name => 'Name' @@ -182,6 +197,43 @@ class NewEnum < ActiveEnum::Base; end enum[:name_1].should == 1 end end + + 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 + + it 'should return id when given a symbol of the name' do + enum[:Name_1].should == :one + 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 match on id before name' do + enum[:one].should == 'two' + enum[:two].should == 'three' + enum[:three].should == :two + end + end + end end describe ".include?" do From 39b32c7e0d4d2a13167f10ca9449f851206b8a89 Mon Sep 17 00:00:00 2001 From: Nicholas Chmielewski Date: Wed, 22 May 2013 16:06:24 +1000 Subject: [PATCH 4/9] Add tests to make sure that form helpers work with symbol ids --- .../form_helpers/formtastic2_spec.rb | 29 ++++++++++++++++++ .../form_helpers/simple_form_spec.rb | 30 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/spec/active_enum/form_helpers/formtastic2_spec.rb b/spec/active_enum/form_helpers/formtastic2_spec.rb index dcb7a34..fbdeab3 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 e7afad4..742b5f5 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) From b1470f08a5ba284312f8ef9713176960286e05a1 Mon Sep 17 00:00:00 2001 From: "Sergey.Gnuskov" Date: Tue, 9 Jul 2013 17:52:30 +0400 Subject: [PATCH 5/9] Not overwriting attribute setter. --- lib/active_enum/extensions.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/active_enum/extensions.rb b/lib/active_enum/extensions.rb index 290ed7b..de67fc0 100644 --- a/lib/active_enum/extensions.rb +++ b/lib/active_enum/extensions.rb @@ -107,15 +107,15 @@ def #{attribute}(arg=nil) # user.sex = :male # 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] - else - super arg - end - end - DEF + # class_eval <<-DEF + # def #{attribute}=(arg) + # if arg.is_a?(Symbol) + # super self.class.active_enum_for(:#{attribute})[arg] + # else + # super arg + # end + # end + # DEF end # Define question method to check enum value against attribute value From e7983f035b6dd0a2896c38681253a811057751dd Mon Sep 17 00:00:00 2001 From: "Sergey.Gnuskov" Date: Tue, 9 Jul 2013 19:50:49 +0400 Subject: [PATCH 6/9] Load symbol from database if it is string --- lib/active_enum/extensions.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_enum/extensions.rb b/lib/active_enum/extensions.rb index de67fc0..75e7452 100644 --- a/lib/active_enum/extensions.rb +++ b/lib/active_enum/extensions.rb @@ -81,6 +81,7 @@ def define_active_enum_read_method(attribute) class_eval <<-DEF def #{attribute}(arg=nil) value = super() + value = value.to_sym if value.is_a?(String) return if value.nil? && arg.nil? enum = self.class.active_enum_for(:#{attribute}) From 811134e06c9f947abffe1d206c0a04d26638a123 Mon Sep 17 00:00:00 2001 From: "Sergey.Gnuskov" Date: Fri, 28 Feb 2014 18:54:49 +0400 Subject: [PATCH 7/9] Rethought and refactoring in more logic way, tests added. --- README.rdoc | 46 ++- lib/active_enum/base.rb | 11 +- lib/active_enum/extensions.rb | 28 +- lib/active_enum/storage/memory_store.rb | 9 + spec/active_enum/base_spec.rb | 66 +++- spec/active_enum/extensions_spec.rb | 367 +++++++++++++----- spec/active_enum/storage/memory_store_spec.rb | 19 + spec/spec_helper.rb | 6 +- spec/support/schema.rb | 8 +- 9 files changed, 413 insertions(+), 147 deletions(-) diff --git a/README.rdoc b/README.rdoc index d4a80da..01737ab 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 @@ -50,6 +50,10 @@ Define using symbol ids 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. @@ -71,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. @@ -156,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 @@ -220,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: @@ -235,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 @@ -244,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 @@ -266,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 7af39d4..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) @@ -60,7 +61,7 @@ def get(index) value = row[1] if row end - if (index.is_a?(String) || index.is_a?(Symbol)) && value.nil? + if (index.is_a?(String) || (index.is_a?(Symbol) && !symbol_ids?)) && value.nil? row = store.get_by_name(index) value = row[0] if row end @@ -80,7 +81,7 @@ def meta(index) value = row[2] if row end - if (index.is_a?(String) || index.is_a?(Symbol)) && value.nil? + 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 @@ -88,6 +89,10 @@ def meta(index) value || {} end + def symbol_ids? + store.symbol_ids? + end + private def id_and_name_and_meta(hash) diff --git a/lib/active_enum/extensions.rb b/lib/active_enum/extensions.rb index 75e7452..5351c55 100644 --- a/lib/active_enum/extensions.rb +++ b/lib/active_enum/extensions.rb @@ -101,22 +101,23 @@ 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 # user.sex = :male # 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] - # else - # super arg - # end - # end - # DEF + class_eval <<-DEF + def #{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 + end + DEF end # Define question method to check enum value against attribute value @@ -128,7 +129,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 edb92b0..4061cfa 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 4d9da79..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,6 +108,30 @@ 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 @@ -102,6 +142,14 @@ class NewEnum < ActiveEnum::Base; 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' @@ -109,12 +157,13 @@ class NewEnum < ActiveEnum::Base; end enum.meta(:one).should == {:description => 'extra'} end - it 'should match on id before name and return meta values hash' do + it 'should not match value by symbol when symbol ids used' do enum = define_enum do value :id => :one, :name => 'Name 1', :description => 'extra' - value :id => :name_1, :name => 'Name 2', :description => 'extra2' end - enum.meta(:name_1).should == {:description => 'extra2'} + + 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 @@ -214,11 +263,6 @@ class NewEnum < ActiveEnum::Base; end enum['Name 1'].should == :one end - it 'should return id when given a symbol of the name' do - enum[:Name_1].should == :one - enum[:name_1].should == :one - end - context 'ids and names are similar' do let(:enum) { define_enum do @@ -227,10 +271,10 @@ class NewEnum < ActiveEnum::Base; end end } - it 'should match on id before name' do + it 'should not match by symbolized value' do enum[:one].should == 'two' enum[:two].should == 'three' - enum[:three].should == :two + enum[:three].should be_nil end end end diff --git a/spec/active_enum/extensions_spec.rb b/spec/active_enum/extensions_spec.rb index 7e5353c..c217379 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,323 @@ class Accepted < ActiveEnum::Base }.should 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 with no arg' do - person.sex.should be_nil + 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 return nil enum id' do - person.sex(:id).should be_nil + context "with undefined value" do + let(:person) { Person.new(:sex => -1) } + + 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 + 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 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 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 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 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 store id value when valid enum name' do - person.sex = :female - person.sex.should == 2 + 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/storage/memory_store_spec.rb b/spec/active_enum/storage/memory_store_spec.rb index bf365c8..192c1b2 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' }.should 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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 49a4348..a11bf6c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,13 +9,14 @@ require 'active_enum' require 'active_enum/acts_as_enum' -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 end end ActiveEnum::Application.initialize! @@ -27,6 +28,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 set_table_name 'people' diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 9eb1aaf..14e02ac 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 :sexes, :force => true do |t| From cc17e840a6a403234cc9d8723f5791d41b55c10b Mon Sep 17 00:00:00 2001 From: Sergey Gnuskov Date: Fri, 28 Feb 2014 18:04:07 +0300 Subject: [PATCH 8/9] Update README.rdoc --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 01737ab..d10ac96 100644 --- a/README.rdoc +++ b/README.rdoc @@ -50,7 +50,7 @@ Define using symbol ids value :id => :f, :name => 'Female' end -**NOTE** +*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. From 9b1abef4ae3769eefb6a8c61c9f1e51911835b5f Mon Sep 17 00:00:00 2001 From: "Sergey.Gnuskov" Date: Mon, 9 Jun 2014 15:29:42 +0400 Subject: [PATCH 9/9] empty string is a nil value for string ids --- lib/active_enum/extensions.rb | 9 ++++++--- spec/active_enum/extensions_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/active_enum/extensions.rb b/lib/active_enum/extensions.rb index 5351c55..f50cfab 100644 --- a/lib/active_enum/extensions.rb +++ b/lib/active_enum/extensions.rb @@ -81,10 +81,13 @@ def define_active_enum_read_method(attribute) class_eval <<-DEF def #{attribute}(arg=nil) value = super() - value = value.to_sym if value.is_a?(String) - 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' } diff --git a/spec/active_enum/extensions_spec.rb b/spec/active_enum/extensions_spec.rb index c217379..ec52195 100644 --- a/spec/active_enum/extensions_spec.rb +++ b/spec/active_enum/extensions_spec.rb @@ -106,21 +106,26 @@ 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 @@ -267,6 +272,26 @@ 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 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