Drakken Saer
Drakken Saer

Reputation: 889

Ruby: Send define_method with block arguments

I'm trying to do something like this, where the send method's block has block arguments:

  klass.attribute_names.each do |attribute|
    klass.send(define_method("next_by_" + attribute) { object, user = nil
      if user
        klass.joins(:users).where("#{attribute} > ?", object.send(attribute)).where(users: {id: user.id}).first
      else
        klass.where("#{attribute} > ?", object.send(attribute)).first
      end
    })
  end

Which isn't valid Ruby. Is this possible to do, and if so what is the best way to do so?

EDIT: I realize the reserved keyword "class" here. It is simply for illustration purposes. I've changed it to klass to prevent further confusion.

More detail, this is the entire file:

module Helpers::ResourceRecordHelper

  # Remove this and call upon the module's methods to include instead to fix performance hit
  def self.included(source)
    define_class_methods(source)
  end

  def define_class_methods(source)
    source.attribute_names.each do |attribute|

      # These defined methods never reach the class
      define_method("next_by_" + attribute) { object, user = nil
        if user
          self.class.joins(:users).where("#{attribute} > ?", object.send(attribute)).where(users: {id: user.id}).first
        else
          self.class.where("#{attribute} > ?", object.send(attribute)).first
        end
      }

      define_method("previous_by_" + attribute) do object, user = nil
        if user
          self.class.joins(:users).where("#{attribute} < ?", object.send(attribute)).where(users: {id: user.id}).last
        else
          self.class.where("#{attribute} < ?", object.send(attribute)).last
        end
      end
    end

    def source.next(object, user = nil)
      source.next_by_id(object, user)
    end

    def source.previous(object, user = nil)
      source.previous_by_id(object, user)
    end
  end

  extend self

end

Upvotes: 1

Views: 838

Answers (1)

Andrey Deineko
Andrey Deineko

Reputation: 52347

You have few issues, like using reserved words, not understanding the self (or rather not implementing your understanding correctly).

source.attribute_names.each do |attribute|
  source.singleton_class.instance_eval do
    define_method('next_by_' + attribute) do |object, user = nil|
      if user
        source.joins(:users)
              .where("#{attribute} > ?", object.public_send(attribute))
              .where(users: { id: user.id })
              .first
      else
        source.where("#{attribute} > ?", object.public_send(attribute)).first
      end
    end
  end
end

Upvotes: 1

Related Questions