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.
Related
I keep trying to reason upon the functionality of a method within a subclass that inherits functionality of a parents class. But it seems that I keep getting into a mental loop of: one cannot behave without the other but the other cannot come before the one... My brain hurts...
Ok heres my relevant code in the parent class
class BankAccount
# method to initialize and other methods etc...
def withdraw(amount)
if (amount <= #balance)
#balance -= amount
else
'Insufficient funds'
end
end
end
And heres my relevant code in the subclass
class CheckingAccount < BankAccount
# methods to initialize and other methods etc...
def withdraw
super
end
end
According to the tutorial im learning from - what I am trying to accomplish is
"CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"
So if I create a variable number_of_withdrawals inside of my BankAccount class (as tutorial examples hint towards) then how is it that when I call super from the subclass version of withdraw that it would know to increment number_of_withdrawals based on the if else statement executing a withdraw or not.
Shouldn't a variable number_of_withdrawals be declared in the BankAccount class, not the CheckingAccount class (even though tutorial examples hint towards putting it in the CheckingAccount class). For a full picture of this here is a gist with the test specs() below of my current code state:
Test Specs / Code Attempt
If someone can provide a working example of
"CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"
With modified code I have provided in the GIST - I would really appreciate it. Im very new to ruby.(1 week)
With the code as it's currently written, CheckingAccount#withdraw could check the return value of super to determine whether the withdrawal was successful or not.
For example:
def withdraw(n)
result = super
if result != 'Insufficient funds'
#number_of_withdrawals += 1
end
result
end
Your method does way too much and internalizes too many assumptions. A better approach to this is to break things up a little:
class BankAccount
attr_reader :balance
def initialize
#balance = 0
end
def withdraw(amount)
if (can_withdraw?(amount))
credit(amount)
after_withdraw
else
false
end
end
def can_withdraw?(amount)
amount <= balance
end
def after_withdraw
# No default behaviour
end
def debit(amount)
#balance += amount
end
def credit(amount)
#balance -= amount
end
end
Then you can make the subclass specialize very specific methods instead of having to lean on super so hard:
class CheckingAccount < BankAccount
attr_reader :overdraft
def initialize
super
#overdraft = 0
#withdrawals = 0
end
def can_withdraw?(amount)
amount <= balance + overdraft
end
def after_withdraw
#withdrawals += 1
end
end
With inheritance, the parent can be thought of as a 'template' for the child. That is to say, you can instead of using a parent at all simply write everything into the child class (not that you should). The point is that everything from the parent class can be thought of as getting copied on to the child class, so if you make an instance variable on the parent and change it from the child, there is only one instance variable defined since there is only one object instantiated. In other words, when you say CheckingAccount.new there is no separate BankAccount getting instantiated - it's all the same object.
So, for every method that is defined in both the parent and child, you need to call super or else the parent method won't be called. Here's an example with your code:
class BankAccount
def initialize
#balance = 0
#number_of_withdrawals = 0
end
def withdraw(amount)
if amount <= #balance
#balance -= amount
#number_of_withdrawals += 1
else
'Insufficient funds'
end
end
end
class CheckingAccount < BankAccount
MAX_FREE_WITHDRAWALS = 3
def withdraw(amount)
if #number_of_withdrawals >= self.class::MAX_FREE_WITHDRAWALS
amount += 5
super(amount)
end
end
I just skimmed the requirement document, so make sure to double check (e.g. don't just take my code and hand it is as homework :D)
The behavior you're seeing -- or expected to provide in order to complete the exercise -- is due to Ruby's dynamic nature. Since your program is "interpreted" as it's run (and is subject to change), there's no way for Ruby to know that the instance variable in question won't exist until the method is actually executed.
Here's a contrived example which (hopefully) demonstrates why you're seeing/hoping to see this behavior:
class Foo
def say_something_that_doesnt_exist
# Foo is free to try to make use of #nothing,
# in case it's been provided by a child class'
# instance, but if it's not present, its value
# will just be nil
puts "say_something_that_doesnt_exist, like #{#nothing}!"
end
def say_something_that_does_exist
puts "say_something_that_does_exist, like #{#bar}!"
end
end
class Bar < Foo
attr_reader :bar
def initialize
super
#bar = "bar"
end
end
bar = Bar.new
bar.say_something_that_doesnt_exist # say_something_that_doesnt_exist, like !
bar.say_something_that_does_exist # say_something_that_does_exist, like bar!
You should have a look at this question and its answers for a more detailed discussion about the distinction between static/dynamic languages and the early/late binding of values.
EDIT: For those criticizing my intentions with replacing self, you are free to click the back button, continue developing with your own opinions, and leave me to develop with mine :)
I was wondering if there is a way to completely remove the object that self references and replace it with a new instance.
Example:
def refresh_from_server!
self = newly_fetched_object_from_server
end
I don't want to return the new object.
It seems like I would have to build my own copying interface and call self.copy_from(other_object) but maybe someone has a cool ruby bit to share that works better!
--EDIT
Since some people seem unclear on the question, I want instance.my_method! to completely replace instance with a new instance of that class
For example lets imagine we have a class
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
No, you can't replace self. You can only change some/all of its state, but the object reference will remain the same.
Why would you want to do this, anyway? If you just want to piggyback on your initialization logic (as it seems to me to be the case), some refactoring will help: just call a shared method from both places.
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
As already noted by others, self can't be replaced from enclosed instance. If replacement of instance with a new instance is required, it need to be done from outside, like in a class factory which registers its class instances.
Bellow is a simplest example using a delegator, demonstrating what I mean. SimpleDelegator represents a simple wrapper around Counter instance:
require 'delegate'
class Counter
attr_accessor :count
def initialize
#count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
Though the question is old, it is still visited. I will attempt to elaborate more on the "why" in "Why can't self be replaced in Ruby?".
usage of self in which context
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
There are various contexts in which self can be used. You question uses it in the context of an instance method, so I will focus on that.
E.g. this context:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
Note that there are other usages of self: e.g. where it reference the Class object in scope of a class definition. E.g.
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
the intended effect of replacing self
Below code a demonstration of what you expected / wished (as I understand it):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
But what about other references? E.g. assuming you wanted:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
If stated as such it gives a hint on the why not.
why we can't replace self
The short answer is that it is simply not something that the language / interpreter offers. As to the reasoning: in a way #matthewd answers that in this answer:
All ruby variable references are essentially pointers (but not
pointers-to-pointers), in C parlance.
You can mutate an object (assuming it's not immutable), and all
variables that reference it will thus be pointing at the same (now
mutated) object. But the only way to change which object a variable is
referring to is with direct assignment to that variable -- and each
variable is a separate reference; you can't alias a single reference
with two names.
In short: there may be other references to that object in variables that are not in the scope of the instance method. These cannot be manipulated by that instance method.
a way to achieve the intended effect
If you want this effect and only want to touch the code of Counter you might move all methods and state to an inner class Counter::Inner and make Counter behave like a decoupled reference. The only 'state' of Counter would be the reference to the Counter::Inner object and Counter can delegate all calls it receives to that reference in a method_missing method. In case of your refresh! you can replace the reference in Counter same as you now intent to replace self. All outside code will now use indirectly the new Counter:Inner instance.
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
#reference = Inner.new(*args)
end
def method_missing(method_id, *args)
#reference.send(method_id, *args)
end
def refresh!
#reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
Just one more answer for the archives :-) I hope this gives useful insights to future visitors.
How can I create an opbjet that's totally lazy by itself? I have a block, and I want to pass around (as a dependency) the "current value" (at call time) of the block instead of the value at dependency injection time.
I can't actually pass around a lambda because all the services expect an actual object, so they won't send :call to them, just access them.
This (oversimplified) example might clarify the situation:
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
print #current_time
end
end
class Injector
def current_time
# a lazy object that when accessed actually calls the lambda below
# every single time.
end
def current_time_lazy
-> { Time.now }
end
def instantiate(class_name)
# search for the class, look at the constructor and
# create an instance with the dependencies injected by
# name
# but to be simple
if class_name == "Timer"
Timer.new(current_time)
end
end
end
timer = Injector.new.instantiate("Timer")
timer.print_current_time # => some time
sleep 2
timer.print_current_time # => some *different* time
The actual situation implies passing around the current_user but depending on the situation the current user might change after those values are injected.
I would really appreciate any suggestion (even if for now I will carefully sort the dependency injection code so this doesn't happen, but I think it's pretty fragile)
This should help :
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
puts #current_time
end
end
class LazyMaker < BasicObject
def self.instantiate(class_name, lambada)
if class_name == 'Timer'
::Timer.new(new(class_name, lambada))
end
end
def initialize(class_name, lambada)
#lambada = lambada
end
def method_missing(method, *args)
#lambada.call.send(method, *args)
end
end
timer = LazyMaker.instantiate('Timer', -> { Time.now })
timer.print_current_time # some time
sleep 2
timer.print_current_time # some other time
I'm trying to use delegation to implement it, so that I can call the block first, get a new object and redirect the method call to it. Why this way ? Because basically, accessing an object to do something means to call a method on it. For instance, in print #current_time, it sends #current_time.to_s.
But since almost all objects will have a few methods inherited from standard base classes in Ruby like Object, LazyMaker also has methods like to_s. So I thought of making just the LazyMaker inherit from BasicObject, which is a blank class. So almost all of the methods get delegated.
But yeah, there might be another way to do this.
I'm so lost. I know how to use caller to get the caller method, but what do you use the get the caller class?
For example:
class Testing
def return_caller_class
return caller_class
end
end
class Parent
attr_accessor :test_me
def initialize
self.test_me = Testing.new
end
end
class Child < Parent
end
class GrandChild < Child
end
test_Parent = Parent.new
test_Child = Child.new
test_GrandChild = GrandChild.new
puts test_Parent.test_me.return_caller_class => Parent
puts test_Child.test_me.return_caller_class => Child
puts test_GrandChild.test_me.return_caller_class => GrandChild
Thank you!!!
Edit:
I've tried to do the following
class Testing
def return_caller_class
return caller[0][/`.*'/][1..-2]
end
end
And the output is:
{"
"=>Parent}
{"
"=>Child}
{"
"=>GrandChild}
To explain better about my question.
I would the output to display this instead
Parent
Child
GrandChild
I'm a bit out of my depth with this question, but I think you have made a few mistakes unrelated to the problem of getting the caller's class name. If I can help you with those things, at least you might be a step closer (if a solution is even possible)!
Firstly, it seems to me that you're calling return_caller_class from the main program object, not from one of those three objects you created. You have an object of class Testing inside an object of class Parent, but the method call is outside of both.
Secondly, the only reason you seem to be getting anything close to what you want (when you get output like "=>Parent} has nothing to do with the return_caller_class method. It seems as though you inadvertently created little hashes in the last three lines of your program (when you added => Parent, etc), which are being output with puts. (Confirmed here: Has #puts created a new hash?) If these are meant to be comments, they need a # before them.
PS. I found a link to this gem on another thread: https://github.com/asher-/sender. Might be worth checking out.
class Testing
def return_caller_class
self.class.name
end
end
class ChildOne < Testing
end
class ChildTwo < Testing
end
result:
------------------------------------------------
>ChildOne.new.return_caller_class
=> "ChildOne"
>ChildTwo.new.return_caller_class
=> "ChildTwo"
>Testing.new.return_caller_class
=> "Testing"
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.