MicRum
MicRum

Reputation: 1721

Rails concerns: class method with block

I'm trying to write class method my_method (like a validate), that uses code block.

Model contains next code:

class Model < ActiveRecord::Base
  include Usable
  my_method :arg_1, :arg_2

  my_method do |model|
    # just for example 
    if model.attribute.present?
      :arg3
    else
      :arg4
    end
  end
  #...
end

my_method is class method defined in concern:

module Usable
  extend ActiveSupport::Concern

  # instance methods
  def instance_method
    self.class.display_attrs
  end
  # ...

  module ClassMethods
    #  class methods
    def display_attrs
      @attrs_for_display ||= []
    end

    def my_method(*attr_names, &block)
      attr_names.each do |attr|
        attr_data = { attr: attr }
        display_attrs << attr_data
      end

      display_attrs << { attr: block} if block_given? 
    end
  end
end

Instance method @model.instance_method works fine when arguments putted in my_method directly my_method :arg_1, :arg_2

> Model.last.instance_method
=> [{:attr=>:arg_1}, {:attr=>:arg_2}]

But instance_method returns a Proc object when my_method uses block my_method {:arg_3}

> Model.last.instance_method
=>[{:attr=>   #<Proc:0x00 ... >}]

How to put in my_method arguments in according of instance state? For example, @model.instance_method should return :arg_3 if model.attribute.present?

Upvotes: 2

Views: 1073

Answers (1)

MicRum
MicRum

Reputation: 1721

The problem was solved with call method. I rewrote instance_method as follows:

  def instance_method
    class_attrs = self.class.display_attrs.map { |n| n[:attr] }
    class_blocks = self.class.display_attrs.map { |n| n[:block].call(self) if n[:block] }
    (class_attrs + class_blocks).compact!
  end

  module ClassMethods
  #...
       display_attrs << { block: block} if block_given?
  #...
  end

Upvotes: 1

Related Questions