Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Short scopes and predicates (similar functionality exists in ActiveRecord::Enum). #21

Open
MityaLiu opened this issue Jun 15, 2020 · 2 comments

Comments

@MityaLiu
Copy link
Contributor

MityaLiu commented Jun 15, 2020

@albertosaurus
I'm currently using short scopes and predicates defined as ActiveSupport::Concern.
If I make a PR would you be interested in adding such functionality?
I'm asking because it'll take me a day to port this code and add specs.
It allows the following syntax:

has_enumerated :status, short_scopes: true, predicates: true
# existing => new
booking.with_status(:active) => booking.active
booking.all_except(:active) => booking.not_active
booking.status === :rejected => booking.rejected?

# Extensions for PowerEnum
module PowerEnumEnhancements
  extend ActiveSupport::Concern

  # TODO: Consider making a PR to PowerEnum
  module ClassMethods
    # Dynamically defines predicate methods from enum names
    # @param [Symbol] relation_name
    # @return [Nil]
    private def define_enum_predicates(relation_name)
      reflection = find_reflection_by_name!(relation_name)
      if enum_table_exists?(reflection)
        reflection.klass.names.each do |name|
          define_method("#{name}?") do
            public_send(reflection.name).like?(name)
          end
        end
      end
    end

    # Dynamically defines scopes from enum names
    # @param [Symbol] relation_name
    # @return [Nil]
    private def define_enum_scopes(relation_name)
      reflection = find_reflection_by_name!(relation_name)
      if enum_table_exists?(reflection)
        reflection.klass.names.each do |name|
          scope name, -> { public_send("with_#{reflection.name}", name) }
          scope "not_#{name}", -> { public_send("exclude_#{reflection.name}", name) }
        end
      end
    end

    # Finds a reflection by name
    # @param [Symbol] relation_name
    # @raise [ArgumentError]
    # @return [PowerEnum::Reflection::EnumerationReflection]
    private def find_reflection_by_name!(relation_name)
      reflection = reflect_on_all_associations(:belongs_to).find { |relation| relation.name == relation_name.to_sym }
      if reflection&.class != PowerEnum::Reflection::EnumerationReflection
        raise ArgumentError, "Relation #{relation_name} doesn't exist"
      end

      reflection
    end

    # Checks if there's a DB connection and if given table exists
    # @param [Symbol] relation_name
    # @return [Boolean]
    private def enum_table_exists?(reflection)
      connection.table_exists?(reflection.plural_name)
    rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad
      false
    end
  end
end
@MityaLiu
Copy link
Contributor Author

Thank you.

@albertosaurus
Copy link
Owner

First of all, sorry about the late reply. Otherwise, my big concern with what you're doing is that I think you're forcing enum values to be read at load time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants