Extending BaseClass method (Polymorphism) - ruby

I played around with inheritances and tried to extend a method from the base class from the subclass. Polymorphism. Basically, I tried to extend the base method (status) with an additional option.
However, for some reason it doesn't work as expected. I thought B.new.status(1) would return 2 instead of nil.
If I place super after "y if opt == 2" then the results are vice verse.
Why is that and how can I solve this problem?
Many thanks in advance!
class A
attr_reader :x
def initialize
#x = 2
end
def status(opt)
x if opt == 1
end
end
class B < A
attr_reader :y
def initialize
super
#y = 10
end
def status(opt)
super
y if opt == 2
end
end
B.new.status(1)
=> nil
B.new.status(2)
=> 2

That's because the return value from A's status is not used in B's status. Yes, in A's status, 'x' will be returned, but that isn't used in B's status.
The result of the last statement in a method is automagically returned, but super isn't the last statement in B's status.

If the codition of an if statement is not satisfied, it returns nil. So you should do something like this:
if opt == 2
y
else
super
end

Related

What is the "other" object and how does it work?

I have seen other used often in class comparisons, such as
def ==(other)
...
end
or
def eql?(other)
self == other
end
but I still have found no explanation of what it actually is. What's going on here?
And perhaps this is for another question, but what does starting a method with == imply?
In ruby, operators are in fact method calls. If you have two variables a and b and want to check their equality, you generally write a == b, but you could write a.==(b). The last syntax shows what happens during an equality check : ruby calls a's method == and passes it b as an argument.
You can implement custom equality check in your classes by defining the == and/or the eql? methods. In your example, other is simply the name of the argument it receives.
class Person
attr_accessor :name
def initialize name
#name = name
end
end
a = Person.new("John")
b = Person.new("John")
a == b # --> false
class Person
def == other
name == other.name
end
end
a == b # --> true
For your second question, the only methods starting with == you're allowed to implement are == and ===. Check here for the full list of restrictions on method names in ruby: What are the restrictions for method names in Ruby?
other is a parameter to this method, the object, that is being passed.
For example:
class A
def ==(other)
:lala == other
end
end
obj = A.new
obj.==(:foo) # full syntax, just like any other method
# but there's also a shorthand for operators:
obj == :foo # => false
obj == :lala # => true
other is the parameter for == and it represents the object you are comparing with.
Example
x == y
The == method (yes, its just a method!), on your x object, gets called with y as a parameter.
Welcome to Ruby, you'll love it after a while :)
other, in this case, is the object you're comparing to, so this:
class SomeClass
def ==(other)
self == other
end
end
means:
SomeClass.new == nil # here "other" is nil, this returns false
Basically def ==(other) is the implementation of the == operator, for cases where you do something specific in your class regarding comparison using ==.
For numbers you can get the sum of 2 numbers using:
a= x + y
in Ruby everything is object, Right! so x and y are objects and for Number(Object) x it has a defined method + which accept 1 paramter which is y
same for what you are trying to understand, what if you have 2 classes and you want to check if they are equal or not and its your defined class and you want to specify how the are equal for example if their name attribute is equal then you can say:
class Student
def ==(other)
self.name == other.name
end
end
fi = Student.new(name: 'Mark')
sec = Student.new(name: 'Hany')
if (fi == sec)
# do something here

Keep if on object

I'm looking for a clean way to evaluate an object and return object if condition is verified, nil otherwise so that I can use a default value instead. Something like:
result = object.verify?{ |object| object.test? } || default_value
I can see several ways to implement this, but I am hoping that there is a built-in way to do that. Ex:
Going to the Array level
def verify?(&block)
Array(self).filter(block).first
end
Using instance_eval
def verify?(&block)
self.instance_eval{ |object| yield(object) ? object : nil}
end
EDIT
Here's my actual example (though the question isn't tighted to it):
class User < ActiveRecord::Base
def currency
self.billing_information.try(:address).try(:country).try(:currency_code).instance_eval{ |currency| Finance::CURRENCIES.include?(currency) ? currency : nil} || 'EUR'
end
end
I know this is ugly, but I do like the logic of it: if the object I'm looking for exists, go get the next one. The first conditions are existence (with try), then inclusion.
Regarding your actual problem - Rails provides presence_in:
Returns the receiver if it's included in the argument otherwise returns nil.
'EUR'.presence_in %w(EUR USD) #=> "EUR"
'JPY'.presence_in %w(EUR USD) #=> nil
I would probably separate the actual currency from the verified currency (so you can still access the former one):
class User < ActiveRecord::Base
def currency
billing_information.try(:address).try(:country).try(:currency_code)
end
def verified_currency
Finance::CURRENCIES.include?(currency) ? currency : 'EUR'
end
end
And move the logic for checking a currency and providing a default one into Finance:
class User < ActiveRecord::Base
def currency
billing_information.try(:address).try(:country).try(:currency_code)
end
def verified_currency
Finance.verified_currency(currency)
end
end
module Finance
CURRENCIES = %w(EUR USD)
DEFAULT_CURRENCY = 'EUR'
def self.verified_currency(currency)
CURRENCIES.include?(currency) ? currency : DEFAULT_CURRENCY
end
end
This also avoids having to evaluate User#currency twice.
The try-chain can be replaced by delegate:
class User < ActiveRecord::Base
delegate :currency, to: billing_information, allow_nil: true
def verified_currency
Finance.verified_currency(currency)
end
end
class BillingInformation < ActiveRecord::Base
delegate :currency, to: address, allow_nil: true
end
class Address < ActiveRecord::Base
delegate :currency, to: country, allow_nil: true
end
If you want the receiver when the condition is satisfied, then assuming that test? returns a truthy value when the condition is met and a falsy value otherwise:
result = object.tap{|object| break unless object.test?} || default_value
Or, following Stefan's suggestion:
result = object.tap{|object| break default_value unless object.test?}
I think #Stefan's suggestion is best, but if you insist on being obtuse, you could write:
(object.test? && object) || default
If you think you need Object#try version that can handle default values, you can monkey patch it.
require "rails"
class Object
alias_method :old_try, :try
def try(params, default = nil, &block)
old_try(params, &block) || default
end
end
object = {}
default = "I'm default"
p result = object.try(:test, default)
#=> I'm default
I think you're looking for Enumerable#detect:
detect(ifnone = nil) { |obj| block } → obj or nil
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
(1..10).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
(1..100).find { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
So you could do result = object.detect { |object| object.test? } || default_value as you've written, or the more idiomatic result = object.detect(default_value) { |object| object.test? }.

Passing variable values between class methods

I am trying to figure out if it is possible to pass a value between methods in a Ruby class.
I haven't found much on my own and figured I would ask the experts. Could it be done passed as a arg/parameter to another method as well?
class PassingValues
def initialize
#foo = 1
end
def one
#foo += 1
end
def two
#foo += 1
end
def three
p #foo
end
end
bar = PassingValues.new
If I wanted this to print out foo value of 3:
bar.three
If you want bar.three to print a 3 you will need to call before the one and two methods to ensure the variable is updated to the three so:
class PassingValues
def initialize
#foo = 1
end
def one
#foo += 1
end
def two
one
#foo += 1
end
def three
two
p #foo
end
end
Anyway this doesn't make sense as long as the methods will be eventually modifying the variable, so each time you call one of them the number will increase the amount expected.
Those are instance methods, not class methods. And #foo is already shared among them.
bar = PassingValues.new
bar.one
bar.two
bar.three
In this case #foo is an instance variable and it can be referenced (it is not passed here) in any method of the class.
When you call you create bar as an instance of the class using: PassingValues.new
it is initialized, and sets #foo to 1.
When you then call the method three using bar.three, you simply print out #foo, which is still equal to 1.
You can change your three method to increment #foo by 2, so it is now equal to 3, then print:
def three
#foo += 2
p #foo
end

Overriding == Operator is not working and exits program

I am trying to override the == operation as shown here. I created == as so:
class Point
def ==(g)
p'Never reaches here'
if #x==g.grid
true
else
false
end
end
end
I am calling the method as so:
def home? (gridPoint,point)
if point==gridPoint
do something
end
When I run an application, "Never reaches here" is never printed, and the application skips the rest of the code and exits.
If I print gridPoint and Point right before the ==, I can see they are both points. Why does the program seem to have never entered the == method?
I tried point.==(gridPoint). This does not work either, and also defies the purpose of trying to clean the code.
Can anyone help me?
I think I have some suggestions why your exact code doesn't work
Class names should always be constants. This means you must capitalize the first letter!
You need to declare your == method as a class method not an instance method.
Along with #2 you need to call it using the class name not from the objects instance
class Point
attr_accessor :x
def initialize(string)
#x == string
end
def self.== (g)
p'Never reaches here'
#x==(g) ? true : false
end
def home? (gridpoint, point)
Point == (gridpoint)
end
end
n = Point.new("Schylar")
puts n.home?("Schylar","Schylar")

Change the binding of a Proc in Ruby

I have this code:
l = lambda { a }
def some_function
a = 1
end
I just want to access a by the lambda and a special scope which has defined a already somewhere like inside some_function in the example, or just soon later in the same scope as:
l = lambda { a }
a = 1
l.call
Then I found when calling l, it is still using its own binding but not the new one where it was called.
And then I tried to use it as:
l.instance_eval do
a = 1
call
end
But this also failed, it is strange that I can't explain why.
I know the one of the solution is using eval, in which I could special a binding and executing some code in text, but I really do not want to use as so.
And, I know it is able to use a global variable or instance variable. However, actually my code is in a deeper embedded environment, so I don't want to break the completed parts if not quite necessary.
I have referred the Proc class in the documentation, and I found a function names binding that referred to the Proc's context. While the function only provided a way to access its binding but cannot change it, except using Binding#eval. It evaluate text also, which is exactly what I don't like to do.
Now the question is, do I have a better (or more elegant) way to implement this? Or using eval is already the regular manner?
Edit to reply to #Andrew:
Okay, this is a problem which I met when I'm writing a lexical parser, in which I defined a array with fixed-number of items, there including at least a Proc and a regular expression. My purpose is to matching the regular expressions and execute the Procs under my special scope, where the Proce will involved some local variables that should be defined later. And then I met the problem above.
Actually I suppose it is not same completely to that question, as mine is how to pass in binding to a Proc rather than how to pass it out.
#Niklas:
Got your answer, I think that is what exactly I want. It has solved my problem perfectly.
You can try the following hack:
class Proc
def call_with_vars(vars, *args)
Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
end
end
To be used like this:
irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3)
=> 3
irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1)
=> 4
This is not a very general solution, though. It would be better if we could give it Binding instance instead of a Hash and do the following:
l = lambda { |a| foo + a }
foo = 3
l.call_with_binding(binding, 1) # => 4
Using the following, more complex hack, this exact behaviour can be achieved:
class LookupStack
def initialize(bindings = [])
#bindings = bindings
end
def method_missing(m, *args)
#bindings.reverse_each do |bind|
begin
method = eval("method(%s)" % m.inspect, bind)
rescue NameError
else
return method.call(*args)
end
begin
value = eval(m.to_s, bind)
return value
rescue NameError
end
end
raise NoMethodError
end
def push_binding(bind)
#bindings.push bind
end
def push_instance(obj)
#bindings.push obj.instance_eval { binding }
end
def push_hash(vars)
push_instance Struct.new(*vars.keys).new(*vars.values)
end
def run_proc(p, *args)
instance_exec(*args, &p)
end
end
class Proc
def call_with_binding(bind, *args)
LookupStack.new([bind]).run_proc(self, *args)
end
end
Basically we define ourselves a manual name lookup stack and instance_exec our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding, it can also be used to build up much more complex lookup chains:
l = lambda { |a| local + func(2) + some_method(1) + var + a }
local = 1
def func(x) x end
class Foo < Struct.new(:add)
def some_method(x) x + add end
end
stack = LookupStack.new
stack.push_binding(binding)
stack.push_instance(Foo.new(2))
stack.push_hash(:var => 4)
p stack.run_proc(l, 5)
This prints 15, as expected :)
UPDATE: Code is now also available at Github. I use this for one my projects too now.
class Proc
def call_with_obj(obj, *args)
m = nil
p = self
Object.class_eval do
define_method :a_temp_method_name, &p
m = instance_method :a_temp_method_name; remove_method :a_temp_method_name
end
m.bind(obj).call(*args)
end
end
And then use it as:
class Foo
def bar
"bar"
end
end
p = Proc.new { bar }
bar = "baz"
p.call_with_obj(self) # => baz
p.call_with_obj(Foo.new) # => bar
Perhaps you don't actually need to define a later, but instead only need to set it later.
Or (as below), perhaps you don't actually need a to be a local variable (which itself references an array). Instead, perhaps you can usefully employ a class variable, such as ##a. This works for me, by printing "1":
class SomeClass
def l
#l ||= lambda { puts ##a }
end
def some_function
##a = 1
l.call
end
end
SomeClass.new.some_function
a similar way:
class Context
attr_reader :_previous, :_arguments
def initialize(_previous, _arguments)
#_previous = _previous
#_arguments = _arguments
end
end
def _code_def(_previous, _arguments = [], &_block)
define_method("_code_#{_previous}") do |_method_previous, _method_arguments = []|
Context.new(_method_previous, _method_arguments).instance_eval(&_block)
end
end
_code_def('something') do
puts _previous
puts _arguments
end

Resources