calling a super class' method with a twist - ruby

Suppose I have two classes like so:
class Parent
def say
"I am a parent"
end
end
class Child < Parent
def say
"I am a child"
end
def super_say
#I want to call Parent.new#say method here
end
end
What are the options to do that? I thought of:
def super_say
self.superclass.new.say #obviously the most straight forward way, but inefficient
end
def super_say
m = self.superclass.instance_method(:say)
m = m.bind(self)
m.call
#this works, but it's quite verbose, is it even idiomatic?
end
I am looking for a way which doesn't involve aliasing Parent.new#say to something else, which would make it unique in the method lookup chain (Or is that actually the preferred way?).
Any suggestions?

I tend to prefer using an alias. (I'm not quite sure I understand your objection to it.)
Example:
class Child < Parent
alias :super_say :say
def say
"I am a child"
end
end
Gives:
irb(main):020:0> c = Child.new
=> #<Child:0x45be40c>
irb(main):021:0> c.super_say
=> "I am a parent"

Your second solution (the bind()) is the one i would go for. It is verbose because what you are doing is highly unusual, but if you really need to do it -- that solution seems fine to me.

Related

Metaprogramming in Ruby with derived classes

I'm trying to write a method that prints class variable names and their values. As an example:
class A
def printvars
???
end
end
class <<A
def varlist(*args)
???
end
end
class B < A
varlist :c
def initialize(c)
#c = c
end
b = B.new(10)
b.printvars()
And I would like the output to be c => 10. But I don't know what goes in the ???. I've tried using a self.class_eval in the body of varlist, but that won't let me store args. I've also tried keeping a hash in the class A and just printing it out in printvars, but the singleton class is a superclass of A and so has no access to this hash. So far everything I've tried doesn't work.
I think something similar must be possible, since Rails does something related with its validates_* methods. Ideally I could make this work exactly as expected, but even a pointer to how to print just the variable names (so just c as output) would be most appreciated.
You might like this answer: What is attr_accessor in Ruby?
Basically, as you surmised, varlist needs to be a class method which takes a variable list of arguments (*args). Once you have those arguments you could try any number of things using send, respond_to?, or maybe even instance_variable_get. Note, none of those are really recommended, but I wanted to answer your question a bit.
The other half is that you should probably look into method_missing in order to understand how things like validates_* are working. The * part necessitates that you do something like method_missing because you can't actually do module_eval until you know what you're looking for. In the case of the magic rails methods, you don't necessarily ever know what you're looking for! So we rely on the built in method_missing to let us know what got called.
For funzies, try this in IRB:
class A
def method_missing(method, *args, &block)
puts method, args.inspect
end
end
A.new.banana(13, 'snakes')
A.new.validates_serenity_of('Scooters', :within => [:calm, :uncalm])
Does that help?
Just use Module#class_variables
As far as I can tell, you're vastly over-complicating this. All you need is the pre-defined Module#class_variables method. You can call this directly on the class, or invoke it through self if you want to bind it to an instance of the class. For example:
class Foo
##bar = "baz"
def show_class_variables
self.class.class_variables
end
end
Foo.class_variables
#=> [:##bar]
foo = Foo.new
foo.show_class_variables
#=> [:##bar]

Pass block into define_method

The Problem
There is a pattern that I find myself to be frequently using, so I'd like to dry it up. I have stuff like this:
class InfoGatherer
def foo
true
end
def people
unless #people
#people = # Long and complex calculation (using foo)
end
#people
end
end
I'd like to dry this up to look like this:
class InfoGatherer
extend AttrCalculator
def foo
true
end
attr_calculator(:people) { # Long and complex calculation (using foo) }
end
To accomplish this, I defined a module AttrCalculator to extend into InfoGatherer. Here's what I tried:
module AttrCalculator
def attr_calculator(variable_name_symbol)
variable_name = "##{variable_name_symbol}"
define_method variable_name_symbol do
unless instance_variable_defined?(variable_name)
instance_variable_set(variable_name, block.call)
end
instance_variable_get(variable_name)
end
end
end
Unfortunately, when I try something as simple as InfoGatherer.new.people, I get:
NameError: undefined local variable or method `foo' for InfoGatherer:Class
Well, that's odd. Why is block running in the scope of InfoGatherer:Class, rather than its instance InfoGatherer.new?
The Research
I know I can't use yield, because that would try to catch the wrong block, as seen here.
I attempted to use self.instance_exec(block) in the place of block.call above, but then I received a new error:
LocalJumpError: no block given
Huh? I see the same error in this SO question, but I'm already using bracket notation, so the answers there don't seem to apply.
I also tried to use class_eval, but I'm not sure how to call block inside of a string. This certainly doesn't work:
class_eval("
def #{variable_name_symbol}
unless #{variable_name}
#{variable_name} = #{block.call}
end
#{variable_name}
end
")
That use case is called memoization. It can be done easily like:
def people
#people ||= # Long and complex calculation (using foo)
end
You shouldn't go into the mess like you are.
The problem was that, inside the define_method, self was surprisingly InfoGatherer, rather than an instance of InfoGatherer. So I was on the right track with self.instance_exec(block).
The working solution is self.instance_exec(&block) (note the ampersand). I guess the interpreter doesn't recognize that block is a block unless you label it as such? If anyone can explain this better than me, please do.
As a side note, this is not the best way to solve this particular problem. See #sawa's answer for a clean way to memoize complicated calculations.
To expand on the last persons
def people(varariable = nil)
#people ||= ComplexCalculation.new(variable).evaluate
end
class ComplexCalculation
def initialize(variable)
#variable = variable
end
def evaluate(variable)
#stuff
end
end
By extracting this class you are isolating that complexity and will have a much better experience.

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)

Calling a parent's method from the child

Some code
class Parent
def print
p "Hi I'm the parent"
end
end
class Child < Parent
def initialize(num)
#num = num
end
def print
child_print
end
def child_print
if #num == 1
#call parent.print
else
p "I'm the child"
end
end
end
c1 = Child.new(1)
c2 = Child.new(2)
c1.print
c2.print
Child is an instance of Parent. Print is the method exposed in the interface, and both classes define them. Child decides to do other things in a (possibly really complex) method, but will invoke its parent's method under some condition.
I could just write
def print
if #num == 1
super
else
p "I'm the child"
end
end
And that works, but what if it's not just a simple one-liner comparison but instead is doing lots of complicated things that deserve to be separated into another method? It may have to do some calculations before deciding that the parent's method should be called.
Or perhaps there is a different, better way to design it.
Parent.instance_method(:print).bind(self).call
This is already pretty readable, but here's an explanation.
Get the #print method of the Parent class
Bind it to your current object
Call it
PS: You can even give arguments to #call and they will be relayed to the called method.
PPS: That said, such code almost always hints at an issue in your class design. You should try to avoid it whenever possible.

Detecting when a method is chained

I am looking to do some method chaining. I have the following code:
class MyClass
attr_accessor :handler
def do_a
puts 'i just did a'
self.handler = 'a'
self
end
def do_b_if_a
puts 'i just did b' if handler == 'a'
end
end
So the following works:
irb > test = MyClass.new
=> #<MyClass:0x007fa44ced9a70 #handler=nil>
irb > test.do_a
'i just did a'
irb > test.do_a.do_b_if_a
'i just did a'
'i just did b'
What I DONT want to work is when I call do_a the first time it sets the handler, which means now do_b_if_a can be called at any time. But I only want it to be called when it is chained with do_a, how do I do that?
In general, you don't want to care (and in most cases, you don't even get to know) if your methods are called in a certain way. That way lies madness. Magic call sequences make for a hell of a time debugging and testing.
What you could do, though...instead of having do_a return self, wrap a decorator around it that defines do_b, and return the decorator. At that point, your original MyClass can't do_b, cause it doesn't know how. But the thingie returned from do_a can.
(Now, you can still say like a = test.do_a and then a.do_b, but you can't really get around that without parsing the Ruby code yourself.)
I think you're tracking state in the wrong place. You'd be better off with something similar to ActiveRecord's query interface, for example:
class MyClass
attr_accessor :handler
def do_a
puts 'i just did a'
with_handler 'a'
end
def do_b_if_a
puts 'i just did b' if handler == 'a'
end
private
def with_handler(h)
o = dup
o.handler = h
o
end
end
That way you always have an instance of MyClass but you have a throw-away copy that keeps track of its history. This is similar to cHao's approach but it doesn't need an extra decorator class as MyClass can decorate itself.
As cHao said, you shouldn't try to method call patterns. But I can also think of another way to do this:
def do_a
puts "i just did a"
do_b_if_a(true)
end
def do_b_if_a(did_a=false)
puts "i just did b" if did_a
end
do_a # prints "i just did a"
do_b_if_a # does nothing
Sure, you can call do_b_if_a(true), but then it just makes it more flexible ;)
Doesn't get you all the way there, but you could define do_b_if_a as a singleton method that gets added when to_a is called:
class MyClass
def do_a
puts 'i just did a'
self.tap{|s| def s.do_b_if_a; puts 'i just did b' end}
end
end
You could take a look at Kernel.caller if you want to take certain actions based on the call stack. However, you might get punched in the face by another developer if you don't have a good reason for doing so, lol.

Resources