Is is possible to create instance of child class from parent class? - ruby

I try to define operational semantics in Ruby, just for learning purposes. And I'd like to define operations like: add, multiply, subtraction, division.
Here is code:
class Operation < Struct.new(:left, :right)
def inspect
"#{self}"
end
def reducible?
true
end
end
class Add < Operation
def to_s
"#{left} + #{right}"
end
def reduce
if left.reducible?
Add.new(left.reduce, right)
elsif right.reducible?
Add.new(left, right.reduce)
else
Number.new(left.value + right.value)
end
end
end
class Multiply < Operation
def to_s
"#{left} * #{right}"
end
def reduce
if left.reducible?
Multiply.new(left.reduce, right)
elsif right.reducible?
Multiply.new(left, right.reduce)
else
Number.new(left.value + right.value)
end
end
end
Is it possible to have the same thing, but define reduce in Operation class, but create instance of some particular child class in Operation class?
Thanks in advance.

Yes.
class Operation
def reduce
if left.reducible?
self.class.new(left.reduce, right)
elsif right.reducible?
self.class.new(left, right.reduce)
else
Number.new(left.value + right.value)
end
end
end

Related

How to make Ruby Tester?

It asks to implement the class Tester, which receives a class and and runs all its methods that start with the word test.
class AssertionFailed < Exception
end
class TestCase
def setUp
end
def tierDown
end
def assertTrue(expresion)
if not expresion then
raise AssertionFailed.new(expresion.to_s + " is not true")
end
end
def assertEquals(result, expected)
if result != expected
raise AssertionFailed.new(result.to_s + " is not equal to " + expected.to_s)
end
end
end
class IntegerTest < TestCase
def setUp
#number = 1
end
def testSum
assertTrue(1 + #number == 2)
#number += 1
end
def testSub
assertTrue(2 - #number == #number)
end
def testMulByZero
assertEquals(#number*0, 1)
end
def testAddByZero
assertEquals(#number + 0, #number + 1)
end
end
Tester.test(IntegerTest)
Example:
Tester.test(IntegerTest)
[*] testMulByZero failed: 0 is not equals to 1
[*] testAddByZero failed: 1 is not equals to 2
Help: The grep method of the Iterable module receives a regular expression, and returns all
The elements that match that expression. For the exercise, use grep (\ test * ) on
The collection of methods to obtain the methods sought.
I finally got an answer by this source and this one
What I have done is starting the test that is given, then i create an array by asking to de class testig his instace which start with test, finaly it's an iteration on the array asking to execute eachone of the methods and if they fail the assertion then show them
class Tester
def self.test(testing)
tested=testing.new
tested.setUp
method_arr=testing.instance_methods.grep(/test*/)
method_arr.each do |x|
begin
tested.send(:"#{x}")
rescue AssertionFailed =>assert_fail
puts "[*]" + "#{assert_fail}"
end
end
end
end

Ruby classes, subclasses and factory methods

I'm working on a TestFirst exercise (temperature_object) and have come to a standstill when it comes to integrating a subclass. So far I've got:
class Temperature
def initialize(opts = {})
#options = opts
#c = #options[:c]
#f = #options[:f]
end
def self.from_celsius(num)
self.new(:c => num)
end
def self.from_fahrenheit(num)
self.new(:f => num)
end
def in_celsius
if #options.has_key?(:c)
#c
elsif #options.has_key?(:f)
ctof(#f)
end
end
def in_fahrenheit
if #options.has_key?(:f)
#f
elsif #options.has_key?(:c)
ftoc(#c)
end
end
def ftoc(num)
(((num * 9) / 5.000) + 32)
end
def ctof(num)
(((num - 32) * 5) / 9.0000)
end
end
class Celsius < Temperature
def initialize(num)
#c = num
end
end
class Fahrenheit < Temperature
def initialize(num)
#f = num
end
end
All of the tests pass until I get to the following:
require "temperature_object"
describe Temperature do
# Here's another way to solve the problem!
describe "Temperature subclasses" do
describe "Celsius subclass" do
it "is constructed in degrees celsius" do
Celsius.new(50).in_celsius.should == 50
Celsius.new(50).in_fahrenheit.should == 122
end
it "is a Temperature subclass" do
Celsius.new(0).should be_a(Temperature)
end
end
describe "Fahrenheit subclass" do
it "is constructed in degrees fahrenheit" do
Fahrenheit.new(50).in_fahrenheit.should == 50
Fahrenheit.new(50).in_celsius.should == 10
end
it "is a Temperature subclass" do
Fahrenheit.new(0).should be_a(Temperature)
end
end
end
end
So, I'm thinking the problem is that I'm trying to go from Temperature.new, which takes a hash, to Celsius.new, which only takes a value. I'm getting an undefined method "has_key?" for nil:NilClass error message. Do I need to set num as a hash value and assign it a key? If so, how do I do that? If not, any suggestions?
Your problem is that you refer to #options, but you don't assign it when creating an instance of Celsius. You should call the super constructor in your inherited classes:
class Celsius < Temperature
def initialize(num)
super(c: num)
end
end
class Fahrenheit < Temperature
def initialize(num)
super(f: num)
end
end
Now, when you call Celsius.new(50) the initialize(opts) will be called as if you called Temperature.new(c: 50), and all members will be properly assigned.

Ruby Check Class Owner From Other Inheritance With Default Library

I wondering of how to check the owner of certain method/class from other class.
For example:
class Value
attr_accessor :money
def initialize
#money = 0.0
end
def get_money
return self.money
end
def transfer_money(target, amount)
self.money -= amount
target.money += amount
end
end
class Nation
attr_accessor :value
def initialize
#value = Value.new
end
end
class Nation_A < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_B.value, 500)
end
end
class Nation_B < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_C.value, 200)
end
end
class Nation_C < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_A.value, 300)
end
end
Yea, makes no sense how the decendant goes in a circle, but I'd like to implement the idea that different subclass has different argument.
The list is pretty long (I've installed at least 40 of these already with much more complex desendant branches and much more methods that call transfer_money from Value class). Then I have some idea to implement to the structure. I'd like to add currency, but to override all transfer_money method call would be a tremendous task for me to apply. Therefore I create a hash table that generate the call for me.
class Nation
def self.get_descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
module Additional_Value
currency_table = {}
min = 50
max = 100
def self.range (min, max)
rand * (max-min) + min
end
Nation.get_descendants.each do |derived_classes|
currency_table[derived_classes] == self.range min, max
end
end
class Value
attr_accessor :currency
def initialize
#money = 0
#currency = Additional_Value::currency_table
end
def transfer_money(target, amount)
self.money -= amount
amount = amount * #currency[self.class.owner] / #currency[target.class.owner]
target.money += amount
end
end
and I need to figure out how to define owner class. I tried using the caller, but it returns me string / array of string instead of object, method or calle work only for the same method, the 'sender' gem gives me an idea, but it's written in C, and I need to use the default library due to my circumstances.
Greatly appreciated.
Edit:
I'll rewrite the problem in a shorter way:
class Slave
def who_is_the_owner_of_me
return self.class.owner unless self.class.owner.nil?
end
end
class Test
attr_accessor :testing
def initialize
#testing = Slave.new
end
end
class Test2 < Test1
end
a = Test.new
b = Test2.new
c = Slave.new
a.testing.who_is_the_owner_of_me #=> Test
b.testing.who_is_the_owner_of_me #=> Test2
c.who_is_the_owner_of_me #=> main

Is there a way to redefine []=+ in ruby

I am trying to write a simple DSL (against Redis) and I would like to define []+= myself
I have
def []=(key,val)
#redis.zadd(#name,val,key)
end
and I would like to define
def []+=(key,val)
#redis.zincrby(#name,val,key)
end
But my understanding is that Ruby provides the "[]+=" operator automaticallygiven []=
Is there a way to over-ride this behavior
Obviously I don't want this because I would not be able to, say, run this in pipeline mode
No, <operator>= can not be redefined in Ruby.
You can try to get really fancy and wrap your return values in classes that delegate to the actual value. This way, they behave like the actual value, but you can play tricks, for instance with +.
Here's a simple example:
require 'delegate'
module Redis
class Set
class Value < SimpleDelegator
def +(val)
Increment.new(self, val)
end
end
class Increment < SimpleDelegator
attr_reader :increment
def initialize(source, increment)
super(source.__getobj__ + increment)
#increment = increment
end
end
def [](key)
Value.new(#redis.not_sure_what(#name, key))
end
def []=(key,val)
if val.is_a?(Increment)
#redis.zincrby(#name,val.increment,key)
else
#redis.zadd(#name,val,key)
end
end
end
end
This is just a starting point. You'll have to be more careful than this, for example by checking the key is the same. In my simplistic example, redis[:foo] = redis[:bar] + 1 would actually be equivalent to redis[:foo] += 1...
Nope. x[y] += z expands to exactly x[y] = x[y] + z:
class << (object = Object.new)
def [](key)
puts "[#{key.inspect}]"
key
end
def []=(key, value)
puts "[#{key.inspect}] = #{value.inspect}"
value
end
end
# These are all equivalent
object['See?'] += " It's impossible."
object['See?'] = object['See?'] + " It's impossible."
object.[]=('See?', object.[]('See?').+(" It's impossible."))
# They all produce the same output:
# ["See?"]
# ["See?"] = "See? It's impossible."
# => "See? It's impossible."
You will have to create a separate method.

Metaprogramming errors in Ruby

Alright, so this is driving me crazy. The point of this code is that I ought to be able to add a method dynamically provided it is of the form object.plusnum, where num is any number. I'm not quite sure how to get this to work. This is my best shot at it so far, but I'm currently getting several errors.
Code:
class Adder
def initialize(_val)
#start_value = _val
end
def method_missing(method_name, *args)
method = method_name.to_s
if method.start_with?("plus") then
num = method[4 .. method.length]
if (/^[\d]+(\.[\d]+){0,1}$/ === num) then
number = Integer(num)
self.class_eval("def #{method} return #start_value + #{number} end")
else
super
end
else
super
end
end
end
The error I'm currently getting is that "class_eval" is undefined. I am pretty new to metaprogramming and ruby, and this is driving me crazy.
I think you got it all wrong :)
Calling a method for the first time yields different result, than calling it second time so probably you would like this method to be called instantly after defining. Also -- you are using fairly complex regex and then converting value to Integer and dropping all digits after dot.
You are using class_eval and passing string to it, which is usually bad idea, and block should be used whenever possible, for security and performance reasons.
How I see it could look:
class Adder
def initialize(val)
#start_value = val
end
def method_missing(method_name, *args)
if method_name.to_s =~ /^plus(\d+)$/
self.class.class_eval do
define_method(method_name) { #start_value + $1.to_i }
end
self.send(method_name)
else
super
end
end
end
class Adder
def initialize(_val)
#start_value = _val
end
def method_missing(method_name, *args)
method = method_name.to_s
if method.start_with?("plus") then
num = method[4 .. method.length]
if (/^[\d]+(\.[\d]+){0,1}$/ === num) then
number = Integer(num)
self.class.class_eval("def #{method}() return #start_value + #{number} end")
eval(method)
else
super
end
else
super
end
end
end
a = Adder.new(0)
a.plus1
Make sure to add eval(method) in the end to call the method otherwise it will return nil for just creating the method. Or you can simple return with return #start_value + #{number}
You have to call class_eval on the Adder class, not on an instance of adder.
The string isn't valid Ruby. Put parentheses after #{method}.
The new version of the code:
class Adder
def initialize(_val)
#start_value = _val
end
def method_missing(method_name, *args)
method = method_name.to_s
if method.start_with?("plus") then
num = method[4 .. method.length]
if (/^[\d]+(\.[\d]+){0,1}$/ === num) then
number = Integer(num)
self.class.class_eval("def #{method}() return #start_value + #{number} end")
else
super
end
else
super
end
end
end
a = Adder.new(0)
a.plus1
Speaking for myself, the way I would have built this method up is just to start off with
class Adder
end
Adder.class_eval("def plus1() return 0 + 1 end")
a = Adder.new
a.plus1
and then gradually replaced the hard-wired values with configurable values, rather than writing everything at once.

Resources