Ruby rescue-like syntax - ruby

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.

Related

Default method for Ruby class

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.

Binding method to instance

Is there a way to bind an existing method to an existing instance of an object if both the method and the instance are passed as symbols into a method that does that if the instance is not a symbol?
For example:
def some_method
#do something
end
some_instance = Klass.new(something)
def method_that_binds(:some_method, to: :some_instance)
#how do I do that?
end
Your requirements are a little unusual, but it is possible to do this mostly as you say:
class Person; end
harry = Person.new
barry = Person.new
def test
puts 'It works!'
end
define_method :method_that_binds do |a_method, to|
eval(to[:to].to_s).singleton_class.send(:define_method, a_method, &Object.new.method(a_method))
end
method_that_binds :test, to: :harry
harry.test
# It works! will be sent to STDOUT
barry.test
# undefined method 'test'
This doesn't actually use a named parameter, but accepts a hash with a to key, but you can see you can call it in the way you want. It also assumes that the methods you are defining are defined globally on Object.
The API you want doesn't easily work, because you have to know from which scope you want to access the local variable. It's not quite clear to me why you want to pass the name of the local variable instead of passing the content of the local variable … after all, the local variable is present at the call site.
Anyway, if you pass in the scope in addition to the name, this can be accomplished rather easily:
def some_method(*args)
puts args
puts "I can access some_instance's ivar: ##private_instance_var"
end
class Foo; def initialize; #private_instance_var = :foo end end
some_instance = Foo.new
def method_that_binds(meth, to:, within:, with: [])
self.class.instance_method(meth).bind(within.local_variable_get(to)).(*with)
end
method_that_binds(:some_method, to: :some_instance, within: binding, with: ['arg1', 'arg2'])
# arg1
# arg2
# I can access some_instance's ivar: foo
As you can see, I also added a way to pass arguments to the method. Without that extension, it becomes even simpler:
def method_that_binds(meth, to:, within:)
self.class.instance_method(meth).bind(within.local_variable_get(to)).()
end
But you have to pass the scope (Binding) into the method.
If you'd like to add a method just to some_instance i.e. it's not available on other instances of Klass then this can be done using define_singleton_method (documentation here.)
some_instance.define_singleton_method(:some_method, method(:some_method))
Here the first use of the symbol :some_method is the name you'd like the method to have on some_instance and the second use as a parameter to method is creating a Method object from your existing method.
If you'd like to use the same name as the existing method you could wrap this in your own method like:
def add_method(obj, name)
obj.define_singleton_method(name, method(name))
end
Let's say we have a class A with a method a and a local variable c.
class A
def a; 10 end
end
c = '5'
And we want to add the method A#a to c.
This is how it can be done
c.singleton_class.send :define_method, :b, &A.new.method(:a)
p c.b # => 10
Explanations.
One way to add a method to an object instance and not to its class is to define it in its singleton class (which every ruby object has).
We can get the c's singleton class by calling the corresponding method c.signleton_class.
Next we need to dynamically define a method in its class and this can usually be accomplished by using the define_method which takes a method name as its first argument (in our case :b) and a block. Now, converting the method into a block might look a bit tricky but the idea is relatively simple: we first transform the method into a Method instance by calling the Object#method and then by putting the & before A.new.method(:a) we tell the interpreter to call the to_proc method on our object (as our returned object is an instance of the Method, the Method#to_proc will be called) and after that the returned proc will be translated into a block that the define_method expects as its second argument.

Generic methods/Default methods in Ruby, when some method is not defined

I want to do something but I'm not sure if it is possible. I want to use "generic methods" or "default methods" in case when some method is called but is not defined. This is a simple example so you can understand my point:
This is the class:
class XYZ
def a
#...
end
def b
#...
end
end
The instance of the class XYZ:
n = XYZ.new
n.a
n.b
n.c
As you can see, I'm calling the method "c" which is not defined and it will throw an error. Can I do something in the class XYZ so when someone calls a method not defined get the name of the method and do something, in base of the name of the method? And, is this possible in another languages (not making a compiler)? If this is possible, how is it called (theory speaking)?
Use method_missing:
class XYZ
def a; end
def b; end
def method_missing(name, *args)
"Called #{name} with args: #{args}"
end
end
XYZ.new.c #=> "Called c"
You should also define respond_to_missing? to get respond_to? to work nicer in 1.9.2+. You should read more about respond_to?/respond_to_missing? when using method_missing.
This, by the way, would be considered to be metaprogramming. This isn't typically possible in compiled languages because of the way they call functions.
its called method_missing.
When you call a method that is not defined on object, ruby redirects the call to method_missing method which raises the error for you.
you can do this:
class XYZ
def method_missing(method, *args, &blck)
puts "called #{method} with arguments #{args.join(',')}"
end
end
now instead of error you will get output to your console.

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