0

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.

2
  • Try def self.tosymbol, then call Test::tosymbol(val). What are you actually trying to do? Perhaps we can suggest a better way. Commented Oct 31, 2016 at 14:42
  • def self.tosymbol didn't work unfortunately. What I want to do is run a validation method against whatever is pushed to an instance variable. At the moment I'm trying to do that by overriding the instances << method. The problem is that I want to reuse code I have elsewhere, and the define_singleton_method seems not to be able to see anything outside of itself. Commented Oct 31, 2016 at 14:58

1 Answer 1

2

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]
Sign up to request clarification or add additional context in comments.

4 Comments

Sorry, but this isn't working for me. The << on tags just acts like it normally does for an array, nothing in the def <<(val) method is happening (even puts).
@I_do_python sorry, I edited the answer, It should just be test << "Hello World"
Ok, that's working now, thanks. Is there anyway to make this specific to an instance variable though? As presumably this would mean you can only have a single array instance variable in the class. It's also not clear to the user that your appending to the tags variable (i.e. That the result of test << 4 can be found under test.tags).
@I_do_python sure, let me edit the answer to make your try working

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.