According to the documentation for modules and classes, calling super (without arguments or parentheses) calls the parent method with the same arguments:
When used without any arguments super uses the arguments given to the subclass method.
Assigning a new value to the "argument variable" seems to alter this behavior:
class MyClass
def foo(arg)
puts "MyClass#foo(#{arg.inspect})"
end
end
class MySubclass < MyClass
def foo(arg)
puts "MySubclass#foo(#{arg.inspect})"
super
arg = 'new value'
super
end
end
MySubclass.new.foo('inital value')
Output:
MySubclass#foo("inital value")
MyClass#foo("inital value")
MyClass#foo("new value") # <- not the argument given to MySubclass#foo
Is this expected?
Update
This seems to be the expected behavior for positional and keyword arguments, but it doesn't work for block arguments:
class MyClass
def foo(&block)
puts "MyClass#foo { #{block.call.inspect} }"
end
end
class MySubclass < MyClass
def foo(&block)
puts "MySubclass#foo { #{block.call.inspect} }"
super
block = Proc.new { 'new value' }
super
end
end
MySubclass.new.foo { 'initial value' }
Output:
MySubclass#foo { "initial value" }
MyClass#foo { "initial value" }
MyClass#foo { "initial value" }
Lets take one example from the Ruby core:
Keyword2
class Base
def single(a) a end
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
def keyword(**a) a end
end
class Keyword2 < Base
def keyword(foo: "keyword2")
foo = "changed1"
x = super
foo = "changed2"
y = super
[x, y]
end
end
Now, see the test case :-
def test_keyword2
assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end
Above example exactly mathes the keyword documentation.
Called with no arguments and no empty argument list, super calls the appropriate method with the same arguments, and the same code block, as those used to call the current method. Called with an argument list or arguments, it calls the appropriate methods with exactly the specified arguments (including none, in the case of an empty argument list indicated by empty parentheses).
same arguments means it is saying the current values of argument variables.test_super.rb files contains all the varieties of stuffs we can do with super in Ruby.
No, it work with block too (taken from core) :
a = Class.new do
def foo
yield
end
end
b = Class.new(a) do
def foo
super{
"b"
}
end
end
b.new.foo{"c"} # => "b"
But, have no idea why the below is giving "c"? This is actually the updated question of the OP:
c = Class.new do
def foo(&block)
block.call
end
end
d = Class.new(c) do
def foo(&block)
block = -> { "b" }
super
end
end
d.new.foo{"c"} # => "c"
It seems to be the expected behavior, based on the RubySpec anyway.
module RestArgsWithSuper
class A
def a(*args)
args
end
end
class B < A
def a(*args)
args << "foo"
super
end
end
end
(language/fixtures/super.rb).
It's then expected that the arguments are modified:
it "passes along modified rest args when they weren't originally empty" do
Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
end
(language/super_spec.rb)
It's the expected behaviour. Technically, arg is the same argument, it just points to another value.
This answer might explain it better: https://stackoverflow.com/a/1872159/163640
Related
hey I want that my method logify puts each method with its parameters and return value of my class A. I wrote for example a simple class A with two methods add and sub and the output should look like that:
Output:
Method add(1, 2) called
return value 3
Method sub(1, 2) called
return value -1
I know that I can get each method with self.instance_methods(false) but can someone please help me further?
require_relative "log"
class A
extend Log
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
logify
end
a = A.new
a.add(2,1)
a.sub(2,1)
module Log
def logify
puts self.instance_methods(false)
end
end
You can use Module#prepend and Module#prepended to help with this like so:
module Log
def self.prepended(base)
base.instance_methods(false).each do |m|
define_method(m) do |*args, &block|
puts "Method #{m}(#{args.join(',')}) called"
val = super(*args, &block)
puts "return value #{val}"
val
end
end
end
end
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
A.prepend(Log)
What this does is it defines a method in the prepended module with the same name as the original then builds your output and delagets to the original method in the middle (super) to obtain the return value.
Examples
a = A.new
a.add(2,1)
# Method add(2,1) called
# return value 3
#=> 3
a.sub(2,1)
# Method sub(2,1) called
# return value 1
#=> 1
Caveat: this will only show the provided arguments and will not output default arguments in the method signature
The ruby core library includes the class TracePoint, which can be used to trace just about anything - from methods being defined, or invoked, or exceptions being raised, ...
Here is an example usage, which will perform the tracking you desired:
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
TracePoint.trace(:call, :return) do |tp|
next unless tp.defined_class == A
case tp.event
when :call
params = tp.parameters.map { |arg| eval(arg[1].to_s, tp.binding) }
puts "Method #{tp.method_id}(#{params.join(', ')}) called"
when :return
puts "return value #{tp.return_value}"
end
end
# The trace has been enabled! Any time one of those events occurs, the block is evaluated.
a = A.new
a.add(2,1)
a.sub(2,1)
Output:
Method add(2, 1) called
return value 3
Method sub(2, 1) called
return value 1
Fetching the params data is, as you can see, a little troublesome. TracePoint has access to the method signature, but you need to make use of the trace's binding to see what values it's actually been called with.
How can a parent get the constructor arguments of a child ?
class A
include Parent
def initialize(foo, bar)
#foo = foo
#bar = bar
end
end
class B
include Parent
def initialize(foo)
#foo = foo
end
end
module Parent
def print_args
# here is the code for print args of child, this is not real code
puts Child.args # this is not real code
end
end
The expected behavior would be :
a = A.new('hello', 'world')
a.print_args
=> "hello world"
b = B.new('hello')
b.print_args
=> "hello"
The Parent module should not now the args names
One way is to have the "children" implement a method that returns their arguments:
class A
include Parent
def initialize(foo, bar)
#foo = foo
#bar = bar
end
def args
[#foo, #bar]
end
end
class B
include Parent
def initialize(foo)
#foo = foo
end
def args
[#foo]
end
end
The "parent" can than call that method without having to know its implementation:
module Parent
def print_args
puts args.join(' ')
end
end
If your module is included in many classes and you want to display instance variable values space separated, then you can do as follow,
using only ruby,
def print_args
instance_variables.map { |x| instance_variable_get(x) }.join(' ')
end
using rails,
def print_args
instance_values.values.join(' ')
end
You're asking how to get the "constructor arguments from the parent" and since almost everything is possible in Ruby: if you're really adventurous (read: don't do this), you can override the new method upon including Parent in order to intercept its arguments and define a singleton method on the instance which prints the argument:
module Parent
def self.included(mod)
def mod.new(*args)
super.tap do |instance|
instance.define_singleton_method(:print_args) do
puts args.join(' ')
end
end
end
end
end
Example usage:
class A
include Parent
def initialize(foo, bar)
end
end
A.new('hello', 'world').print_args
# prints "hello world"
The instance doesn't even have to store the arguments in instance variables.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
ruby: can I have something like Class#inherited that's triggered only after the class definition?
class A
def self.inherited(child)
puts "XXX"
end
end
class B < A
puts "YYY"
end
prints out
XXX
YYY
I'd prefer
YYY
XXX
if I could get it somehow.
You can trace until you find the end of the class definition. I did it in a method which I called after_inherited:
class Class
def after_inherited child = nil, &blk
line_class = nil
set_trace_func(lambda do |event, file, line, id, binding, classname|
unless line_class
# save the line of the inherited class entry
line_class = line if event == 'class'
else
# check the end of inherited class
if line == line_class && event == 'end'
# if so, turn off the trace and call the block
set_trace_func nil
blk.call child
end
end
end)
end
end
# testing...
class A
def self.inherited(child)
after_inherited do
puts "XXX"
end
end
end
class B < A
puts "YYY"
# .... code here can include class << self, etc.
end
Output:
YYY
XXX
This is not possible. Consider this: when should a class definition be considered "done" in Ruby?
Instead of self.inherited, I would personally make a class method called finalize! and put your post-class-creation routine in there.
# A silly, contrived example
class A
def self.author(person)
##authors ||= Array.new
##authors << person
end
def self.finalize!
##authors.map! { |a| Author[a] }
end
end
class B < A
author "Jason Harris"
author "George Duncan"
finalize!
end
You can probably get away with making a wrapper function instead:
class A
def self.define_authors(&blk)
yield
# (...finalize here)
end
...
end
class B < A
define_authors {
author "Jason Harris"
author "George Duncan"
}
end
...Or do consider that there may be ways where that finalizing step may not be needed.
There is no such hook AFAIK.
A workaround could be:
class A
##my_lambda = nil
def self.inherited(child)
##my_lambda = lambda { puts "XXX" }
end
def self.after_inherited
##my_lambda.yield
end
end
class B < A
puts "YYY"
# other definitions
self.superclass.after_inherited
end
It looks a bit messy but its output is:
YYY
XXX
This way you guarantee that your code in B gets executed before the after_interited() of A. So it does what you want but not the way you want to it.
In Ruby, is there a way to redefine a method of a particular instance of a class using a proc? For example:
class Foo
def bar()
return "hello"
end
end
x = Foo.new
y = Foo.new
(Something like):
y.method(:bar) = lambda { return "goodbye" }
x.bar
y.bar
Producing:
hello
goodbye
Thanks.
def define_singleton_method_by_proc(obj, name, block)
metaclass = class << obj; self; end
metaclass.send(:define_method, name, block)
end
p = proc { "foobar!" }
define_singleton_method_by_proc(y, :bar, p)
or, if you want to monkey-patch Object to make it easy
class Object
# note that this method is already defined in Ruby 1.9
def define_singleton_method(name, callable = nil, &block)
block ||= callable
metaclass = class << self; self; end
metaclass.send(:define_method, name, block)
end
end
p = proc { "foobar!" }
y.define_singleton_method(:bar, p)
#or
y.define_singleton_method(:bar) do
"foobar!"
end
or, if you want to define your proc inline, this may be more readable
class << y
define_method(:bar, proc { "foobar!" })
end
or,
class << y
define_method(:bar) { "foobar!" }
end
this is the most readable, but probably doesn't fit your needs
def y.bar
"goodbye"
end
This question is highly related
I'm not sure what version of Ruby this was added in (at least 1.8.7), but there seems to be an even simpler way of doing this:
str1 = "Hello"
str2 = "Goodbye"
def str1.to_spanish
"Hola"
end
puts str1 # => Hello
puts str1.to_spanish # => Hola
puts str2 # => Goodbye
puts str2.to_spanish # => Throws a NoMethodError
Learnt about this whilst reading the Ruby Koans (about_class_methods.rb lesson).
I'm still not entirely sure what the purpose of this is since it seems a bit dangerous to me.
You can use the syntax class <<object to get an object's "singleton class" (that's a special parent class belonging only to that object) and define methods only for that instance. For example:
str1 = "Hello"
str2 = "Foo"
class <<str1
def to_spanish
'Hola'
end
end
Now if you do str1.to_spanish, it will return "Hola", but str2.to_spanish will give you a NoMethodFound exception.
I'm trying to dynamically define functions that call through to another function that takes an options parameter:
class MyClass
["hour", "minute", "second"].each do |interval|
define_method "get_#{interval}" do |args|
some_helper(interval, args)
end
end
def some_helper(interval, options={})
# Do something, with arguments
end
end
I'd like to be able to call the different methods on MyClass in these two ways (with and without optional arguments):
mc = MyClass.new
mc.get_minute( :first_option => "foo", :second_option => "bar")
mc.get_minute # This fails with: warning: multiple values for a block parameter (0 for 1)
On the second call to minute, I see this warning:
warning: multiple values for a block parameter (0 for 1)
Is there a way to write the block for the "get_*" method so that this warning won't come up?
Am I abusing define_method?
The only change you need to make is to change args to *args. The * indicates that args will contain an array of optional arguments to the block.
Two years later...
I don't know if is is a new feature with ruby 1.9.2, or if it was also possible in the past, but this works:
class MyClass
["hour", "minute", "second"].each do |interval|
define_method "get_#{interval}" do |args = {:first_option => "default foo", :second_option => "default bar"}|
some_helper(interval, args)
end
end
def some_helper(interval, options={})
# Do something, with arguments
p options
end
end
mc = MyClass.new
mc.get_minute( :first_option => "foo", :second_option => "bar")
mc.get_minute
Result is:
{:first_option=>"foo", :second_option=>"bar"}
{:first_option=>"default foo", :second_option=>"default bar"}
I agree with Gordon adding * to your args will make it go away.
Another way of doing this is to use method_missing()
Something like this:
class MyClass
def method_missing(m, *args)
if /get_(.+)/.match(m.to_s)
some_helper($1, args)
else
raise 'Method not found...'
end
end
def some_helper(interval, *args)
puts interval + ' -> ' + args.inspect
end
end
m = MyClass.new
m.get_minute( :first_option => "foo", :second_option => "bar" )