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
Related
How to define an original name scope in module/class with Ruby
I want to implement class like the following:
module SomeModule
extend OriginalNameScope
scope(:some) do
def method1
puts 1
end
def method2
puts 2
end
end
end
class SomeClass
include SomeModule
end
c = SomeClass.new
# I want to call methods like the following:
c.some_method1
c.some_method2
How to implement the OriginalNameScope module? I found out to get the method definitions in this method, but I don't know how to redefine methods with a prefix scope.
module OriginalNameScope
def scope(name, &method_definition)
puts method_definition.class
# => Proc
end
end
This is actually just a combination of some simple standard Ruby metaprogramming patterns and idioms:
module OriginalNameScope
def scope(name)
singleton_class.prepend(Module.new do
define_method(:method_added) do |meth|
if name && !#__recursion_guard__
#__recursion_guard__ = meth
method = instance_method(meth)
undef_method(meth)
define_method(:"#{name}_#{meth}") do |*args, &block|
method.bind(self).(*args, &block)
end
end
#__recursion_guard__ = nil
super(meth)
end
end)
yield
end
end
I just slapped this together, there's probably a lot that can be improved (e.g. use Refinements) and simplified.
I try to write a metaprogramming for execute a method before 'master' method. Why ? Because, I have several class and it's ugly to repeat the call in the head of the method
Case :
class MyClass
include MySuperModule
before :method, call: before_method
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
module MySuperModule
# the awesome code
end
Output :
SomeClass.new.method => "Before.. Method.."
So, I try write a module with ClassMethodsor method_missingwithout success.
You don't need a gem for simple metaprogramming like this. What you can do is redefine the "after" method to call the "before" method and then the original "after" method.
This works even when using before multiple times on the same method or when creating a chain of before calls.
module MySuperModule
def before meth, opts
old_method = instance_method(meth)
define_method(meth) do
send opts[:call]
old_method.bind(self).call
end
end
end
class MyClass
extend MySuperModule
def foo
puts "foo"
end
def bar
puts "bar"
end
def baz
puts "baz"
end
before :foo, call: :bar
before :bar, call: :baz
end
MyClass.new.foo
# baz
# bar
# foo
If it is just for subclassing purposes you can take advantage of Module#prepend:
class Superclass
def self.inherited(subclass)
# subclass.send :prepend, Module.new { on Ruby < 2.1
subclass.prepend Module.new {
def method
before_method
super
end
}
end
def before_method
puts 'Before'
end
end
class Subclass < Superclass
def method
puts 'Method'
end
end
Subclass.new.method
#=> Before
#=> Method
What you are looking for is Aspect oriented programming support for ruby. There are several gems implementing this, like aquarium.
Another way to do this is to use the rcapture gem.
It is pretty awesome.
Eg:
require 'rcapture'
class A
# Makes the class intercept able
include RCapture::Interceptable
def first
puts 'first'
end
def second
puts 'second'
end
end
# injects methods to be called before each specified instance method.
A.capture_pre :methods => [:first, :second] do
puts "hello"
end
n = A.new
n.first
n.second
produces:
hello
first
hello
second
Maybe you can use a decorator. In ruby there is a nice gem called 'drapeer'. See Drapper Link
Every call in ruby runs through set_trace_func so you can hook into that and call exactly what you want. Not the prettiest solution and there are better ways but it does work. Another option is the Hooks gem, though I haven't tried it myself, it looks like it should give you the ability to do what you want.
module MySuperModule
# the awesome code
end
class MyClass
include MySuperModule
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
set_trace_func proc { |event, file, line, id, binding, class_name|
if event == "call" && class_name == SomeClass && id == :method
caller = binding.eval("self")
caller.send(:before_method)
end
}
SomeClass.new.method
#=> Before..
#=> Method..
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">
I have the following code:
#!/usr/bin/ruby
class Person
def self.speak
p = self.new
puts "Hello"
p.chatter
end
private
def chatter
puts "Chattering"
end
end
p = Person.new
Person.speak
I'd like to make chatter private, accessible only within p.. but I want p to be able to access it within the class method. Is there a better way to design this so chatter isn't available to the public, but a "factory" method like self.speak can call chatter?
In Ruby 1.8.7, "send" bypasses the usual protections against calling private methods:
#!/usr/bin/ruby1.8
class Person
def self.speak
puts "Hello"
new.send(:chatter)
end
def speak
puts "Hello"
puts chatter
end
private
def chatter
puts "Chattering"
end
end
Person.speak # => Hello
# => Chattering
Person.new.speak # => Hello
# => Chattering
However, what you want can be achieved without any voodoo, by simply having the class method do all the work, and the instance method defer to the class method:
class Person
def self.speak
puts "Hello"
puts "Chatter"
end
def speak
self.class.speak
end
end
If you had more than a few of these forwarding methods, it might be convenient to make a helper method that makes them for you:
module DelegateToClass
def delegate_to_class(name)
define_method(name) do |*args|
self.class.send(name, *args)
end
end
end
class Person
extend DelegateToClass
def self.speak
puts "Hello"
puts "Chatter"
end
delegate_to_class :speak
end
The built-in module Forwardable can do this just as well:
require 'forwardable'
class Person
extend Forwardable
def self.speak
puts "Hello"
puts "Chatter"
end
def_delegator self, :speak
end
def_delegator also bypasses protections against private methods.
There's a number of ways to do this. One approach would be a class method which instantiates a new instance and calls the #speak method on the instance.
class Person
def self.speak
new.speak
end
def speak
puts "Hello"
chatter
end
private
def chatter
puts "Chattering"
end
end
Then you could call it in either the class or instance context:
p = Person.new
p.speak
# ...or...
Person.speak
What's the best way to abstract this pattern:
class MyClass
attr_accessor :foo, :bar
def initialize(foo, bar)
#foo, #bar = foo, bar
end
end
A good solution should take superclasses into consideration and be able to handle still being able to have an initializer to do more things. Extra points for not sacrificing performance in your solution.
A solution to that problem already (partially) exists, but if you want a more declarative approach in your classes then the following should work.
class Class
def initialize_with(*attrs, &block)
attrs.each do |attr|
attr_accessor attr
end
(class << self; self; end).send :define_method, :new do |*args|
obj = allocate
init_args, surplus_args = args[0...attrs.size], args[attrs.size..-1]
attrs.zip(init_args) do |attr, arg|
obj.instance_variable_set "##{attr}", arg
end
obj.send :initialize, *surplus_args
obj
end
end
end
You can now do:
class MyClass < ParentClass
initialize_with :foo, :bar
def initialize(baz)
#initialized = true
super(baz) # pass any arguments to initializer of superclass
end
end
my_obj = MyClass.new "foo", "bar", "baz"
my_obj.foo #=> "foo"
my_obj.bar #=> "bar"
my_obj.instance_variable_get(:#initialized) #=> true
Some characteristics of this solution:
Specify constructor attributes with initialize_with
Optionally use initialize to do custom initialization
Possible to call super in initialize
Arguments to initialize are the arguments that were not consumed by attributes specified with initialize_with
Easily extracted into a Module
Constructor attributes specified with initialize_with are inherited, but defining a new set on a child class will remove the parent attributes
Dynamic solution probably has performance hit
If you want to create a solution with absolute minimal performance overhead, it would be not that difficult to refactor most of the functionality into a string which can be evaled when the initializer is defined. I have not benchmarked what the difference would be.
Note: I found that hacking new works better than hacking initialize. If you define initialize with metaprogramming, you'd probably get a scenario where you pass a block to initialize_with as a substitute initializer, and it's not possible to use super in a block.
This is the first solution that comes to my mind. There's one big downside in my module: you must define the class initialize method before including the module or it won't work.
There's probably a better solution for that problem, but this is what I wrote in less than a couple of minutes.
Also, I didn't keep performances too much into consideration. You probably can find a much better solution than me, especially talking about performances. ;)
#!/usr/bin/env ruby -wKU
require 'rubygems'
require 'activesupport'
module Initializable
def self.included(base)
base.class_eval do
extend ClassMethods
include InstanceMethods
alias_method_chain :initialize, :attributes
class_inheritable_array :attr_initializable
end
end
module ClassMethods
def attr_initialized(*attrs)
attrs.flatten.each do |attr|
attr_accessor attr
end
self.attr_initializable = attrs.flatten
end
end
module InstanceMethods
def initialize_with_attributes(*args)
values = args.dup
self.attr_initializable.each do |attr|
self.send(:"#{attr}=", values.shift)
end
initialize_without_attributes(values)
end
end
end
class MyClass1
attr_accessor :foo, :bar
def initialize(foo, bar)
#foo, #bar = foo, bar
end
end
class MyClass2
def initialize(*args)
end
include Initializable
attr_initialized :foo, :bar
end
if $0 == __FILE__
require 'test/unit'
class InitializableTest < Test::Unit::TestCase
def test_equality
assert_equal MyClass1.new("foo1", "bar1").foo, MyClass2.new("foo1", "bar1").foo
assert_equal MyClass1.new("foo1", "bar1").bar, MyClass2.new("foo1", "bar1").bar
end
end
end
class MyClass < Struct.new(:foo, :bar)
end
I know this is an old question with perfectly acceptable answers but I wanted to post my solution as it takes advantage of Module#prepend (new in Ruby 2.2) and the fact that modules are also classes for very simple solution. First the module to make the magic:
class InitializeWith < Module
def initialize *attrs
super() do
define_method :initialize do |*args|
attrs.each { |attr| instance_variable_set "##{attr}", args.shift }
super *args
end
end
end
end
Now let's use our fancy module:
class MyClass
prepend InitializeWith.new :foo, :bar
end
Note that I left our the attr_accessible stuff as I consider that a separate concern although it would be trivial to support. Now I can create an instance with:
MyClass.new 'baz', 'boo'
I can still define an initialize for custom initialization. If my custom initialize take an argument those will be any extra arguments provided to the new instance. So:
class MyClass
prepend InitializeWith.new :foo, :bar
def initialize extra
puts extra
end
end
MyClass.new 'baz', 'boo', 'dog'
In the above example #foo='baz', #bar='boo' and it will print dog.
What I also like about this solution is that it doesn't pollute the global namespace with a DSL. Objects that want this functionality can prepend. Everybody else is untouched.
This module allows an attrs hash as an option to new(). You can include the module in a class with inheritance, and the constructor still works.
I like this better than a list of attr values as parameters, because, particularly with inherited attrs, I wouldn't like trying to remember which param was which.
module Attrize
def initialize(*args)
arg = args.select{|a| a.is_a?(Hash) && a[:attrs]}
if arg
arg[0][:attrs].each do |key, value|
self.class.class_eval{attr_accessor(key)} unless respond_to?(key)
send(key.to_s + '=', value)
end
args.delete(arg[0])
end
(args == []) ? super : super(*args)
end
end
class Hue
def initialize(transparent)
puts "I'm transparent" if transparent
end
end
class Color < Hue
include Attrize
def initialize(color, *args)
p color
super(*args)
p "My style is " + #style if #style
end
end
And you can do this:
irb(main):001:0> require 'attrize'
=> true
irb(main):002:0> c = Color.new("blue", false)
"blue"
=> #<Color:0x201df4>
irb(main):003:0> c = Color.new("blue", true, :attrs => {:style => 'electric'})
"blue"
I'm transparent
"My style is electric"