From 2ad33af3b0da159a5422e7e6f126e598879083d2 Mon Sep 17 00:00:00 2001 From: Damon Timm Date: Sun, 12 Apr 2020 12:28:00 -0700 Subject: [PATCH 1/3] Refactor module, separate include features Now use: # Can go in base ApplicationRecord include Enumbler # For the models you wish to hold Enumbler data include Enumbler::Enable --- .rubocop.yml | 20 ++---- lib/enumbler.rb | 156 ++++++---------------------------------- lib/enumbler/enabler.rb | 150 ++++++++++++++++++++++++++++++++++++++ spec/enumbler_spec.rb | 9 +-- 4 files changed, 181 insertions(+), 154 deletions(-) create mode 100644 lib/enumbler/enabler.rb diff --git a/.rubocop.yml b/.rubocop.yml index 07e6562..2d32269 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,6 +21,9 @@ Lint/RaiseException: Lint/StructNewOverride: Enabled: true +Style/Documentation: + Enabled: false + Style/HashEachMethods: Enabled: true @@ -38,20 +41,5 @@ Style/TrailingCommaInArrayLiteral: Enabled: true EnforcedStyleForMultiline: consistent_comma -Metrics/BlockLength: +Metrics: Enabled: false - -Metrics/AbcSize: - Enabled: false - -Metrics/CyclomaticComplexity: - Enabled: false - -Metrics/MethodLength: - Max: 30 - -Metrics/ModuleLength: - Max: 300 - -Metrics/ClassLength: - Max: 300 diff --git a/lib/enumbler.rb b/lib/enumbler.rb index fe8fd5c..a359688 100644 --- a/lib/enumbler.rb +++ b/lib/enumbler.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'enumbler/enumble' +require 'enumbler/enabler' require 'enumbler/version' require 'active_support/concern' @@ -10,65 +11,23 @@ module Enumbler extend ActiveSupport::Concern + # An error raised by the Enumbler. class Error < StandardError; end - # The Enumble definition that this record defined. - # @return [Enumbler::Enumble] - def enumble - @enumble = self.class.enumbles.find { |enumble| enumble.id == id } - - raise Error, 'An enumble is not defined for this record!' if @enumble.nil? - - @enumble - end - - # These are the ClassMethods that are added to an ApplicationRecord model when - # `include Enumbler` is added to the class. + # Include these ClassMethods in your base ApplicationRecord model to bestow + # any of your models with the ability to be connected to an Enumbled relation + # in the same way you would use `belongs_to` now you use `enumbled_to`. + # + # class ApplicationRecord > ActiveRecord::Base + # include Enumbler + # self.abstract_class = true + # end + # + # class House < ApplicationRecord + # enumbled_to :color + # end + # module ClassMethods - attr_reader :enumbles - - # Defines an Enumble for this model. An enum with integrity. - # - # # in your migration - # create_table :colors, force: true do |t| - # t.string :label, null: false - # end - # - # class Color < ApplicationRecord - # include Enumbler - # - # enumble :black, 1 - # enumble :white, 2 - # enumble :dark_brown, 3, # label: 'dark-brown' - # enumble :black_hole, 3, label: 'Oh my! It is a black hole!' - # end - # - # # Dynamically adds the following methods: - # Color::BLACK #=> 1 - # Color.black #=> MyRecord.find(1) - # color.black? #=> true || false - # color.is_black #=> true || false - # - # @param enum [Symbol] the enum representation - # @param id [Integer] the primary key value - # @param label [String] optional: label for humans - # @param **options [Hash] optional: additional attributes and values that - # will be saved to the database for this enumble record - def enumble(enum, id, label: nil, **options) - @enumbles ||= [] - @enumbled_model = self - - enumble = Enumble.new(enum, id, label: label, **options) - - if @enumbles.include?(enumble) - raise Error, "You cannot add the same Enumble twice! Attempted to add: #{enum}, #{id}." - end - - define_dynamic_methods_and_constants_for_enumbled_model(enum, id) - - @enumbles << enumble - end - # Defines the relationship between a model and the Enumbled class. Use this # in lieu of `belongs_to` to establish that relationship. It requires a # model that has defined one or more `Enumbles`. @@ -78,13 +37,17 @@ def enumble(enum, id, label: nil, **options) # t.references :color, foreign_key: true, null: false # end # - # class House < ApplicationRecord + # class ApplicationRecord > ActiveRecord::Base # include Enumbler + # self.abstract_class = true + # end + # + # class House < ApplicationRecord # enumbled_to :color # end # # @param name [Symbol] symbol representation of the class this belongs_to - # @param *args [Array] additional arguments passed to `belongs_to` + # @param **options [Hash] additional options passed to `belongs_to` def enumbled_to(name, scope = nil, prefix: nil, **options) class_name = name.to_s.classify @enumbled_model = class_name.constantize @@ -101,83 +64,8 @@ def enumbled_to(name, scope = nil, prefix: nil, **options) raise Error, "The class #{class_name} cannot be found. Uninitialized constant." end - # Return the record id(s) based on different argument types. Can accept an - # Integer, a Symbol, or an instance of Enumbled model. This lookup is a - # databse-free lookup. - # - # Color.ids_from_enumablable(1, 2) # => [1, 2] - # Color.ids_from_enumablable(:black, :white) # => [1, 2] - # Color.ids_from_enumablable(Color.black, Color.white) # => [1, 2] - # - # @raise [Error] when there is no enumble to be found - # @param *args [Integer, Symbol, Class] - # @return [Array] - def ids_from_enumablable(*args) - args.flatten.compact.uniq.map do |arg| - err = "Unable to find a #{@enumbled_model}#enumble with #{arg}" - - begin - arg = Integer(arg) # raises Type error if not a real integer - enumble = @enumbled_model.enumbles.find { |e| e.id == arg } - rescue TypeError - enumble = if arg.is_a?(Symbol) - @enumbled_model.enumbles.find { |e| e.enum == arg } - elsif arg.instance_of?(@enumbled_model) - arg.enumble - end - end - - enumble&.id || raise(Error, err) - rescue Error - raise Error, err - end - end - - # Seeds the database with the Enumble data. - # @param delete_missing_records [Boolean] remove any records that are no - # longer defined (default: false) - def seed_the_enumbler(delete_missing_records: false) - max_database_id = all.order('id desc').take&.id || 0 - max_enumble_id = enumbles.map(&:id).max - - max_id = max_enumble_id > max_database_id ? max_enumble_id : max_database_id - - discarded_ids = [] - - (1..max_id).each do |id| - enumble = @enumbles.find { |e| e.id == id } - - if enumble.nil? - discarded_ids << id - next - end - - record = find_or_initialize_by(id: id) - record.attributes = enumble.attributes - record.save! - end - - where(id: discarded_ids).delete_all if delete_missing_records - end - - # Seeds the database with the Enumble data, removing any records that are no - # longer defined. - def seed_the_enumbler! - seed_the_enumbler(delete_missing_records: true) - end - private - def define_dynamic_methods_and_constants_for_enumbled_model(enum, id) - method_name = "#{enum}?" - alias_method_name = "is_#{enum}" - - const_set(enum.to_s.upcase, id) - define_method(method_name) { self.id == id } - alias_method alias_method_name, method_name - define_singleton_method(enum) { find(id) } - end - def define_dynamic_methods_for_enumbled_to_models(prefix: nil) model_name = @enumbled_model.to_s.underscore @@ -188,7 +76,7 @@ def define_dynamic_methods_for_enumbled_to_models(prefix: nil) end define_singleton_method(method) do |*args| - where("#{model_name}_id": ids_from_enumablable(args)) + where("#{model_name}_id": @enumbled_model.ids_from_enumablable(args)) end end end diff --git a/lib/enumbler/enabler.rb b/lib/enumbler/enabler.rb new file mode 100644 index 0000000..2a021f3 --- /dev/null +++ b/lib/enumbler/enabler.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module Enumbler + module Enabler + extend ActiveSupport::Concern + + # The Enumble definition that this record defined. + # @return [Enumbler::Enumble] + def enumble + @enumble = self.class.enumbles.find { |enumble| enumble.id == id } + + raise Error, 'An enumble is not defined for this record!' if @enumble.nil? + + @enumble + end + + # These ClassMethods can be included in any model that you wish to + # _Enumble_! + # + # class Color < ApplicationRecord + # include Enumbler::Enabler + # + # enumble :black, 1 + # enumble :white, 2 + # end + # + module ClassMethods + attr_reader :enumbles + + # Defines an Enumble for this model. An enum with integrity. + # + # # in your migration + # create_table :colors, force: true do |t| + # t.string :label, null: false + # end + # + # class Color < ApplicationRecord + # include Enumbler::Enabler + # + # enumble :black, 1 + # enumble :white, 2 + # enumble :dark_brown, 3, # label: 'dark-brown' + # enumble :black_hole, 3, label: 'Oh my! It is a black hole!' + # end + # + # # Dynamically adds the following methods: + # Color::BLACK #=> 1 + # Color.black #=> MyRecord.find(1) + # color.black? #=> true || false + # color.is_black #=> true || false + # + # @param enum [Symbol] the enum representation + # @param id [Integer] the primary key value + # @param label [String] optional: label for humans + # @param **options [Hash] optional: additional attributes and values that + # will be saved to the database for this enumble record + def enumble(enum, id, label: nil, **options) + @enumbles ||= [] + @enumbled_model = self + + enumble = Enumble.new(enum, id, label: label, **options) + + if @enumbles.include?(enumble) + raise Error, "You cannot add the same Enumble twice! Attempted to add: #{enum}, #{id}." + end + + define_dynamic_methods_and_constants_for_enumbled_model(enum, id) + + @enumbles << enumble + end + + # Return the record id(s) based on different argument types. Can accept an + # Integer, a Symbol, or an instance of Enumbled model. This lookup is a + # databse-free lookup. + # + # Color.ids_from_enumablable(1, 2) # => [1, 2] + # Color.ids_from_enumablable(:black, :white) # => [1, 2] + # Color.ids_from_enumablable(Color.black, Color.white) # => [1, 2] + # + # @raise [Error] when there is no enumble to be found + # @param *args [Integer, Symbol, Class] + # @return [Array] + def ids_from_enumablable(*args) + args.flatten.compact.uniq.map do |arg| + err = "Unable to find a #{@enumbled_model}#enumble with #{arg}" + + begin + arg = Integer(arg) # raises Type error if not a real integer + enumble = @enumbled_model.enumbles.find { |e| e.id == arg } + rescue TypeError + enumble = if arg.is_a?(Symbol) + @enumbled_model.enumbles.find { |e| e.enum == arg } + elsif arg.instance_of?(@enumbled_model) + arg.enumble + end + end + + enumble&.id || raise(Error, err) + rescue Error + raise Error, err + end + end + + # Seeds the database with the Enumble data. + # @param delete_missing_records [Boolean] remove any records that are no + # longer defined (default: false) + def seed_the_enumbler(delete_missing_records: false) + max_database_id = all.order('id desc').take&.id || 0 + max_enumble_id = enumbles.map(&:id).max + + max_id = max_enumble_id > max_database_id ? max_enumble_id : max_database_id + + discarded_ids = [] + + (1..max_id).each do |id| + enumble = @enumbles.find { |e| e.id == id } + + if enumble.nil? + discarded_ids << id + next + end + + record = find_or_initialize_by(id: id) + record.attributes = enumble.attributes + record.save! + end + + where(id: discarded_ids).delete_all if delete_missing_records + end + + # Seeds the database with the Enumble data, removing any records that are no + # longer defined. + def seed_the_enumbler! + seed_the_enumbler(delete_missing_records: true) + end + + private + + def define_dynamic_methods_and_constants_for_enumbled_model(enum, id) + method_name = "#{enum}?" + alias_method_name = "is_#{enum}" + + const_set(enum.to_s.upcase, id) + define_method(method_name) { self.id == id } + alias_method alias_method_name, method_name + define_singleton_method(enum) { find(id) } + end + end + end +end diff --git a/spec/enumbler_spec.rb b/spec/enumbler_spec.rb index 3b01955..0276baa 100644 --- a/spec/enumbler_spec.rb +++ b/spec/enumbler_spec.rb @@ -14,13 +14,16 @@ end class ApplicationRecord < ActiveRecord::Base + # @!parse extend Enumbler::ClassMethods + include Enumbler + self.abstract_class = true end # Our Color has been Enumbled with some basic colors. class Color < ApplicationRecord - # @!parse extend Enumbler::ClassMethods - include Enumbler + # @!parse extend Enumbler::Enabler::ClassMethods + include Enumbler::Enabler enumble :black, 1 enumble :white, 2 @@ -30,8 +33,6 @@ class Color < ApplicationRecord # Our House class, it has a color of course! class House < ApplicationRecord - # @!parse extend Enumbler::ClassMethods - include Enumbler enumbled_to :color end From 59893091e4e966e577c4d529d96d3fbf119a9082 Mon Sep 17 00:00:00 2001 From: Damon Timm Date: Sun, 12 Apr 2020 17:30:11 -0700 Subject: [PATCH 2/3] Add a few more dynamic methods --- Gemfile.lock | 2 +- README.md | 52 ++++++++++++++++++++++++++++++++++++++++- lib/enumbler.rb | 43 ++++++++++++++++++++++------------ lib/enumbler/enabler.rb | 4 +++- lib/enumbler/version.rb | 2 +- spec/enumbler_spec.rb | 23 +++++++++++++++--- 6 files changed, 104 insertions(+), 22 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9e90fc8..202110c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - enumbler (0.1.0) + enumbler (0.2.0) activerecord (>= 6.0) activesupport (>= 6.0) diff --git a/README.md b/README.md index 0eb50ac..e7f5f95 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,56 @@ # Enumbler -`Enums` are terrific, but they lack integrity. Enumbler! +`Enums` are terrific, but they lack integrity. Enumbler! The _enum enabler_! The goal is to allow one to maintain a true foreign_key database driven relationship that also behaves a little bit like an `enum`. Best of both worlds? We hope so. + + +## Example + +Suppose you have a `House` and you want to add some `colors` to the house. You are tempted to use an `enum` but the `Enumbler` is calling! + +```ruby +ActiveRecord::Schema.define do + create_table :colors|t| + t.string :label, null: false + end + + create_table :houses|t| + t.references :color, foreign_key: true, null: false + end +end + +class ApplicationRecord < ActiveRecord::Base + include Enumbler + self.abstract_class = true +end + +# Our Color has been Enumbled with some basic colors. +class Color < ApplicationRecord + include Enumbler::Enabler + + enumble :black, 1 + enumble :white, 2 + enumble :dark_brown, 3 + enumble :infinity, 4, label: 'Infinity - and beyond!' +end + +# Our House class, it has a color of course! +class House < ApplicationRecord + enumbled_to :color +end + +# This gives you some power: +Color::BLACK # => 1 +Color.black # => equivalent to Color.find(1) +Color.black.black? # => true +Color.black.is_black # => true +Color.white.not_black? # => true + +house = House.create!(color: Color.black) +house.black? +house.not_black? + +House.color(:black) # => [house] +``` ## Installation diff --git a/lib/enumbler.rb b/lib/enumbler.rb index a359688..5f84b85 100644 --- a/lib/enumbler.rb +++ b/lib/enumbler.rb @@ -47,36 +47,49 @@ module ClassMethods # end # # @param name [Symbol] symbol representation of the class this belongs_to + # @param prefix [Boolean] default: false; prefix the instance method + # attributes with the Enumble name, ex: `House.color_black?` instead of + # `House.black?` + # @param scope_prefix [string] optional, prefix the class scopes, for + # example: `where_by` will make it `House.where_by_color(:black)` # @param **options [Hash] additional options passed to `belongs_to` - def enumbled_to(name, scope = nil, prefix: nil, **options) + def enumbled_to(name, scope = nil, prefix: false, scope_prefix: nil, **options) class_name = name.to_s.classify - @enumbled_model = class_name.constantize + enumbled_model = class_name.constantize - unless @enumbled_model.respond_to?(:enumbles) - raise Error, "The class #{class_name} does not have any enumbles defined."\ + unless enumbled_model.respond_to?(:enumbles) + raise Error, "The model #{class_name} does not have any enumbles defined."\ " You can add them via `#{class_name}.enumble :blue, 1`." end belongs_to(name, scope, **options) - define_dynamic_methods_for_enumbled_to_models(prefix: prefix) + define_dynamic_methods_for_enumbled_to_models(enumbled_model, prefix: prefix, scope_prefix: scope_prefix) rescue NameError - raise Error, "The class #{class_name} cannot be found. Uninitialized constant." + raise Error, "The model #{class_name} cannot be found. Uninitialized constant." end private - def define_dynamic_methods_for_enumbled_to_models(prefix: nil) - model_name = @enumbled_model.to_s.underscore + # Define the dynamic methods for this relationship. + # + # @todo - we should check for naming conflicts! + # dangerous_attribute_method?(method_name) + # method_defined_within?(method_name, self, Module) + def define_dynamic_methods_for_enumbled_to_models(enumbled_model, prefix: false, scope_prefix: nil) + model_name = enumbled_model.to_s.underscore + column_name = "#{model_name}_id" - method = if prefix.blank? - model_name - else - "#{prefix}_#{model_name}" - end + cmethod = scope_prefix.blank? ? model_name : "#{scope_prefix}_#{model_name}" + define_singleton_method(cmethod) do |*args| + where(column_name => enumbled_model.ids_from_enumablable(args)) + end - define_singleton_method(method) do |*args| - where("#{model_name}_id": @enumbled_model.ids_from_enumablable(args)) + enumbled_model.enumbles.each do |enumble| + method_name = prefix ? "#{model_name}_#{enumble.enum}?" : "#{enumble.enum}?" + not_method_name = prefix ? "#{model_name}_not_#{enumble.enum}?" : "not_#{enumble.enum}?" + define_method(method_name) { self[column_name] == enumble.id } + define_method(not_method_name) { self[column_name] != enumble.id } end end end diff --git a/lib/enumbler/enabler.rb b/lib/enumbler/enabler.rb index 2a021f3..83ba360 100644 --- a/lib/enumbler/enabler.rb +++ b/lib/enumbler/enabler.rb @@ -45,7 +45,7 @@ module ClassMethods # # # Dynamically adds the following methods: # Color::BLACK #=> 1 - # Color.black #=> MyRecord.find(1) + # Color.black #=> Color.find(1) # color.black? #=> true || false # color.is_black #=> true || false # @@ -138,10 +138,12 @@ def seed_the_enumbler! def define_dynamic_methods_and_constants_for_enumbled_model(enum, id) method_name = "#{enum}?" + not_method_name = "not_#{enum}?" alias_method_name = "is_#{enum}" const_set(enum.to_s.upcase, id) define_method(method_name) { self.id == id } + define_method(not_method_name) { self.id != id } alias_method alias_method_name, method_name define_singleton_method(enum) { find(id) } end diff --git a/lib/enumbler/version.rb b/lib/enumbler/version.rb index 2a1a08f..da1f3af 100644 --- a/lib/enumbler/version.rb +++ b/lib/enumbler/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Enumbler - VERSION = '0.1.0' + VERSION = '0.2.0' end diff --git a/spec/enumbler_spec.rb b/spec/enumbler_spec.rb index 0276baa..8a44b9f 100644 --- a/spec/enumbler_spec.rb +++ b/spec/enumbler_spec.rb @@ -70,11 +70,11 @@ class House < ApplicationRecord it 'creates the query methods', :seed do expect(Color.black).to be_black expect(Color.black.is_black).to be true + expect(Color.white).to be_not_black end end describe '.enumbled_to', :seed do - after(:example) { House.enumbled_to(:color) } it 'raises an error when the class does not exist' do expect { House.enumbled_to(:bob) }.to raise_error(Enumbler::Error, /cannot be found/) end @@ -93,11 +93,28 @@ class House < ApplicationRecord it 'raises an error when the Enumble is not defined' do expect { House.color(100, 1) }.to raise_error(Enumbler::Error, /Unable to find/) end - it 'allows a prefix to be set' do + it 'allows a scope prefix to be set' do house = House.create! color: Color.black - House.enumbled_to(:color, prefix: 'where_by') + House.enumbled_to(:color, scope_prefix: 'where_by') expect(House.where_by_color(:black)).to contain_exactly(house) end + it 'adds instance methods to query the enumble' do + house = House.new color: Color.black + expect(house).to be_black + expect(house).not_to be_white + + expect(house).to be_not_white + expect(house).not_to be_not_black + end + it 'can add a prefix if requested' do + House.enumbled_to(:color, prefix: true) + house = House.new color: Color.black + expect(house).to be_color_black + expect(house).not_to be_color_white + + expect(house).to be_color_not_white + expect(house).not_to be_color_not_black + end end end From 1f8e94c117df72b794dc8290a194bdeb0f6b326a Mon Sep 17 00:00:00 2001 From: Damon Timm Date: Wed, 29 Apr 2020 11:30:32 -0700 Subject: [PATCH 3/3] Add .id_from_enumbler helper method --- Gemfile.lock | 6 +++--- enumbler.gemspec | 4 ++-- lib/enumbler/enabler.rb | 21 ++++++++++++++++++--- lib/enumbler/version.rb | 2 +- spec/enumbler_spec.rb | 2 ++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4e0c969..b8a38ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: . specs: - enumbler (0.3.0) - activerecord (>= 6.0) - activesupport (>= 6.0) + enumbler (0.3.1) + activerecord (~> 6.0.2) + activesupport (~> 6.0.2) GEM remote: https://rubygems.org/ diff --git a/enumbler.gemspec b/enumbler.gemspec index 14efcfe..37e8e5c 100644 --- a/enumbler.gemspec +++ b/enumbler.gemspec @@ -29,8 +29,8 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 6.0' - spec.add_dependency 'activesupport', '>= 6.0' + spec.add_dependency 'activerecord', '~> 6.0.2' + spec.add_dependency 'activesupport', '~> 6.0.2' spec.add_development_dependency 'database_cleaner-active_record', '~> 1.8.0' spec.add_development_dependency 'fuubar', '~> 2.5' diff --git a/lib/enumbler/enabler.rb b/lib/enumbler/enabler.rb index 4b6870d..a07e517 100644 --- a/lib/enumbler/enabler.rb +++ b/lib/enumbler/enabler.rb @@ -72,9 +72,24 @@ def enumble(enum, id, label: nil, **options) @enumbles << enumble end - # Return the record id(s) based on different argument types. Can accept an - # Integer, a Symbol, or an instance of Enumbled model. This lookup is a - # databse-free lookup. + # Return the record id for a given argument. Can accept an Integer, a + # Symbol, or an instance of Enumbled model. This lookup is a database-free + # lookup. + # + # Color.id_from_enumbler(1) # => 1 + # Color.id_from_enumbler(:black) # => 1 + # Color.id_from_enumbler(Color.black) # => 1 + # + # @raise [Error] when there is no enumble to be found + # @param arg [Integer, Symbol, Class] + # @return [Integer] + def id_from_enumbler(arg) + ids_from_enumbler(arg).first + end + + # Return the record id(s) based on different argument types. Can accept + # an Integer, a Symbol, or an instance of Enumbled model. This lookup is + # a database-free lookup. # # Color.ids_from_enumbler(1, 2) # => [1, 2] # Color.ids_from_enumbler(:black, :white) # => [1, 2] diff --git a/lib/enumbler/version.rb b/lib/enumbler/version.rb index f7e68f7..9a2be9b 100644 --- a/lib/enumbler/version.rb +++ b/lib/enumbler/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Enumbler - VERSION = '0.3.0' + VERSION = '0.3.1' end diff --git a/spec/enumbler_spec.rb b/spec/enumbler_spec.rb index e4a5c80..21addae 100644 --- a/spec/enumbler_spec.rb +++ b/spec/enumbler_spec.rb @@ -130,12 +130,14 @@ class House < ApplicationRecord describe '.ids_from_enumbler', :seed do it 'returns a numeric id' do + expect(Color.id_from_enumbler(1)).to eq 1 expect(Color.ids_from_enumbler(1)).to contain_exactly(1) end it 'raises an error when the id is not defined' do expect { Color.ids_from_enumbler(100, 1) }.to raise_error(Enumbler::Error, /Unable to find/) end it 'returns an id from a symbol' do + expect(Color.id_from_enumbler(:black)).to eq 1 expect(Color.ids_from_enumbler(:black)).to contain_exactly(1) expect(Color.ids_from_enumbler(:black, :white)).to contain_exactly(1, 2) end