views:

199

answers:

5

I'm interested in how one would go in getting this to work :

me = "this is a string"
class << me
  alias :old<< :<<
  def <<(text)
    old<<(text)
    puts "appended #{text}"
  end
end

I'd like that when something gets appended to the me variable, the object will use the redefined method.

If I try to run this, I get syntax error, unexpected ':', expecting kEND at :<<.

+2  A: 

:old<< looks like ":old <<". Try just :old, or if you really want, :"old<<" (but have fun calling it through that name).

ephemient
Suppose I'd really like to call it `:old<<`. Will the only way of calling it be with a `send`?
Geo
I think it's silly -- why not just pick a real name? -- but yes, `send(:"old<<", text)` will work. Offhand, I can't think of any other methods to call something with an odd name like that.
ephemient
Just out of curiosity :)
Geo
I actually did a similar thing the first time I aliased a setter method in Ruby. I named the alias 'old_value='. That won't work for obvious reasons :)
Ed Swangren
+1  A: 

The problem is with :old<<. It gets interpreted as :old <<, i.e. a symbol :old followed by the << operator, so it is a syntax error. Maybe you can try :"old<<"?

newacct
+5  A: 

Only certain characters are allowed in symbol literals. You are looking for:

alias :"old<<" :"<<"
thenduks
+1  A: 

As others have already explained, the problem is simply that old<< is not a legal Ruby identifier. You can, with tricks, create a method with that name, but you can't call it in the normal ways, and it certainly won't be recognized as an operator.

However, all the answers so far, while they have certainly answered your question, have completely ignored the underlying problem: that method shouldn't even have a name in the first place! And if it doesn't have a name, then the problem of the name being illegal simply doesn't even arise.

#!/usr/bin/env ruby

require 'test/unit'
require 'stringio'
class TestOperatorDecorator < Test::Unit::TestCase
  def setup; @old_stdout, $> = $>, (@fake_stdout = StringIO.new) end
  def teardown; $> = @old_stdout end

  def test_that_me_dot_append_writes_to_stdio
    me = 'this is a string'
    class << me
      old_method = instance_method :<<

      define_method :<< do |text|
        old_method.bind(self).(text)
        puts "appended #{text}"
      end
    end

    me << 'Test'

    assert_equal "appended Test\n", @fake_stdout.string
  end
end

In this case, the method never gets named, which not only means that we don't have to invent a name for it, it also means that it doesn't pollute the namespace.

Jörg W Mittag
A: 

While I agree with thenduks and ephemient, You can alias the operator that way then use send to call it, you can also still use class inheritance. e.g.:

me = "is a string"

class << me
  def <<(text)
    super
    puts "appended #{text}"
  end
end

me << " bob"
puts me #=> is a string bob
frogstarr78