diff --git a/Gemfile.lock b/Gemfile.lock index 7fbcaff..c802fb9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - enumbler (0.8.0) + enumbler (0.8.1) activerecord (>= 5.2.3, < 6.1) activesupport (>= 5.2.3, < 6.1) diff --git a/README.md b/README.md index 0f9397f..d1177ff 100644 --- a/README.md +++ b/README.md @@ -170,14 +170,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To ## Roadmap -* We need to add in support for additional attributes/columns in the enumbled table. For example, following the `Color` concept, we may want to have a column which is `hex` and stores the colors `hex` value (e.g., `FFFFFF`). This should be supported. -* Ideally, we could make this work more like a traditional `enum`; for example, overriding the `.where` method by allowing something like: `House.where(color: :blue)` instead of `House.where_color(:blue)`. But right now am in a rush and not sure how to go about doing that properly. +* Ideally, we could make this work more like a traditional `enum`; for example, overriding the `.where` method by allowing something like: `House.where(color: :blue)` instead of `House.color(:blue)`. But right now am in a rush and not sure how to go about doing that properly. ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/linguabee/enumbler. - ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/lib/enumbler/enabler.rb b/lib/enumbler/enabler.rb index 77ecc16..04a28d5 100644 --- a/lib/enumbler/enabler.rb +++ b/lib/enumbler/enabler.rb @@ -375,11 +375,21 @@ def define_dynamic_methods_and_constants_for_enumbled_model(enumble) method_name = "#{enumble.enum}?" not_method_name = "not_#{enumble.enum}?" alias_method_name = "is_#{enumble.enum}" + any_method_name = "any_#{enumble.enum}?" + + [method_name, not_method_name, alias_method_name].each do |mname| + detect_enumbler_conflict(enumble.enum, mname) + end + + [enumble.enum, any_method_name].each do |mname| + detect_enumbler_conflict(enumble.enum, mname, klass_method: true) + end const_set(enumble.enum.to_s.upcase, enumble.id) define_method(method_name) { id == enumble.id } define_method(not_method_name) { id != enumble.id } alias_method alias_method_name, method_name + define_singleton_method(enumble.enum) do |attr = nil| return find(enumble.id) if attr.nil? @@ -388,13 +398,42 @@ def define_dynamic_methods_and_constants_for_enumbled_model(enumble) raise Enumbler::Error, "The attribute #{attr} is not supported on this Enumble." end - define_singleton_method("any_#{enumble.enum}?") do + define_singleton_method(any_method_name) do where(id: enumble.id).exists? rescue NoMethodError raise Enumbler::Error, "The attribute #{attr} is not supported on this Enumble." end end + # This idea sourced lovingly from ActiveRecord::Enum + ENUMBLER_CONFLICT_MESSAGE = <<~TEXT.squish + You tried to define the enumble :%s on the model %s, but + this will generate a %s method `%s`, which is already defined + by %s. + TEXT + + def detect_enumbler_conflict(enumble_name, method_name, klass_method: false) + if klass_method && dangerous_class_method?(method_name) + raise_conflict_error(enumble_name, method_name, type: 'class') + elsif klass_method && method_defined_within?(method_name, ActiveRecord::Relation) + raise_conflict_error(enumble_name, method_name, type: 'class', source: ActiveRecord::Relation.name) + elsif !klass_method && dangerous_attribute_method?(method_name) + raise_conflict_error(enumble_name, method_name) + end + end + + def raise_conflict_error(enumble_name, method_name, type: 'instance', source: 'ActiveRecord') + raise Error, + format( + ENUMBLER_CONFLICT_MESSAGE, + enum: enumble_name, + klass: name, + type: type, + method: method_name, + source: source + ) + end + # I accidentally forgot to provide an id one time and it was confusing as # the last argument became the hash of options. This should help. def validate_id_is_numeric(enum, id) diff --git a/lib/enumbler/version.rb b/lib/enumbler/version.rb index f8663b8..3324153 100644 --- a/lib/enumbler/version.rb +++ b/lib/enumbler/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Enumbler - VERSION = '0.8.0' + VERSION = '0.8.1' end diff --git a/spec/enumbler_spec.rb b/spec/enumbler_spec.rb index 04270fa..42b8cef 100644 --- a/spec/enumbler_spec.rb +++ b/spec/enumbler_spec.rb @@ -110,13 +110,36 @@ class Sky < ApplicationRecord it 'raises an error when the same enumble is added twice' do expect { Color.enumble(:white, 1) }.to raise_error(Enumbler::Error, /twice/) end + it 'raises an error when no numeric id is passed as the second argument' do expect { Color.enumble(:white, label: 'error') }.to raise_error(Enumbler::Error, /numeric/) end + + it 'raises an error when there is a class method naming conflict' do + expect do + Class.new(ApplicationRecord) do + include Enumbler::Enabler + + enumble :transaction, 1 + end + end.to raise_error(Enumbler::Error, /class method `transaction`/) + end + + it 'raises an error when there is a instance method naming conflict' do + expect do + Class.new(ApplicationRecord) do + include Enumbler::Enabler + + enumble :persisted, 1 + end + end.to raise_error(Enumbler::Error, /instance method `persisted\?`/) + end + it 'creates the constants' do expect(Color::BLACK).to eq 1 expect(Color::WHITE).to eq 2 end + it 'creates the class finder methods', :seed do expect(Color.black).to eq Color.find(Color::BLACK) end