I_do_python I_do_python - 1 month ago 11
Ruby Question

How do I access class methods in a define_singleton_method block in Ruby

I am trying to access class methods within a

define_singleton_method
block, but it doesn't seem to be working.

Here is an example.

class Test
attr_accessor :tags

def initialize
@tags = []
@tags.define_singleton_method(:<<) do |val|
val = tosymbol(val)
push(val)
end
end

def tosymbol(value)
value = value.to_s
value = value.gsub!(/\s+/,'_') || value
value = value.downcase! || value

return value.to_sym
end
end


But when I use it I get an error.

test = Test.new
test.tags<<"Hello World"
NoMethodError: undefined method `tosymbol' for []:Array
from /home/joebloggs/GitHub/repo/file.rb:183:in `block in initialize'
from (irb):9
from /home/joebloggs/.rvm/rubies/ruby-2.3.0/bin/irb:11:in `<main>'


I tried changing
val = tosymbol(val)
to
val = Test::tosymbol(val)
but that didn't work either, I get
undefined method 'tosymbol' for Test:Class


I could re-write what
tosymbol
is doing, but it wouldn't be very DRY. Where am I going wrong? Thanks.

Answer

Where am I going wrong?

You're (re)defining a << method for instance of Array class, not Test class.

While doing so you are trying to access tosymbol method, that is not defined in Array class, but in Test class.

What you want, probably (read judging by your code sample), is to define << method for instances of Test class:

def initialize
  @tags = []
end

def <<(val)
  tags << tosymbol(val)
end

test = Test.new
test << "Hello World"
#=> [:hello_world]

EDIT

To make your example to work you just need to assign the instance to a variable and call the tosymbol method with correct receiver:

def initialize
  @tags = []
  test = self # <============
  @tags.define_singleton_method(:<<) do |val|
    val = test.tosymbol(val)
    push(val)
  end
end

Now:

test.tags << 'Hello World'
#=> [:hello_world]
Comments