How to override an instance method in Ruby - ruby

Okay, I have a question about how to do something in ruby. I have a python example to show what I'm going for, so here it goes.
class TestScript:
def say(word):
pass
def x():
self.say("hello") #right now it will pass
So lets Say that module was called "tester.py" but now, in another module we can do this now:
import tester
class doScript(tester.TestScript):
def say(word):
return word #now its overrided so in this current module it will return it rather pass it
Now the previous say def that was passed is voided by the new one so now if something gets passed to say it will return it rather pass it. Is there any way to do this in ruby? thanks

Here is an example with three files: animal.rb, dog.rb, and script.rb.
# animal.rb
# Our base class.
class Animal
def speak
puts 'click-click'
end
def eat
puts 'chomp-chomp'
end
end
# dog.rb
# Dog inherits from Animal, but we override the speak() method.
require 'animal'
class Dog < Animal
def speak
puts 'woof-woof'
end
end
# script.rb
# Demo script.
require 'dog'
d = Dog.new
d.speak
d.eat

You can always inherit from other class:
class BasicSay
def say(text)
puts prepare_text(text)
end
def prepare_text(text)
do_something_with(text)
end
end
class HtmlSay < BasicSay
def say(text)
"<p>" + prepare_text(text) + "</p>"
end
end

Related

In ruby from class with function without self how to get current class name?

how is is possible in the below example to print ValidatorStatus class name?
class ValidatorStatus
def initialize(host, msg)
#host = host
#msg = msg
end
attr_accessor :host
attr_accessor :msg
def to_s
puts %|How to have this print *ValidatorStatus*
by dynamically discovering class name?
also if im a subclass of this should print
the subclass name|
end
end
self.class.name
Does just what you want. But as you added the restriction not to use self:
public_send(:class).name
def to_s
self.class.name
end
As others have said, the correct answer is self.class.name
If you want to get creative, you could use :
class A
def to_s
Module.nesting.first.to_s
end
end
puts A.new
# A
There's no self here, not even an implicit, hidden one.
It doesn't return the subclass though:
class B < A
end
puts B.new
# A
Another possibility would be to parse the current file :
class C < A
def to_s
File.readlines(__FILE__).take(__LINE__).reverse.join[/(?<=^class )\w+/]
end
end
puts C.new
# C

Insert code into the beginning of each method of a class

How can I dynamically and easily insert code into the beginning of each method of a class and subclasses without actually inserting it manually? I want something like a macros.
class C1
def m1
#i_am = __method__
end
def m2
#i_am = __method__
end
end
This is one of the examples where I want to avoid repetition.
I initially misinterpreted the question (but have left my original answer after the horizontal line below). I believe the following may be what you are looking for.
class C1
[:m1, :m2].each do |m|
define_method(m) do |name|
#i_am = __method__
puts "I'm #{name} from method #{#i_am}"
end
end
end
C1.instance_methods(false)
#=> [:m1, :m2]
c1 = C1.new
#=> #<C1:0x007f94a10c0b60>
c1.m1 "Bob"
# I'm Bob from method m1
c1.m2 "Lucy"
# I'm Lucy from method m2
My original solution follows.
class C1
def add_code_to_beginning(meth)
meth = meth.to_sym
self.class.send(:alias_method, "old_#{meth}".to_sym, meth)
self.class.send(:define_method, meth) do
yield
send("old_#{meth}".to_sym)
end
end
end
Module#alias_method
and Module#define_method are private; hence the need to use send.
c = C1.new
#=> #<C1:0x007ff5e3023650>
C1.instance_methods(false)
#=> [:m1, :m2, :add_code_to_beginning]
c.add_code_to_beginning(:m1) do
puts "hiya"
end
C1.instance_methods(false)
#=> [:m1, :m2, :add_code_to_beginning, :old_m1]
c.m1
# hiya
#=> :m1
You can use a rails like class decorators to that. The piece of code below is rendering a method called before_action defined in the Base class of the ActiveRecord module. The Test class is inherited from the ActiveRecord. The define_method is used if we want to call something explicitly from the Base class.
module ActiveRecord
class Base
def self.before_action(name)
puts "#{name}"
puts "inside before_action of class Base"
define_method(name) do
puts "Base: rendering code from Base class"
end
end
end
end
class Test < ActiveRecord::Base
before_action :hola
def render()
puts "inside render of class Test"
end
end
test = Test.new
test.render
test.hola
It has the output
hola
inside before_action of class Base
inside render of class Test
Base: rendering code from Base class
So, before running the render method it runs the before_action method in the Base class. It can be applied to all other methods in the Test class. This is a way of representing macros in ruby.
Assuming that the function is the same, you could create a module and include it in your classes.
Example:
module MyModule
def test_method
puts "abc"
end
end
class MyClass
include MyModule
def my_method
puts "my method"
end
end
inst = MyClass.new
inst.test_method # => should print "abc"
inst.my_method # => should print "my method"

Ruby: Performing additional commands on delegated method

I'd like to use delegate to pass map from a string on to chars, then join back to a string result.
require 'forwardable'
module Map
module String
extend Forwardable
def self.included(base)
base.send :extend, Forwardable
end
# Map for String
delegate map: :chars
end
end
class String
include Map::String
end
As it's originally a string I'd like to perform join after the delegated method has performed its duties. How do I modify the delegate line to include the additional change? The closest thing I've seen online is SimpleDelegator with __setobj__. But that's not well documented and I'm not able to ascertain how to use it for this.
I'm strictly looking for an answer in regards to delegate or SimpleDelegate
The equivalent behavior I'm looking for is this:
module Map
module String
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
end
class String
include Map::String
end
I'm looking to understand how to do this with delegate.
The Fowardable docs are hilarious--as if that first example will run without a hundred errors. Your pseudo code tells ruby to forward the method call String#map, which doesn't exist, to String#chars, and you want to join() the result of that? Skip all the method calls and just write puts "some_string". So your question doesn't seem to make a lot of sense. In any case, Forwardable#delegate() does not allow you to map one name to another name.
With regards to SimpleDelegat**or**, you can do this:
module Map
require 'delegate'
class MyStringDecorator < SimpleDelegator
def map
chars.shuffle.join('|')
end
end
end
d = Map::MyStringDecorator.new 'hello'
puts d.map
--output:--
h|o|l|l|e
Response to edit: The equivalent behavior I'm looking for..
The problem is ruby won't let you do this:
class String < SomeClass
end
which is what include does, and you need to be able to do that in order to use delegate to forward all the method calls sent to one class to another class. This is the best you can do:
require 'delegate'
class MyString < DelegateClass(String)
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
s = MyString.new 'hello'
puts s.upcase
puts s.map {|letter| letter.succ }
--output:--
HELLO
ifmmp
Or:
require 'forwardable'
class MyString
extend Forwardable
def initialize(str)
#str = str
end
def_delegators :#str, :upcase, :capitalize, :[], :chars #etc., etc., etc.
#Or: delegate({[:upcase, :capitalize, :[], :chars] => :#str})
#Or: instance_delegate({[:upcase, :capitalize, :[], :chars] => :#str})
def map(*args, &block)
if (!args.compact.empty? || !block.nil?)
self.chars.map(*args,&block).join
else
self.chars.map(*args,&block)
end
end
end
s = MyString.new('hello')
puts s.upcase
puts s.map {|letter| letter.succ }
--output:--
HELLO
ifmmp
Of course, you could always override String#method_missing() to do what you want. What is it that you read about delegate that made you think it could replace include?

ruby class extensions

I would like to write a method in ruby that takes a class with certain methods and modifies its behavior by adding methods or changing how existing methods work. I would like to do this in a way that doesn't modify the base class so basically I want a function that takes a class and returns a new modified class without harming the initial class. I'm pretty sure this is possible but I'm not sure where to start.
You have a couple options:
you can use x = Class.new(Parent) { def meth; puts "hello"; super; puts "bye"; end } to dynamically define a class and override methods (& define new ones)
you can use a Delegator
So for instance, if you wanted to dynamically create classes that logged certain method calls:
class Class
def logging_subclass(*methods)
Class.new(self) do
methods.each do |method|
define_method(method) do |*args,&blk|
puts "calling #{method}"
ret = super(*args,&blk)
puts "#{method} returned #{ret.inspect}"
ret
end
end
end
end
end
class One
def foo
"I'm foo!"
end
end
# this prints nothing
One.new.foo #=> returns :foo
# this prints:
# > calling foo
# > foo returned "I'm foo!"
One.logging_subclass(:foo).new.foo #=> returns :foo
Note that you need ruby 1.9 to support capturing do |&blk| (capturing blocks in block arguments).
I'd suggest using inheritance or a mixin; in my opinion, the use of a mixin would be a wiser idea though using inheritance is easier for a newbie.
Remember, you can always inherit from the class and change behavior or wrap it with new code as desired.
class Mammal
def speak
"..."
end
end
class Cat < Mammal
def speak
"meow"
end
end
class Lion < Cat
def speak
"get ready for a big " + super + "!"
end
end
module Asexual_Critter
def reproduce(critter_list)
puts "*poink!*"
critter_list << self.clone
end
end
class Mutated_Kitty < Cat
include Asexual_Critter # inane example I know, but functional...
end
Just remember that if you want to play with this not to do:
critters = [Mutated_Kitty.new]
begin
critters.each { |c| c.reproduce(critters) }
end while critters.length > 0
Or else you'll be in for a long wait until you run out of RAM, or perhaps segfault.

Ruby module with a static method call from includer class

I need to define the constant in the module that use the method from the class that includes this module:
module B
def self.included(base)
class << base
CONST = self.find
end
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
But the compiler gives the error on the 4th line.
Is there any other way to define the constant?
The more idiomatic way to achieve this in Ruby is:
module B
def self.included(klass)
klass.class_eval <<-ruby_eval
CONST = find
ruby_eval
# note that the block form of class_eval won't work
# because you can't assign a constant inside a method
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
What you were doing (class << base) actually puts you into the context of A's metaclass, not A itself. The find method is on A itself, not its metaclass. The thing to keep in mind is that classes are themselves objects, and so have their own metaclasses.
To try to make it clearer:
class Human
def parent
# this method is on the Human class and available
# to all instances of Human.
end
class << self
def build
# this method is on the Human metaclass, and
# available to its instance, Human itself.
end
# the "self" here is Human's metaclass, so build
# cannot be called.
end
def self.build
# exactly the same as the above
end
build # the "self" here is Human itself, so build can
# be called
end
Not sure if that helps, but if you don't understand it, you can still use the class_eval idiom above.
In your specific case.
module B
def self.included(base)
base.const_set("CONST", base.find)
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
Despite it works, it's a little bit messy. Are you sure you can't follow a different way to achieve your goal?
module B
def self.included(base)
class << base
CONST = self.find
end
end
end
class A
class << self
def self.find
"AAA"
end
end
include B
end
then the compiler error is fixed, pls try.

Resources