What is the difference between calling super and calling super()? Which is the best one if the arguments passed to the child method don’t match what the parent is expecting.
When you call super with no arguments, Ruby sends a message to the parent of the current object, asking it to invoke a method with the same name as where you called super from, along with the arguments that were passed to that method.
On the other hand, when called with super(), it sends no arguments to the parent.
If the arguments you have don't match what the parent is expecting, then I would say you would want to use super(), or explicitly list parameters in the functional call to match a valid parent constructor.
Dictates arguments that are sent up the object ancestor chain
super - sends all arguments passed to the function to parent
super() - no arguments
super equals to super(*args), which brings all args to the inherited method
Use super() when you just want to call the method inherited from Parent without passing args
super example:
class Parent
def say(message)
p message
end
end
class Child < Parent
def say(message)
super
end
end
Child.new.say('Hello world!') # => "Hello world!"
super() examples:
class Parent
def say
p "I'm the parent"
end
end
class Child < Parent
def say(message)
super
end
end
Child.new.say('Hello!') # => ArgumentError (wrong number of arguments (given 1, expected 0))
class Parent
def say
p "I'm the parent"
end
end
class Child < Parent
def say(message)
super()
end
end
Child.new.say('Hi!') # => "I'm the parent"
Related
I found the below working solutions (link 1, link 2) which call the grandparent method but without any parameters. Does anyone know how to call the grandparent method with parameters?
class GrandParent
def fun(word)
puts word
end
end
class Parent < GrandParent
def fun(word)
puts word + 'end'
end
end
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call
end
end
You can pass parameters directly to call like this:
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call(param1, param2)
end
end
call accepts parameters
GrandParent.instance_method(:fun).bind(self).call word
I don't know your use case, but this is a Really Bad Idea™. It creates unnecessary dependencies directly between Child and GrandParent such that all kinds of normally reasonable refactoring would result in a crash e.g. moving fun to only be implemented in Parent, changing Parent to subclass a different but similar parent, etc.
If a child inherits from the parent, it inherits the parents methods as well.
So how come an inherited method can't access the child's constants?
Example -
class Parent
def my_method
puts "Value of FOO is #{FOO}"
end
end
class Child < Parent
FOO = "bar"
end
Child.new.my_method #=> NameError: uninitialized constant Parent::FOO
Doesn't the inherited method run "inside" the child class?
How can I get around this?
No it doesn't. When you call Child.new.my_method it looks for the FOO constant in Parent and upwards.
I'm not going to question your approach and just provide you with a way to do what you want:
class Parent
def self.inherited(other)
other.define_singleton_method(:my_method) do
puts "Value of FOO is #{other.const_get(:FOO)}"
end
end
end
Now whenever you inherit from Parent the inherited hook will define a method my_method on the subclass. Notice that the scope of the block in the inherited method is still the class level scope of Parent. That's why I'm directly referencing the subclass (other) to get the constant.
A way around this would be to use other.instance_exec { ... }.
Update
#maxpleaner pointed out (with the explanation provided by #CarySwoveland) that you could also retrieve the class via self and then retrieve the constant from it.
I.e., just do
class Parent
def my_method
puts "Values of FOO is #{self.class.const_get(:FOO)}"
end
end
That works because when calling child = Child.new; child.my_method the value of self within my_method will be child. And the class of child is obviously Child which knows the constant FOO.
In "Comprehensive Ruby programming course" e-book I have a case when the child class method extends parents method. I am not completely aware how it works:
class Parent
def initialize(foo:, bar:)
#foo = foo
#bar = bar
end
end
class Child < Parent
def initialize(buzz:,**args)
super(**args)
#buzz = buzz
end
end
I cant completely understand why we use splat here - **args.
In here def initialize(buzz:,**args) we are just telling initialize to take unknown number of key-value arguments, right? But what exactly this means super(**args). To tell method to take those key-value arguments from the superclass method? Why not just like this:
class Child < Parent
def initialize(buzz:)
super
#buzz = buzz
end
end
After all, super tells to extend method with whatever there is in the parent, so why these splat args needed?
**args in the parameter list simply means "get all extra keyword arguments and put them in a hash, called args".
Conversely, **args when calling a method does the opposite - "get this hash called args and pass keyword arguments with the corresponding names and values from that hash".
super without arguments will try to pass all arguments that the child method received. Hence if you have extra that the parent didn't expect, you will get an ArgumentError.
In your example, the parent expects only foo: and bar:, while the child also has buzz:.
This question pretty much sums up the simple case for dynamically extending the class hierarchy in Ruby.
The problem I'm having is that I want to define this subclass with a DSL, and I think I'm a victim of my own complicated scope.
I have working code which uses a base class:
module Command
class Command
...
end
end
And then each command is implemented as a subclass:
module Command
class Command_quit < Command
def initialize
name = "quit"
exec do
#user.client.should_terminate = true
end
end
end
end
There is a lot of rote and repetition here, and I have envisioned a DSL which cleans this up significantly:
module Command
define :quit do
exec do # this is global.rb:7 from the error below
#user.client.should_terminate = true
end
end
end
As you can see, I want to DRY out the boilerplate as I am only concerned with the contents of #initialize, which sets some metadata (such as name) and defines the exec block (which is the important part).
I have gotten stuck with the following module method:
module Command
def self.define(cmd_name, &init_block)
class_name = "Command_#{cmd_name.to_s}"
class_definition = Class.new(Command)
class_initializer = Proc.new do
name = cmd_name
init_block.call
end
::Command.const_set class_name, class_definition
::Command.const_get(class_name).send(:define_method, :initialize, class_initializer)
end
end
This code yields lib/commands/global.rb:7:in 'exec': wrong number of arguments (0 for 1+) (ArgumentError)
And suppose I have some metadata (foo) which I want to set in my DSL:
module Command
define :quit do
foo "bar" # this becomes global.rb:7
exec do
#user.client.should_terminate = true
end
end
end
I see lib/commands/global.rb:7:in block in <module:Command>': undefined method 'foo' for Command:Module (NoMethodError)
I think I've got my Proc/block/lambda-fu wrong here, but I'm struggling to get to the bottom of the confusion. How should I write Command::define to get the desired result? It seems like although Ruby creates Command::Command_help as a subclass of Command::Command, it's not actually inheriting any of the properties.
When you refer to something in Ruby, it first look up something in local bindings, if it fails, it then look up self.something. self represents a context of the evaluation, and this context changes on class definition class C; self; end, method definition class C; def m; self; end; end, however, it won't change on block definition. The block captures the current self at the point of block definition.
module Command
define :quit do
foo "bar" # self is Command, calls Command.foo by default
end
end
If you want to modify the self context inside a block, you can use BasicObject.instance_eval (or instance_exec, class_eval, class_exec).
For your example, the block passed to define should be evaluated under the self context of an instance of the concrete command.
Here is an example. I added some mock method definition in class Command::Command:
module Command
class Command
# remove this accessor if you want to make `name` readonly
attr_accessor :name
def exec(&block)
#exec = block
end
def foo(msg)
puts "FOO => #{msg}"
end
def run
#exec.call if #exec
end
end
def self.define(name, &block)
klass = Class.new(Command) do
define_method(:initialize) do
method(:name=).call(name) # it would be better to make it readonly
instance_eval(&block)
end
# readonly
# define_method(:name) { name }
end
::Command.const_set("Command_#{name}", klass)
end
define :quit do
foo "bar"
exec do
puts "EXEC => #{name}"
end
end
end
quit = Command::Command_quit.new #=> FOO => bar
quit.run #=> EXEC => quit
puts quit.class #=> Command::Command_quit
Your problem is that blocks preserve the value of self (among other things) - when you call init_block.call and execution jumps to the block passed to define, self is the module Command and not the instance of Command_quit
You should be ok if you change your initialize method to
class_initializer = Proc.new do
self.name = cmd_name # I assume you didn't just want to set a local variable
instance_eval(&init_block)
end
instance_eval executes the block, but with the receiver (in this case your instance of Command_quit as the subclass.
An exception to the "blocks preserve self" behaviour is define_method: in that case self will always be object on which the method is called, much like with a normal method.
class A
def initialize
print "Hello! "
end
end
class B < A
def initialize(name)
super
print "My name is #{name}!"
end
end
test = B.new("Fred")
And I get
wrong number of arguments (1 for 0)
But why? Class B requires one argument, and I am giving it all right. Class A doesn't require any argument, so I am not passing anything through super at all.
You need to use super() in order to call it with no arguments. Super by itself automatically calls the parent with the arguments provided to itself (ie. "Name")