0

I have a class called RubyCsvRow, which holds a row of a CSV file, in a hash. I am using method_missing to allow any column to be used as a function to return the value of the row at that column. However, I get a method_missing error when I run I attempt to use it.

I wasn't sure exactly what was happening, so I replaced the call to one with a call to class.

m = RubyCsv.new
m.each{|row| puts row.class}

I edited the method missing in RubyCsvRow so that I could see what happens when it prints and see the name of the missing method:

def self.method_missing(name, *args, &block)
      puts "Called Method Missing"
      puts name.to_s
end

The return only confused me more.

Called Method Missing
to_ary
RubyCsvRow
Called Method Missing
to_ary
RubyCsvRow

It calls method missing. I don't know why it prints name as to_ary, which when I searched I found this, but I am not sure when it is being implicitly converted or why.

I searched around and have looked at these links. The labels where why I thought they didn't fit.

I have my private variable defined as a :attr_accesssor

Mine is a method of a class and I am using it like one

I am calling my method after defining it

I am not sure about this one. I am already converting my symbol to_s, but I had trouble determining if this fit

Why I decided to format my each method in RubyCsv the way I did

class RubyCsvRow
    attr_accessor :values

    def initialize(start)
      @values = start
    end 

    def self.method_missing(name, *args, &block)
      if @values.key?(name.to_s)
        @values[name.to_s]
      else
        puts "No Column with name: ", name.to_s, " found!"
      end
    end

    def to_s
      self.inspect
    end
end

r = RubyCsvRow.new({"one" => "dog", "two" => "cat" })
puts r.one 

RubyCsvRow is used in RubyCsv's each, but I get the same error with just this code. I can post the RubyCsv code, but this is the minimum code to reproduce the error.

I get a NoMethodError for one instead of printing dog.

Ehrlich
  • 3
  • 4

1 Answers1

0

Try to use def method_missing instead of self. You call method on instance not class it self.

If you define method with self you define the method as class not instance. In your code you create new instance of RubyCsvRow Class and you need to define method_missing as instace method.

Modify code here:

class RubyCsvRow
  attr_accessor :values

  def initialize(start)
    @values = start
  end

  # Define method missing on instance
  def method_missing(name, *args, &block)
    return @values[name.to_s] if @values[name.to_s]
    # return my error message
    "No Column with name: #{name} found!"
  end

  def to_s
    inspect
  end
end

r = RubyCsvRow.new({ "one" => "dog", "two" => "cat" })
puts r.one
# => dog
puts r.test
# => "No Column with name: test found!"

BTW: If you need the original error message use super in method_missing method

def method_missing(name, *args, &block)
  return @values[name.to_s] if @values[name.to_s]
  # call original method
  super
end
Lukas Baliak
  • 2,849
  • 2
  • 23
  • 26
  • Would using super be more Ruby? Would it call Object's method_missing? – Ehrlich May 03 '19 at 15:20
  • If you wanna to call original method missing and get original error message, you should call super. This will call the original method from object. – Lukas Baliak May 05 '19 at 15:17