Default method for Ruby class - ruby

Is there a way to specify a class method such that when the object is used as if it were a function, that method is called? Something like this:
class MyClass
def some_magic_method(*args)
# stuff happens
end
end
# create object
myob = MyClass.new
# implicitly call some_magic_method
myob 'x'

You could write a command class and make use of a ruby shortcut
class MyClass
def self.call(text)
puts text
end
end
MyClass.('x')
Here MyClass.() defaults to the call class method.

As mentioned by #CarySwoveland in the comments you can use method_missing. A basic example is as follows:
class MyClass
def method_missing(method_name, *args)
if method_name.match?(/[xyz]/)
send(:magic_method, args.first)
else
super
end
end
def magic_method(a)
a = 'none' if a.nil?
"xyz-magic method; argument(s): #{a}"
end
end
myob = MyClass.new
myob.x #=> "xyz-magic method; argument(s): none"
myob.x(1) #=> "xyz-magic method; argument(s): 1"
myob.y #=> "xyz-magic method; argument(s): none"
myob.z #=> "xyz-magic method; argument(s): none"
This captures all methods named x, y or z. Our else branch sends all other undefined methods to the original method_missing:
myob.v #=> test.rb:7:in `method_missing': undefined method `v' for
#<MyClass:0x000000021914f8> (NoMethodError)
#from test.rb:25:in `<main>'
What methods you capture is up to you and is determined by the regex /[xyz]/ in this case.
Key methods: BasicObject#method_missing, Object#send. For further info check out this question, read Eloquent Ruby by Russ Olsen (from which this answer references)

You meant to invoke some class' instance method when the object is invoked as a function. This is already supported: instance method call gets called when you "invoke" an object via the functional invocation method () (for more details, see here How do I reference a function in Ruby?).
class C
def call(x)
puts "Called with #{x}"
end
end
obj = C.new
obj.(88) # Called with 88 => nil
obj (88) # NoMethodError: undefined method `obj' for main:Object
If you do want the latter syntax, a horrible trick is the following one (but works only at the top-level, unless you carry along the bindings):
module Kernel
def method_missing(name,*args)
obj = begin
TOPLEVEL_BINDING.local_variable_get(name)
rescue
nil
end
return super if obj.nil?
obj.send :call, *args
end
end
obj = C.new
obj 88 # Called with OK => nil
This example also wants to communicate that you should always keep in mind
who is the receiver of your method calls, and what syntaxes are available for calling methods (especially when you leave out dots and parentheses).
class D
def obj; C.new end
def f
#(obj) 88 # BAD
(obj).(88)
#obj() 88 # BAD
obj().(88)
end
end
The point is that you do not actually have functions, but methods that get called on objects. If you omit the receiver of a method call, the receiver defaults to self, the current object. But in your example, myob does not appear as an explicit receiver (since there is not following dot as in myob.), hence the current object is looked for a method myob.

Related

Ruby - How is MatchData object created without .new?

I'm creating a class (say, Bar) for an object to be returned by a method of another class (say, Foo#bar), pretty much MatchData object is returned by Regexp#match.
But the class MatchData does not have .new!
I know I don't need to imitate MatchData implementation, but I'd like to understand it and know how to do it when I find it interesting. Suppose I don't want clients creating Bar objects unless by calling Foo#bar.
Questions:
Internally, how is MatchData object created without .new?
How can I implement it (imitating MatchData or not)?
The MatchData.new method is being explicitly undefined:
rb_cMatch = rb_define_class("MatchData", rb_cObject);
rb_define_alloc_func(rb_cMatch, match_alloc);
rb_undef_method(CLASS_OF(rb_cMatch), "new"); // <- here
You can do the same in pure Ruby via undef_method:
class Bar
class << self
undef_method :new
end
def initialize
#bar = '123' # <- for demonstration purposes
end
end
Trying to call Bar.new will now result in an error:
Bar.new #=> undefined method `new' for Bar:Class (NoMethodError)
To create a new instance without a new method, you can call allocate manually (and maybe initialize, too):
bar = Bar.allocate #=> #<Bar:0x007f9eba047cd8>
Bar.send(:initialize) #=> "123"
bar #=> #<Bar:0x007fd8e0847658 #bar="123">
(send is needed because initialize is private)
Let me start with that you shouldn't. It's arubyic to go out of your way to constraint users to do what they want to do even if it's not public interface. A more idiomatic approach would be make it more explicit that it's not part of the public interface. You can do that by making the class private:
class RegexMockery
class MatchDataMockery
def initialize(whatever)
puts "I'm being created #{whatever}"
end
def [](_)
'42'
end
end
private_constant :MatchDataMockery
def match(string)
MatchDataMockery.new(string)
end
end
match_result = RegexMockery.new.match('foo')
# I'm being created foo
# => #<RegexMockery::MatchDataMockery:0x007fe990de2ed0>
match_result[0] # => '42'
RegexMockery::MatchDataMockery # !> NameError: private constant RegexMockery::MatchDataMockery referenced
But if you insist on people hating you, save the method, undef it and call it whenever you want to create instances:
class Foo
def initialize(whatever)
puts "Now you see me #{whatever}"
end
def brag
puts "I can create Foos and you can't!!!1!!"
end
end
class Bar
foos_new = Foo.method(:new)
Foo.singleton_class.send :undef_method, :new
define_method(:sorcery) do
foos_new.call('bar').brag
end
end
Bar.new.sorcery
# Now you see me bar
# I can create Foos and you can't!!!1!!
Foo.new # !> NoMethodError: undefined method `new' for Foo:Class

Why am I getting 'private method new' error only inside a class method definition

I have this Ruby code where I try to implement the Singleton pattern manually:
class A
#a = A.new
def self.instance
p 'initialized'
#a
end
private_class_method :new
end
A.instance #=> prints 'initialized'
Unfortunately, the object will be created before A.instance is even called. To avoid this, I thought of changing the code:
class A
#a = nil
def self.instance
p 'initialized'
#a ||= A.new
end
private_class_method :new
end
A.instance
I get "private method `new' called for A:Class (NoMethodError)" error though. This is very puzzling, why do I get this error in the second example and not in the first? The only difference is that in the second example .new is called in a class method definition. I deliberately put private_class_method on the bottom so this kind of error is prevented (putting it on the top will give the error for both examples). Btw, I'm aware this will work if I change #a from being a class instance variable to a class variable (to start with ##). I don't understand why this would work, since I know instance variables are relative to SELF and SELF is the class, both where I initialize #a to nil and where I lazy instantiate it in self.instance.
Here is a strange thing.
A.new doesn't work because you should call private method only directly, so you should use explicit new call.
In the other hand new call is something like implicit self.new but self.new will raise an exception like A.new. And this is the strange part I don't understand
class A
def self.instance
p 'initialized'
# ok
new
# should be ok but it is not
self.new rescue p("error self.new: #{$!}")
# should fail
A.new rescue p("error A.new: #{$!}")
end
private_class_method :new
end
A.instance
# "initialized"
# "error self.new: private method `new' called for A:Class"
# "error A.new: private method `new' called for A:Class"
PS: http://weblog.jamisbuck.org/2007/2/23/method-visibility-in-ruby
You can't use explicit receiver with private methods
SO: Understanding private methods in Ruby

Ruby rescue-like syntax

I would like to implement something with a rescue-like syntax.
begin
raise "Failed"
rescue Exception => e
puts e
end
This works, and e is assigned to the corresponding value. But used in a method, this will raise an exception saying that e is undefined. In other words, how can rescue assigns something to e this way without throwing an undefined error?
class MyClass
def to_s
"hello"
end
end
def my_method
puts e.to_s
end
my_method(MyClass => e)
#=> undefined local variable or method `e' for main:Object
Perhaps what you are looking for is:
class MyClass
def self.hello
puts "This is a class method."
end
def bye
puts "This is an instance method."
end
end
def my_method(params)
klass = params[:class]
puts klass.hello # Call a class method
inst = klass.new # Create an instance
puts inst.bye # Call an instance method
end
my_method(:class => MyClass)
Three things to note:
Although the rescue syntax and the "named parameter" syntax look the same, all they have in common is the => operator. In the first case, you are telling to rescue the Exception "into" the variable e, effectively storing it in that variable. In the second case, you are telling Ruby to collect all parameters passed to the method and store them in a hash, using the supplied key/value pairs. Effectively, you are storing MyClass in the params hash, under the key :class.
In your above example, the to_s definition will not be callable on MyClass itself, because you defined it as an instance method. Instance methods are only available when you create an "instance" of the class with inst = MyClass.new. Then, you can call inst.to_s. Think of the class as an abstract "type" of thing, and of the instance as a concrete thing of that type. If you want the method to be available on the class, not the instances, you need to prefix it with self. I have illustrated the two different syntaxes above.
Again in your example, you are using def MyClass, which Ruby will interpret as "define a method with the name MyClass". If you want to define a class, you need to use class MyClass instead.
Hope this clarifies things a bit.

Ruby: why does puts call to_ary?

I'm learning metaprogramming in Ruby and am just trying out defining missing methods via method_missing and define_method. I'm getting some unexpected behaviour and am wondering if anyone can explain this. Here is my class:
class X
def method_missing(m, *args, &block)
puts "method #{m} not found. Defining it."
self.class.send :define_method, m do
puts "hi from method #{m}"
end
puts "defined method #{m}"
end
end
Now, this code:
x = X.new
x.some_method
puts
x.some_method
puts
puts x
Produces the output:
method some_method not found. Defining it.
defined method some_method
hi from method some_method
method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>
What I don't get is the last part: why is Ruby calling to_ary in a call to puts? Why would Ruby try to convert my object into an array just to print it?
I've Googled around and found these related links:
http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
These also talk about method_missing and to_ary gotchas, but not specifically about why puts would call to_ary.
I should also mention that the behaviour does not change when I define a to_s, e.g.
def to_s
"I'm an instance of X"
end
The output of "puts x" is then:
method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
puts is a synonym for $stdout.puts. $stdout is an IO class, so look at the documentation for IO.puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line.
This mean that puts method is intended to write several lines of output. Thus it tries to call to_ary method on an object and if to_ary is defined, then prints each element of the returned Array on a new line, else puts calls to_s method.
to_ary internal usage is really not well documented in the Ruby documentation (Matz points this out in his The Ruby Programming Language book).
Methods print and p on the other hand don't call to_ary, only to_s.
Sidenote: Interesting, that to_ary must return real Array object, not an object defining each method or something else:
class Test
def to_ary
10.downto(1)
end
end
puts Test.new
#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
# from (irb):28:in `puts'
# from (irb):28:in `puts'
# from (irb):28

I want to add a singleton method with a closure to a Ruby object

I wish to add a singleton method to a particular object. I wish that when a instance method on a object is first called, it does some work, and then creates a singleton method for said object of the same name (that contains the work). On all subsequent calls on said object, the singleton method would shadow the instance method and would be called.
I know how to create a singleton method, my problem is that I want the singleton method created to call a lambda (l in this case). def does not create a closure, so I cannot reference variable l (code below) when the method is subsequently called (l.call() is commented out in this example) I wish to know how I can create a closure when creating a singleton method on a particular object. Any help would be appreciated. Thank you.
class Thing
end
t = Thing.new
t2 = Thing.new
Thing.instance_eval() do
def speak
puts "I speak for all Things, I am a class method"
end
end
Thing.class_eval() do
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
l = lambda {puts r}
instance_eval() do
def speak()
puts "This is the singleton method in the Thing object #{self}"
#l.call() # I want this to work! How?
end
end
end
end
Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()
Gives the following results when run: (I changed '<' to '#' so they show up in html)
I speak for all Things, I am a class
method
This is the instance method referenced
by the Thing object #Thing:0x1d204>
This is the instance method referenced
by the Thing object #Thing:0x1d1dc>
This is the singleton method in the
Thing object #Thing:0x1d204>
This is the singleton method in the
Thing object #Thing:0x1d1dc>
You can define a method with a block using define_method.
Example:
class Object
def eigenclass
class <<self; self end
end
end
a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String
Here is an implementation of a define_singleton_method method:
class Object
def define_singleton_method(name, &block)
eigenclass = class<<self; self end
eigenclass.class_eval {define_method name, block}
end
end
Now that 1.9 is out, you can use define_singleton_method:
jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f#(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f#(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80
Well, one way to do it would be to pack it into an instance variable:
(FYI you can just do class Thing to reopen Thing (it's a little shorter than using #class_eval, and you don't need #instance_eval to define methods from within a method).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
#l = lambda {puts r}
instance_eval do
def speak()
puts "This is the singleton method in the Thing object #{self}"
#l[]
end
end
end
end
This will redefine #speak, but only for that instance of Thing. Other instances of Thing will still have the original definition.
The alternative is, as Chuck pointed out, to use the singleton class (aka metaclass, aka eigenclass) associated with the instance. The singleton class is the object that stores all the singleton methods associated with an object. You can get the context for singleton class evaluation by using the funny class <<object ; ... ; end syntax (similar to the context given by #class_eval by normal classes).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
singleton_class = class <<self # open singleton class context for current instance
# in this context, self now refers to the singleton class itself
self
end
l = lambda {puts r}
singleton_class.class_eval do
# since we used #class_eval, local variables are still in scope
define_method(:speak) do
puts "This is the singleton method in the Thing object #{self}"
# since we used #define_method, local variables are still in scope
l[]
end
end
end
end

Resources