Can Ruby operators be aliased? - ruby

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 :<<.

Only certain characters are allowed in symbol literals. You are looking for:
alias :"old<<" :"<<"

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

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.

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<<"?

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 appended bob

Related

method_missing with unquoted string arguments in Ruby - possible?

I'm learning Ruby and want to be able to do this:
Printer.hi there
and have Ruby output
"hi there"
So far I have the following implementation
class Printer
def method_missing(name, *args)
puts "#{name} #{args.join(',')}"
end
end
But this only lets me do
Printer.hi "there"
If I attempt
Printer.hi there
I get a
NameError: undefined local variable or method `there' for main:Object
which makes sense as I haven't ever defined 'there'. Is there a way to make this work though?
No, this is not possible in the form given (as far as I know).
You aren't looking for method missing, you are looking for the equivalent in the Ruby interpreter to capture when it cannot find a given symbol. So while you cannot intercept it there, you can do it inside of a block:
def hi(&block)
begin
yield
rescue NameError => e
e.message =~ /variable or method `(.+)'/
puts "hi #{$1}"
end
end
hi { there } # => hi there
Please note that I feel like a terrible world citizen for showing you this. Please don't use it anywhere, ever.
Yes, there is a way. When you write there without an explicit receiver, the receiver is the self object of that scope. In this case, it is main. Define methods_missing in the main context.
def method_missing(name, *args)
puts "#{name} was called with arguments: #{args.join(',')}"
end
But if you do so, that would mess up the rest of your code, perhaps. I see not point in doing this.
Since the return value of puts is nil, if you do Printer.hi there, it will evaluate to Printer.hi(nil). So in order for it to output "hi there", you need to define:
class Printer
def self.hi _; puts "hi there" end
end
No because strings need to be quoted, so they are not seen as variables.
Otherwise variables such as there would need some special sort of character to indicate that it is a string. However this still wouldn't work well as spaces would then need to be dealt with.
Use single or double quotes.
It's how the language works. accept this and move on to the next challenge :)
Interestingly you can do this in ruby 1.8.7 with just this code:
def method_missing(*args)
puts args.join ' '
end
I learned about this from Gary Bernhardt's talk, Wat. In 1.9 this gives you a stack level too deep error unless you do it inside a class. Google lead me to this post on Aurthur's tech blog thing, which claims you can do something similar in JRuby 1.9 mode:
def method_missing(*args)
puts [method.to_s, args].flatten.join ' '
end
However when I tried this on MRI 1.9.3 it did not work either. So in 1.9 you can't quite do what you want. Here is the closest I could come:
class Printer
def self.hi(message)
puts "hi #{message}"
end
def self.method_missing(m, *args)
[m.to_s, args].flatten.join ' '
end
def self.bare
hi there
end
end
Printer.bare

What is the point of using "send" instead of a normal method call?

as far as I understand 'send' method, this
some_object.some_method("im an argument")
is same as this
some_object.send :some_method, "im an argument"
So what is the point using 'send' method?
It can come in handy if you don't know in advance the name of the method, when you're doing metaprogramming for example, you can have the name of the method in a variable and pass it to the send method.
It can also be used to call private methods, although this particular usage is not considered to be a good practice by most Ruby developers.
class Test
private
def my_private_method
puts "Yay"
end
end
t = Test.new
t.my_private_method # Error
t.send :my_private_method #Ok
You can use public_send though to only be able to call public methods.
In addition to Intrepidd's use cases, it is convenient when you want to route different methods on the same receiver and/or arguments. If you have some_object, and want to do different things on it depending on what foo is, then without send, you need to write like:
case foo
when blah_blah then some_object.do_this(*some_arguments)
when whatever then some_object.do_that(*some_arguments)
...
end
but if you have send, you can write
next_method =
case foo
when blah_blah then :do_this
when whatever then :do_that
....
end
some_object.send(next_method, *some_arguments)
or
some_object.send(
case foo
when blah_blah then :do_this
when whatever then :do_that
....
end,
*some_arguments
)
or by using a hash, even this:
NextMethod = {blah_blah: :do_this, whatever: :do_that, ...}
some_object.send(NextMethod[:foo], *some_arguments)
In addition to everyone else's answers, a good use case would be for iterating through methods that contain an incrementing digit.
class Something
def attribute_0
"foo"
end
def attribute_1
"bar"
end
def attribute_2
"baz"
end
end
thing = Something.new
3.times do |x|
puts thing.send("attribute_#{x}")
end
#=> foo
# bar
# baz
This may seem trivial, but it's occasionally helped me keep my Rails code and templates DRY. It's a very specific case, but I think it's a valid one.
The summing briefly up what was already said by colleagues: send method is a syntax sugar for meta-programming. The example below demonstrates the case when native calls to methods are likely impossible:
class Validator
def name
'Mozart'
end
def location
'Salzburg'
end
end
v = Validator.new
'%name% was born in %location%'.gsub (/%(?<mthd>\w+)%/) do
# v.send :"#{Regexp.last_match[:mthd]}"
v.send Regexp.last_match[:mthd].to_sym
end
=> "Mozart was born in Salzburg"
I like this costruction
Object.get_const("Foo").send(:bar)

Ruby class inheritance: What is `<<` (double less than)?

class << Awesomeness
What is this << for? I searched, but the results only tell me about string concatenation...
While it's true that class << something is the syntax for a singleton class, as someone else said, it's most often used to define class methods within a class definition. But these two usages are consistent. Here's how.
Ruby lets you add methods to any particular instance by doing this:
class << someinstance
def foo
"Hello."
end
end
This adds a method foo to someinstance, not to its class but to that one particular instance. (Actually, foo is added to the instance's "singleton class," but that's more or less an implementation quirk.) After the above code executes, you can send method foo to someinstance:
someinstance.foo => "Hello."
but you can't send foo to other instances of the same class. That's what << is nominally for. But people more commonly use this feature for syntactic gymnastics like this:
class Thing
def do_something
end
class << self
def foo
puts "I am #{self}"
end
end
end
When this code -- this class definition -- executes, what is self? It's the class Thing. Which means class << self is the same as saying "add the following methods to class Thing." That is, foo is a class method. After the above completes, you can do this:
t = Thing.new
t.do_something => does something
t.class.foo => "I am Thing"
t.foo => NoMethodError: undefined method `foo'
And when you think about what << is doing, it all makes sense. It's a way to append to a particular instance, and in the common case, the instance being appended to is a class, so the methods within the block become class methods.
In short, it's a terse way to create class methods within a class definition block. Another way would be to do this:
class Thing
def self.foo
# ...
end
end
Same thing. Your example is actually a syntax error, but if you understand how << is used with instances and the class keyword, you'll know how to correct it.
<< is the syntax for "Singleton class definition". Here is an example of where/how it is "typically" used.
In a = "abc"; a << "xyz" it is the syntax for "appending data" (to string, array etc.)
If you want inheritance (based on your question title), you want a single <:
class Awesome < ParentAwesomeness
The code you provide isn't valid ruby:
class Awesomeness
end
class Awesome << Awesomeness
end
SyntaxError: (irb):3: syntax error, unexpected tLSHFT, expecting '<' or ';' or '\n'

How do I use class_eval?

I don't understand class_eval.
class Module
def attr_ (*syms)
syms.each do |sym|
class_eval %{def #{sym}= (val)
##{sym} = val
end}
end
end
end
What does the % mean?
What does class_eval do?
And where is (val) coming from?
The short answer is: you probably want to avoid using class_eval like this.
Here's an explanation of your code:
The %{hello} is just another way to write a string literal in Ruby, without having to worry about escaping double or single quotes within the string:
%{hello "world"} == "hello \"world\"" # => true
The val in your code is an argument of the method being defined.
The class_eval is used to define some methods by computing the text one would write to do the definition and then evaluating it. It is not necessary here, BTW. An equivalent code would be:
class Module
def attr_ (*syms)
syms.each do |sym|
define_method "#{sym}=" do |val|
instance_variable_set "##{sym}", val
end
end
end
end
This is just equivalent to the builtin attr_writer.
Update: There can actually be a significant difference between the two...
The class_eval version is vulnerable if you can't trust the argument syms. For example:
class Foo
attr_ "x; end; puts 'I can execute anything here!'; val=42; begin; val"
end
The class_eval version will print "I can execute anything here" twice, proving it can execute anything. The define_method version won't print anything.
This type of code was pivotal to create major vulnerability for all installed Rails apps.

Ruby core extensions with modules

Basically I have two modules: CoreExtensions::CamelcasedJsonString and …::CamelcasedJsonSymbol. The latter one overrides the Symbol#to_s, so that the method returns a String which is extended with the first module. I don't want every string to be a CamelcasedJsonString. This is the reason why I try to apply the extension instance specific.
My problem is, that Symbol#to_s seems to be overridden again after I included my module (the last spec fails):
require 'rubygems' if RUBY_VERSION < '1.9'
require 'spec'
module CoreExtensions
module CamelcasedJsonString; end
module CamelcasedJsonSymbol
alias to_s_before_core_extension to_s
def to_s(*args)
to_s_before_core_extension(*args).extend(CamelcasedJsonString)
end
end
::Symbol.send :include, CamelcasedJsonSymbol
end
describe Symbol do
subject { :chunky_bacon }
it "should be a CamelcasedJsonSymbol" do
subject.should be_a(CoreExtensions::CamelcasedJsonSymbol)
end
it "should respond to #to_s_before_core_extension" do
subject.should respond_to(:to_s_before_core_extension)
end
specify "#to_s should return a CamelcasedJsonString" do
subject.to_s.should be_a(CoreExtensions::CamelcasedJsonString)
end
end
However the following example works:
require 'rubygems' if RUBY_VERSION < '1.9'
require 'spec'
module CoreExtensions
module CamelcasedJsonString; end
end
class Symbol
alias to_s_before_core_extension to_s
def to_s(*args)
to_s_before_core_extension(*args).extend(CoreExtensions::CamelcasedJsonString)
end
end
describe Symbol do
subject { :chunky_bacon }
it "should respond to #to_s_before_core_extension" do
subject.should respond_to(:to_s_before_core_extension)
end
specify "#to_s should return a CamelcasedJsonString" do
subject.to_s.should be_a(CoreExtensions::CamelcasedJsonString)
end
end
Update: Jan 24, 2010
The background of my problem is that I try to convert a huge nested hash
structure into a JSON string. Each key in this hash is a Ruby Symbol in the
typical underscore notation. The JavaScript library which consumes the JSON
data expects the keys to be strings in camelcase notation. I thought that
overriding the Symbol#to_json method might be the easiest way. But that
didn't work out since Hash#to_json calls first #to_s and afterwards
#to_json on each key. Therefore I thought it might be a solution to extend
all Strings returnd by Symbol#to_s with a module which overrides the
#to_json method of this specific string instance to return a string that has
a #to_json method which returns itself in camelcase notation.
I'm not sure if there is an easy way to monkey patch Hash#to_json.
If someone wants to take a look into the JSON implementation I'm using, here is the link: http://github.com/flori/json/blob/master/lib/json/pure/generator.rb (lines 239 and following are of interest)
Your second monkeypatch works since you are re-opening the Symbol class.
The first one doesn't because all the include does is add the module in the list of included modules. These get called only if the class itself doesn't define a specific method, or if that method calls super. So your code never gets called.
If you want to use a module, you must use the included callback:
module CamelcasedJsonSymbol
def self.included(base)
base.class_eval do
alias_method_chain :to_s, :camelcase_json
end
end
def to_s_with_camelcase_json(*args)
to_s_without_camelcase_json(*args).extend(CamelcasedJsonString)
end
end
I've used active_record alias_method_chain, which you should always do when monkey patching. It encourages you to use the right names and thus avoid collisions, among other things.
That was the technical answer.
On a more pragmatic approach, you should rethink this. Repeatedly extending strings like this is not nice, will be a huge performance drain on most implementations (it clears the whole method cache on MRI, for instance) and is a big code smell.
I don't know enough about the problem to be sure, or suggest other solutions (maybe a Delegate class could be the right thing to return?) but I have a feeling this is not the right way to arrive to your goals.
Since you want to convert the keys of a hash, you could pass an option to #to_json and monkeypatch that instead of #to_s, like:
{ :chunky_bacon => "good" }.to_json(:camelize => true)
My first idea was to monkeypatch Symbol#to_json but that won't work as you point out because Hash will force the keys to strings before calling to_json, because javascript keys must be strings. So you can monkeypatch Hash instead:
module CamelizeKeys
def self.included(base)
base.class_eval do
alias_method_chain :to_json, :camelize_option
end
end
def to_json_with_camelize_option(*args)
if args.empty? || !args.first[:camelize]
to_json_without_camelize_option(*args)
else
pairs = map do |key, value|
"#{key.to_s.camelize.to_json(*args)}: #{value.to_json(*args)}"
end
"{" << pairs.join(",\n") << "}"
end
end
end
That looks kind of complicated. I probably don't understand what it is you're trying to achieve, but what about something like this?
#!/usr/bin/ruby1.8
class Symbol
alias_method :old_to_s, :to_s
def to_s(*args)
if args == [:upcase]
old_to_s.upcase
else
old_to_s(*args)
end
end
end
puts :foo # => foo
puts :foo.to_s(:upcase) # => FOO
and a partial spec:
describe :Symbol do
it "should return the symbol as a string when to_s is called" do
:foo.to_s.should eql 'foo'
end
it "should delegate to the original Symbol.to_s method when to_s is called with unknown arguments" do
# Yeah, wish I knew how to test that
end
it "should return the symbol name as uppercase when to_s(:upcase) is called" do
:foo.to_s(:upcase).should eql "FOO"
end
end

Resources