I have a base class with child classes that override a method that takes multiple arguments.
class Parent
def foo *bar
end
end
class Child < Parent
def foo bar, baz
end
end
This works fine. However, suppose there is a method foobar in Parent that calls foo:
def foobar *foo_args
foo foo_args
end
This raises a ArgumentError when called on a Child instance because foo_args is one single array, while Child.new.foo expects two objects. Is there a way around this?
Your question is not clear, but I think this may be what you want:
def foobar *foo_args
foo(*foo_args)
end
Still, Child.new.foo must take exactly two arguments for it to not raise an error.
Related
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:.
From what I understand about self, it refers to the current instance of the class.
Isn't this the default behaviour at all times anyways? For example, isn't
self.var_one = method(args)
equivalent to
var_one = method(args)
If so, what is the use of self?
There are several important uses, most of which are basically to disambiguate between instance methods, class methods, and variables.
First, this is the best way to define class methods:
class Foo
def self.bar
"class method bar"
end
def bar
"instance method bar"
end
end
Foo.bar #returns "class method bar"
foo = Foo.new
foo.bar #returns "instance method bar"
Also, within instance methods self refers to the instance, within class methods it refers to the class, and it can always be used to distinguish from local variables.
class Bar
def self.foo
"foo!"
end
def baz
"baz!"
end
def self.success
foo #looks for variable foo, doesn't find one, looks for class method foo, finds it, returns "foo!"
end
def self.fail
baz #looks for variable baz, doesn't find one, looks for class method baz, doesn't find one, raises exception
end
def instance_success
baz #looks for variable baz, doesn't find one, looks for instance method baz, finds it, returns "baz!"
end
def instance_fail
foo #looks for variable foo, doesn't find one, looks for instance method foo, doesn't find one, raises exception
end
def local_variable
baz = "is my favorite method"
baz #looks for variable baz, finds it, returns "is my favorite method"
end
def disambiguate
baz = " is my favorite method"
self.baz + baz #looks for instance method baz, finds it, looks for local variable baz, finds it, returns "baz! is my favorite method"
end
end
So, in the end, you can avoid using self in many cases, but it's often helpful to use it to make sure that you don't inadvertently create naming conflicts later on. Sometimes those can create bugs that are very hard to find. In the end it's often a matter of personal style.
As noted in the comments, one more really important thing:
In a class, if you have a method like this:
def bar=(string)
...
end
And in another method you call:
def other_method
bar = "abcd"
end
It isn't going to call your bar= method, it's going to create a local variable bar. So, in this case you use self to tell Ruby not to create a local variable:
def other_method
self.bar = "abcd"
end
The same thing applies if you want to take an argument with the name of a method:
def example
...
end
def other_thing(example)
self.example(example)
end
If you left off self Ruby would assume you meant the local variable with the same name.
So, in general, self in method names is used to distinguish between class and instance variables, and everywhere else you use it when Ruby needs help distinguishing between method calls and local variables or local variable assignment.
I hope that makes sense.
In most cases self.foo is indeed redundant because you can just write foo for the same effect, but in this case it is not and the self is required.
var_one = method(args) will create a local variable called var_one, it will not call any method or do anything else to self.
self.var_one = method(args) will call the method var_one= on self with the argument method(args).
Another case where the use of self is non-optional would be if you want to pass it as an argument to a method, i.e. some_method(self) - you can't do that without the self keyword.
One other use of self is to declare class methods (similar to static methods in Java).
class foo
def self.bar
#do class related stuff here
end
end
That being said, you could also have used def foo.bar instead for the method signature.
Here's an example:
def run miles
self.miles = miles
end
In this case self will help. In most cases self is redundant.
According to this answer, one can get a constant into the global namespace with an include at the top level, at least in IRB. Naively, I thought I could do the same trick inside a module:
module Foo
class Bar
def frob(val)
"frobbed #{val}"
end
end
end
module Baz
include Foo
class Qux
def initialize(val)
#val = val
#bar = Bar.new
end
def twiddle
#bar.frob(#val)
end
end
end
However, attempting to use this produces:
NameError: uninitialized constant Baz::Qux::Bar
I then tried overriding Baz::const_missing to forward to Foo, but it doesn't get called. Baz::Qux::const_missing does, but I suppose by that time the interpreter has already given up on looking in Baz.
How can I import Foo constants into Baz such that classes under Baz don't have to qualify them?
(Note that I don't just want to declare Qux etc. in Foo instead of Baz, for reasons of consistency between directory structure and module names.)
ETA: If I create another class Baz::Corge, Baz::Qux can refer to it without qualification. So clearly there is some sense of "peer scope". I'm aware there are several ways to make it possible for Qux to access Bar, but I'm looking for a way for all classes in Baz to access Bar (and all other classes in Foo) without qualification.
Ok.. The problems is that the constant are (for some reason) not defined inside the Quz class, but in the upper scope, the module Baz scope.
If only we could somehow delegate (current word?) the constants calls in Quz to the upper (Baz) scope- We can:
Using Module#const_missing
I'll show it twice: once for your private case, and once for a more general approach.
1) solve for private case:
module Foo
class Bar
def frob(val)
"frobbed #{val}"
end
end
end
module Baz
include Foo
class Qux
def self.const_missing(c)
#const_get("#{self.to_s.split('::')[-2]}::#{c}")
const_get("Baz::#{c}")
end
def initialize(val)
#val = val
#bar = Bar.new
end
def twiddle
#bar.frob(#val)
end
end
end
puts Baz::Qux.new('My Value').twiddle
So what happens here? almost same thing- only that when the error is received- it's kinda rescued and arriving (or fall back to) the const_missing function to receive a new value- This apply for both Constants and Classes (apparently they are the same type).
But that mean we have to add the self.const_missing(c) method to every class inside module Baz- Or we can just iterate every classes in module Baz and add it (There are probably better ways to do it, but it works)
2) A more automated approach:
module Foo
class Bar
def frob(val)
"frobbed #{val}"
end
end
end
module Baz
class Qux
def initialize(val)
#val = val
#bar = Bar.new
end
def twiddle
#bar.frob(#val)
end
end
def self.add_const_missing_to_classes
module_name = self.to_s
#note 1
classes_arr = constants.select{|x| const_get(x).instance_of? Class} #since constants get also constants we only take classes
#iterate classes inside module Baz and for each adding the const_missing method
classes_arr.each do |klass| #for example klass is Qux
const_get(klass).define_singleton_method(:const_missing) do |c|
const_get("#{module_name}::#{c}")
end
end
end
add_const_missing_to_classes
#we moved the include Foo to the end so that (see note1) constants doesn't return Foo's classes as well
include Foo
end
puts Baz::Qux.new('My Value').twiddle
Here at the end of module Baz, after all classes were defined. we iterate them (inside the add_const_missing_to_classes method). to select them we use the Module#constants method which returns an array of a module constants- meaning both CONSTANTS and CLASSES, so we use select method to only work on classes.
Then we iterate the found classes and add the const_missing class method to the classes.
Notice we moved the include Foo method to the end- because we wanted the constants method not to include constants from module Foo.
Surly there are better ways to do it. But I believe the OP's question:
How can I import Foo constants into Baz such that classes under Baz don't have to qualify them?
Is answered
Here's what I eventually came up with. In the same file that initially declares Baz:
module Foo
def self.included(base)
constants.each { |c| base.const_set(c, const_get("#{self}::#{c}")) }
end
end
module Baz
include Foo
#... etc.
end
When Baz includes Foo, it will set a corresponding constant Baz::Whatever for every Foo::Whatever, with Foo::Whatever as the value.
If you're worried Foo may already define self.included, you can use alias_method to adjust for that:
module Foo
alias_method :old_included, :included if self.method_defined? :included
def self.included(base)
old_included(base) if method_defined? :old_included
constants.each { |c| base.const_set(c, const_get("#{self}::#{c}")) }
end
end
This approach has two limitations --
All the constants (including classes) in Foo that we care about must be defined at the time include Foo is evaluated -- extensions added to Foo later will not be captured.
The file that defines Foo.included here must be required before any file in Baz that uses any of those constants -- simple enough if clients are just using require 'baz' to pull in a baz.rb that in turn uses Dir.glob or similar to load all the other Baz files, but it's important not to require those files directly.
Roko's answer gets around problem (1) above using const_missing, but it still has an analogous problem to (2), in that one has to ensure add_const_missing_to_classes is called after all classes in Baz are defined. It's a shame there's no const_added hook.
I suspect the const_missing approach also suffers performance-wise by depending on const_missing and const_get for every constant reference. This might be mitigated by a hybrid that caches the results, i.e. by calling const_set in const_missing, but I haven't explored that since trying to figure out scoping inside define_singleton_method always gives me a headache.
As you can see from the error given: NameError: uninitialized constant Baz::Qux::Bar
It tries to find the Bar class inside Qux class scope- But can't find it there- why is that?
Because it's not in this scope- It's in the Baz modlue scope, where you used include Foo
So you have two options:
1) address the correct scope when calling Bar class,
so change this:
#bar = Bar.new
into this:
#bar = Baz::Bar.new
Like that:
module Baz
include Foo
class Qux
def initialize(val)
#val = val
#bar = Baz::Bar.new
end
def twiddle
#bar.frob(#val)
end
end
end
Or,
2) insert the include Foo into the class Qux itself:
module Baz
class Qux
include Foo
def initialize(val)
#val = val
#bar = Bar.new
end
def twiddle
#bar.frob(#val)
end
end
end
--EDIT--
As stated by joanbm this doesn't explain this behavior. You might want to take a look at Scope of Constants in Ruby Modules. Though that post is about constants (not classes) the principles are the same.
Since Foo is included in Baz, that's where Bar is found -- it isn't found in Qux. So you can change
#bar = Bar.new
to
#bar = Baz::Bar.new
or move include Foo inside class Qux
Suppose I want to override a method of an existing class Array as below.
class Array
def to_s
self.join(',')
end
end
So my question is - How does this overriding work? Does this add this definition of method to_s to the class Array? I mean if the class contained 'n' method definitions, it would now contain 'n+1' method definitions. Also, is it that there are two definitions of to_s method and the one that is added last is the one that would work?
Thanks.
You aren't overriding the method, you are re-defining it. The one that was there is gone, replaced with what you put in. This is a risky thing to do w/the standard libraries, you don't know what behavior other code is relying upon.
You can try it w/your own class.
class Foo
def bar
puts 'One'
end
end
class Foo
def bar
puts 'Two'
end
end
Foo.new.bar
# Two
class Foo
def bar
puts 'Three'
super
end
end
Foo.new.bar
# Three
# test.rb:18:in `bar': super: no superclass method `bar'
# for #<Foo:0x007fd642029278> (NoMethodError)
When using class inheritance, is there a way to order the methods? I want them ordered, because I am inheriting from a class that executes all public methods, in order.
For example:
class Foo
def action_two
puts "action 2"
end
end
class Bar < Foo
def action_one
puts "action 1"
end
end
Bar.instance_methods # => [:action_two, :action_one, ...]
...and I would like it to return [:action_one, :action_two, ...]
The documentation doesn't say anything about the order and I actually get them in the order you want when I use Ruby 2.1.2. If you want them to come out in a specific order then I think you'll have to do it yourself. You could use the include_super parameter to instance_methods to get just the current class's methods and then ask superclass for its methods:
class Bar < Foo
#...
def self.instance_methods
super(false) + superclass.instance_methods
end
end
If you're going to be dealing with BasicObject as a possible superclass then you'll want to add a superclass.nil? check before calling superclass.instance_methods.
If you're inheritance hierarchy is deep then you should be able to put this hackery into the lowest base class (Foo in this case) and let the subclasses pick it up through inheritance.
In order to do that, you have to define Bar#action_one before inheriting Foo into Bar. Since class inheritance is determined at the creation time of the class that inherits, you cannot do that as is.
A way to overcome this is to make Foo a module instead of a class, and include it into Bar after Bar#action_one is defined.
module Foo
def action_two
puts "action 2"
end
end
class Bar
def action_one
puts "action 1"
end
include Foo
end
Bar.instance_methods
# => [:action_one, :action_two, ...]
Bar.ancestors.each_with_object([]) { |a, o| o << a.instance_methods }.flatten.uniq