Calling a method when a Class name is used like a method - ruby

I'm wondering if one could define a class in Ruby to have the following sort of usage:
Class Book
def Book
puts self.to_s
end
def initialize(name)
#name = name
end
def to_s
#name.to_s
end
end
Usage:
Book "To Kill a Mocking Bird" #=>To Kill a Mocking Bird
The idea that I want is for this to behave like the following
An instance of the method is created (as a short hand).
The method Book is immediately called after this and performs a block of code.
(The intent of having the method named the same as the class is to have the call back when it is used like a method.)
Is this possible in Ruby?

How about this?
class Book
attr_accessor :name
def initialize(name)
self.name = name
end
def to_s
name
end
end
def Book(name)
Book.new(name)
end
puts Book("To Kill a Mocking Bird")
As a minor point of interest, Ruby's Kernel module uses such a technique (written in C) to implement methods named Array, String, and so on:
Array(12) #=> [12]
String(12) #=> '12'
Integer('0x12') #=> 18

Something like this ?
class Book
def show_me
puts self.to_s
end
def initialize(name)
#name = name
show_me
end
def to_s
#name.to_s
end
end
show_me will be executed but once you create a new book, the book object will be returned at the end.
>> Book.new "To Kill a Mocking Bird"
To Kill a Mocking Bird
=> #<Book:0x74f63f0 #name="To Kill a Mocking Bird">

Related

Ruby classes pass variables between

Is there any way to pass variables between classes?
I have the next code.
module Test
class Super
def initialize(name)
#name = name
end
end
class Upper
def test
puts #name
end
end
end
a=Test::Super.new('My name')
b=Test::Upper.new()
b.test()
Thank you!
No, because a is an instance of the class. Two answers for you;
1) It's better programming practice to have send a to b. So you'd do something like this; (assuming attr_reader :name)
class Upper
def test(s)
s.name
end
end
a = Test::Super.new('My Name')
u = Test::Upper.new
u.test(a)
or you could have it part of the setup; I won't give you all the code, but here's how it'd look
a = Test::Super.new('My name')
b = Test::Upper.new(a)
b.test
=> 'My name'
Neither of these examples is particularly good practice for classes but I imagine you have a more specific use case you're trying to achieve that has been anonymised for the purpose of this question :)
If for some reason instances of the class Upper need to have an access to the internals of instances of the class Super, it means you have a design flaw.
One possible way would be Super needs to expose the variable via a getter:
module Test
class Super
def initialize(name)
#name = name
end
def name
#name
end
end
end
Now you might get the name with Test::Super.new("my name").name.
Another possibility is Upper is actually a subclass of Super:
class Upper < Super
def test
puts #name
end
end
Now Test::Upper.new("my name").test will print "my name", because Upper derives the implementation from Super.
Also, one might use an instance variable on the enclosing module level:
module Test
def self.name=(name)
#name = name
end
def self.name
#name
end
class Super
def initialize(name)
Test.name = name
end
end
class Upper
def test
puts Test.name
end
end
end
This would print:
▶ Test::Super.new("my")
#⇒ #<Test::Super:0x0055dae57fe390>
▶ Test::Upper.new.test
#⇒ "my"
You can make use of class variables in module, which is supported natively by Ruby.
You can do:
module Test
##name = ''
class Super
def initialize(name)
##name = name
end
end
class Upper
def test
puts ##name
end
end
end
a=Test::Super.new('My name')
b=Test::Upper.new()
b.test()

Ruby Invoking Module Method On Extend

Given a basic ruby class and module, is there a way to call a method from the module as soon as an instance of the class is extended ?
class Dog
def initialize(name)
#name = name
end
end
module Speech
def say_name
puts #name
end
# call to method in module ?
say_name
end
fido = Dog.new('fido')
fido.extend Speech => *'fido'*
I am aware of the 'included' method that acts sort of like a callback when a module is included, but I was hoping for something similar for extend.
Here is one trick using the method extend_object.
Extends the specified object by adding this module’s constants and methods (which are added as singleton methods). This is the callback method used by Object#extend.
class Dog
def initialize(name)
#name = name
end
end
module Speech
def Speech.extend_object(o)
super
puts o.say_name
end
def say_name
#name
end
end
fido = Dog.new('fido')
fido.extend Speech # 'fido'

I can not use in-class assigned property of a class in Ruby

Well, I can do this:
class Person
attr_accessor :name
def greeting
"Hello #{#name}"
end
end
p = Person.new
p.name = 'Dave'
p.greeting # "Hello Dave"
but when I decide to assign the property in the class itself it doesnt work:
class Person
attr_accessor :name
#name = "Dave"
def greeting
"Hello #{#name}"
end
end
p = Person.new
p.greeting # "Hello"
This is the default behavior, albeit a confusing one (especially if you're used to other languages in the OOP region).
Instance variables in Ruby starts being available when it is assigned to and normally this happens in the initialize method of your class.
class Person
def initialize(name)
#name = name
end
end
In your examples you're using attr_accessor, this magical method produces a getter and a setter for the property name. A Person#name and Person#name=, method is created which overrides your "inline" instance variable (that's why your first example works and the second one doesn't).
So the proper way to write your expected behaviour would be with the use of a initialize method.
class Person
def initialize(name)
#name = name
end
def greeting
"Hello, #{#name}"
end
end
Edit
After a bit of searching I found this awesome answer, all rep should go to that question.
Think of a Person class as a blueprint that you can create single person instances with. As not all of these person instances will have the name "Dave", you should set this name on the instance itself.
class Person
def initialize(name)
#name = name
end
attr_accessor :name
def greeting
"Hello #{#name}"
end
end
david = Person.new("David")
p david.greeting
# => "Hello David"
mike = Person.new("Mike")
p mike.greeting
# => "Hello Mike"

Adding String functionality to class

Consider this class
class Duck
attr_accessor :name
def initialize(name)
#name = name || 'Donald'
end
# Some quacking methods..
def to_s
"#{#name} (Duck)"
end
end
I would like my duck to respond to methods like upcase, sub, gsub, etc.., so I can do
my_duck = Duck.new("Scrooge")
my_duck.upcase
--> "SCROOGE (DUCK)"
Besides manually implementing these methods, is there a nifty way I can pick out String methods that are not self-mutating and automatically have my class respond to those, then call to_s and then call the method on the resulting string?
You could use the Forwardable module:
require 'forwardable'
class Duck
extend Forwardable
# This defines Duck#upcase and Duck#sub, you can
# add as many methods as you like.
def_delegators(:to_s, :upcase, :sub)
# All the other code here...
end
Duck.new('duffy').upcase
# => DUFFY (DUCK)
Duck.new('rubber').respond_to?(:upcase)
# => true
In general calling def_delegators(:foo, :bar) is equivalent to define the bar method by hand like this:
def bar(*args, &block)
foo.bar(*args, &block)
end
The first argument to def_delegators can be the name of an instance variable, i.e. def_delegators(:#foo, :bar, :baz).
You can use the method_missing method to check if the return value of your to_s implementation responds to this method and call it, if this is the case.
class Duck
attr_accessor :name
def initialize(name)
#name = name || 'Donald'
end
# Some quacking methods..
def to_s
"#{#name} (Duck)"
end
def method_missing(m, *args, &block)
raise NoMethodError unless self.to_s.respond_to? m
self.to_s.send(m, *args, &block)
end
end
d = Duck.new "Donald"
puts d.upcase # DONALD (DUCK)
puts d.swapcase # dONALD (dUCK)
puts d.downcase # donald (duck)
puts d.sub('D') { |m| m.downcase } # donald (Duck)
As I understand your question, you would like to have something as a String Module that you could mixin into your Animal classes. However, String is a Class and the only way to access the methods from String would be inheritance on the burden of tight coupling between String and your classes.
If you need to re-use a lot of String manipulations, I would define a module:
module StringMixins
def upcase
# see e.g. Rubinius link below
end
# ...
end
and include StringMixins into your target classes. That would look like:
class Duck
include StringMixins
# ...
end
Rubinius upcase implementation: https://github.com/rubinius/rubinius/blob/master/kernel/common/string.rb#L735-L738

Ruby assign context to lambda?

Is it possible not to assign context to lambda?
For example:
class Rule
def get_rule
return lambda {puts name}
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
Rule.new.get_rule.call() # should say "ruby" but say what object of class Rull, does not have variable name
# or self.instance_eval &Rule.new.get_rule
end
end
My target is -> stored procedure objects without contexts, and assign context before call in specific places. Is it possible?
A bit late to party, but here's an alternate way of doing this by explicitly passing the context to the rule.
class Rule
def get_rule
return lambda{|context| puts context.name}
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
Rule.new.get_rule.call(self)
end
end
Person.new.init_rule
#=> ruby
Yeah, but be careful with it, this one is really easy to abuse. I would personally be apprehensive of code like this.
class Rule
def get_rule
Proc.new { puts name }
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
instance_eval(&Rule.new.get_rule)
end
end
In the spirit of being really late to the party ;-)
I think the pattern that you are using here is the Strategy pattern.
This separates the concerns between the code that changes "rules" and
the part that is re-used "person". The other strength of this pattern is
that you can change the rules at run-time.
How it could look
class Person
attr_accessor :name
def initialize(&rules)
#name = "ruby"
instance_eval(&rules)
end
end
Person.new do
puts #name
end
=> ruby

Resources