Extend return value of class instance method - ruby

I have a class that have an instance method, that returns a hash. I can't change the code of that class directly, but I can extend it with modules. I need to add some new keys to the returning hash of the method. Something like this:
class Processor
def process
{ a: 1 }
end
end
module ProcessorCustom
def process
super.merge(b: 2) # Not works :(
end
end
Processor.send :include, ProcessorCustom
processor = Processor.new
processor.process # returns { a: 1 }, not { a: 1, b: 2 }
How can I do that? Thanks.

You could call prepend instead of include:
Processor.prepend(ProcessorCustom)
processor = Processor.new
processor.process
#=> {:a=>1, :b=>2}
prepend and include result in different ancestor order:
module A; end
module B; end
module C; end
B.ancestors #=> [B]
B.include(C)
B.ancestors #=> [B, C]
B.prepend(A)
B.ancestors #=> [A, B, C]
Alternatives
Depending on your use-case, you could also extend a specific instance: (this doesn't affect other instances)
processor = Processor.new
processor.extend(ProcessorCustom)
processor.process
#=> {:a=>1, :b=>2}
Or use SimpleDelegator to implement a decorator pattern:
require 'delegate'
class ProcessorCustom < SimpleDelegator
def process
super.merge(b: 2)
end
end
processor = ProcessorCustom.new(Processor.new)
processor.process #=> {:a=>1, :b=>2}

I think the first option to consider would be the one requiring the least work by the reader to comprehend; and in Object Oriented software that would be a subclass to specialize the behavior of the superclass. I would deviate from this if, and only if, there were a compelling reason to do so.
How about this?:
#!/usr/bin/env ruby
class Processor
def foo
{ x: 3 }
end
end
class MyProcessor < Processor
def foo
super.merge({ y: 7 })
end
end
p MyProcessor.new.foo # outputs: {:x=>3, :y=>7}

I think it's better to create a proxy than to pollute the original class.
class Proxy
def initialize(target)
#target = target
end
# add some syntactic sugar
singleton_class.class_eval { alias [] new }
def process
#target.process.merge!(b: 2)
end
end
Proxy[Processor.new].process #=> {a: 1, b: 2}
You can even create your own dynamic proxy.
class DynamicProxy < BasicObject
def initialize(target)
#target = target
end
# again, add some syntactic sugar
singleton_class.class_eval { alias [] new }
def method_missing(name, *args, &block)
super unless #target.respond_to?(name)
# Do something before calling the target method
result = #target.send(name, *args, &block)
# Do something after calling the target method
end
def respond_to_missing?(name, include_private = false)
#target.respond_to?(name, include_private)
end
end
To hide the detail of creating Processor instance, you can make a simple factory to handle the creation.
module ProcessorFactory
def self.create
DynamicProxy[Processor.new]
end
end
Then you can do your job
ProcessorFactory.create.process

Related

Ruby: better way to return several methods as procs from a module

It's pretty easy to return a method like a proc from a module:
module Foo
def self.bar
# Method implementation
end
def self.baz
# Method implementation
end
def self.qux
# Method implemenatation
end
def self.zoo
# Method implementation
end
end
Foo.method(:bar) # Returns a proc object
But what if I want to return more than one (but not all of them) method from the same module?
One way to do that is:[:bar,:baz].inject([]) {|memo,i| memo << Foo.method(i)}
Is there a better, more agile way to do the same?
You can always use monkey patching to make it look however you wish:
class Object
def get_methods(*methods)
methods.map { |m| method(m) }
end
end
module Foo
def self.bar
# Method implementation
end
def self.baz
# Method implementation
end
end
Foo.get_methods(:foo, :baz)
Try this:
Foo.methods(false).map {|e| Foo.method(e)}
Foo.methods(false) will return all class methods that are not inherited as an array and map will iterate over each element.
EDIT
After taking into account the comments below:
proc_arr = [:bar, :baz].map {|e| Foo.method(e)}
I don't know if there is a better way, but maybe this is a bit cleaner than your solution:
proc_methods = []
# => []
(Foo.methods - Object.methods).each { |m| proc_methods << Foo.method(m) }
# => [:bar, :baz]
proc_methods
# => [#<Method: Foo.bar>, #<Method: Foo.baz>]
Foo inherits its methods from Object class, that is the default root of all Ruby objects. (http://ruby-doc.org/core-2.2.3/Object.html)
With Foo.methods - Object.methods you keep only your custom methods.

Dynamic method chain?

How can I call a nested hash of methods names on an object?
For example, given the following hash:
hash = {:a => {:b => {:c => :d}}}
I would like to create a method that, given the above hash, does the equivalent of the following:
object.send(:a).send(:b).send(:c).send(:d)
The idea is that I need to get a specific attribute from an unknown association (unknown to this method, but known to the programmer).
I would like to be able to specify a method chain to retrieve that attribute in the form of a nested hash. For example:
hash = {:manufacturer => {:addresses => {:first => :postal_code}}}
car.execute_method_hash(hash)
=> 90210
I'd use an array instead of a hash, because a hash allows inconsistencies (what if there is more than one key in a (sub)hash?).
object = Thing.new
object.call_methods [:a, :b, :c, :d]
Using an array, the following works:
# This is just a dummy class to allow introspection into what's happening
# Every method call returns self and puts the methods name.
class Thing
def method_missing(m, *args, &block)
puts m
self
end
end
# extend Object to introduce the call_methods method
class Object
def call_methods(methods)
methods.inject(self) do |obj, method|
obj.send method
end
end
end
Within call_methods we use inject in the array of symbols, so that we send every symbol to the result of the method execution that was returned by the previous method send. The result of the last send is automatically returned by inject.
There's a much simpler way.
class Object
def your_method
attributes = %w(thingy another.sub_thingy such.attribute.many.method.wow)
object = Object.find(...)
all_the_things << attributes.map{ |attr| object.send_chain(attr.split('.')) }
end
def send_chain(methods)
methods.inject(self, :try)
end
end
There is no predefined method, but you can define your own method for that:
class Object
def send_chain(chain)
k = chain.keys.first
v = chain.fetch(k)
r = send(k)
if v.kind_of?(Hash)
r.send_chain(v)
else
r.send(v)
end
end
end
class A
def a
B.new
end
end
class B
def b
C.new
end
end
class C
def c
D.new
end
end
class D
def d
12345
end
end
chain = { a: { b: { c: :d } } }
a = A.new
puts a.send_chain(chain) # 12345
Tested with http://ideone.com/mQpQmp

Delegate attribute methods to the parent object

I have the following class:
class Alphabet
attr_reader :letter_freqs, :statistic_letter
def initialize(lang)
#lang = lang
case lang
when :en
#alphabet = ('A'..'Z').to_a
#letter_freqs = { ... }
when :ru
#alphabet = ('А'..'Я').to_a.insert(6, 'Ё')
#letter_freqs = { ... }
...
end
#statistic_letter = #letter_freqs.max_by { |k, v| v }[0]
end
end
foo = Alphabet.new(:en)
The central member here is #alphabet.
I'd like to make it some sort of a container class to invoke Array methods directly like
foo[i]
foo.include?
instead of explicitly accessing #alphabet:
foo.alphabet[i]
foo.alphabet.include?
I know I could define a lot of methods like
def [](i)
#alphabet[i]
end
but I'm looking for a proper way of "inheriting" them.
You can use Forwardable (it is included in the Ruby standard library):
require 'forwardable'
class Alphabet
extend Forwardable
def_delegators :#alphabet, :[], :include?
def initialize
#alphabet = ('A'..'Z').to_a
end
end
foo = Alphabet.new
p foo[0] #=> "A"
p foo.include? 'ç' #=> false
If you wish to delegate all the methods not defined by your class you can use SimpleDelegator (also in the standard library); it lets you delegate all the methods that are not responded by the instance to an object specified by __setobj__:
require 'delegate'
class Alphabet < SimpleDelegator
def initialize
#alphabet = ('A'..'Z').to_a
__setobj__(#alphabet)
end
def index
'This is not #alphabet.index'
end
end
foo = Alphabet.new
p foo[0] #=> "A"
p foo.include? 'ç' #=> false
p foo.index #=> "This is not #alphabet.index"
When the delegate doesn't need to be dynamic you can arrange the master class to be a subclass of DelegateClass, passing the name of the class to be delegated as argument and calling super passing the object to be delegated in the #initialize method of the master class:
class Alphabet < DelegateClass(Array)
def initialize
#alphabet = ('A'..'Z').to_a
super(#alphabet)
end
More info about the delegation design pattern in Ruby here
You could extend the Forwardable module:
class Alphabet
require 'forwardable'
extend Forwardable
attr_accessor :alphabet
def initialize
#alphabet = [1,2,3]
end
def_delegator :#alphabet, :[], :include?
end
Then you can do:
alpha = Alphabet.new
alpha[1]==hey.alphabet[1]
=> true
Warning:
Don't try to delegate all methods (don't know if that's even possible) since they probably share some of the same method names such as class, which would probably make chaos.
In Ruby you can extend Objects like this.
class Array
def second
self[1]
end
end
[1, 2, 3, 4, 5].second
# => 2
Or if you want to inherit an Array.
class Foo < Array
def second
self[1]
end
end
[1, 2, 3, 4, 5].include? 2
# => true
[1, 2, 3, 4, 5].second
# => 2
If you have any further questions, comment and I will update the answer.

Abstracting/generecizing class hierarchy variables in Ruby

I have several classes and I want each one to maintain on the class level a hash of all the instances that have been created for future lookup. Something akin to:
class A
def initialize(id, otherstuff)
# object creation logic
##my_hash[id]=self
end
def self.find(id)
##my_hash[id]
end
end
so I can then A.find(id) and get the right instance back.
There are several of these classes (A, B, etc), all having ids, all of which I want to have this functionality.
Can I have them all inherit from a superclass which has a generic version of this which they can leverage so I don't have to reimplement many things for every class?
Yes, you can either inherit from the same superclass, or use modules and include:
module M
def initialize(id)
##all ||= {}
##all[id] = self
end
def print
p ##all
end
end
class C
include M
def initialize(id)
super
puts "C instantiated"
end
end
If you want to keep separate indexes for each subclass, you can do something like:
def initialize(id)
##all ||= {}
##all[self.class] ||= {}
##all[self.class][id] = self
end
Edit: After your comment, I see that you need to keep per-class indexes. So:
class A
def initialize(id)
self.class.index(id, self)
end
def self.index id, instance
#all ||= {}
#all[id] = instance
end
def self.find(id)
#all[id]
end
end
class B < A
end
class C < A
end
a = A.new(1)
b = B.new(2)
c = C.new(3)
p A.find(1)
#=> #<A:0x10016c190>
p B.find(2)
#=> #<B:0x10016c140>
p C.find(3)
#=> #<C:0x10016c118>
p A.find(2)
#=> nil

"Personal" method in ruby

I'm looking for a way of making a method "personal" - note NOT PRIVATE to a class
here is an example - by "personal" I mean the behaviour of method "foo"
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
end
class C < B
end
a=A.new; b=B.new;c=C.new
I'm looking for a way of producing the following behaviour
a.foo #=> "foo"
b.foo #=> "bar"
c.foo #=> "foo" (ultimate base class method called)
Instead of creating 'personal' methods, change your inheritance structure.
It appears that you want the C class to have only some of the same functionality of the B class while not making changes to the A class.
class A
def foo
"foo"
end
end
class BnC < A
end
class B < BnC
def foo
"bar"
end
end
class C < BnC
end
a=A.new; b=B.new;c=C.new
There's no standard way of doing this. It circumvents how inheritance works. You could implement B's method to do the logic like this:
def foo
instance_of?(B) ? "bar" : super
end
And you could of course define a method on Class that would do this for you similar to public and private.
class Class
def personal(*syms)
special_class = self
syms.each do |sym|
orig = instance_method(sym)
define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super}
end
end
end
Then you can personal :foo in B just like you'd private :foo.
(This isn't at all optimized and I didn't implement the zero-argument behavior that public and private have because frankly it's a huge PITA to do right and even then it's a hack.)
Seems like it could be confusing, but here's one option:
class A
def foo
"foo"
end
end
class B < A
def initialize #when constructing, add the new foo method to each instance
def self.foo
"bar"
end
end
end
class C < B
def initialize #when constructing, do nothing
end
end
More generally, using a similar approach, you can always add a method to a given instance, which of course has no effect on inherited classes or indeed on other instances of the same class.
If you give us specifics of what you're ultimately trying to accomplish we can probably be more helpful.
Answering this is a bit tricky since I don't really see what you want to accomplish in practice, but you could try something like
class C < B
def foo
self.class.ancestors[-3].instance_method(:foo).bind(self).call
end
end
(The ancestors[-3] assumes that A inherits from Object and Kernel and your intent was to access the method from the topmost non-builtin class. Of course you could substitute self.class.ancestors[-3] with just A, or figure out the class from the Array ancestors yourself, etc.)
In practice it would be simpler to alias the original in class B if you can modify it (i.e. alias :foo_from_A :foo in class B < A before the new def foo, then you can call foo_from_A in C). Or just redefine what you want in C. Or design the whole class hierarchy differently.
You can write a shortcut function to handle personalizing methods.
def personalize(*methodNames)
old_init = instance_method(:initialize)
klass = self
modul = Module.new {
methodNames.each { |m|
define_method(m, klass.instance_method(m)) if klass.method_defined?(m)
}
}
methodNames.each { |m|
remove_method(m) if method_defined?(m)
}
define_method(:initialize) { |*args|
# I don't like having to check instance_of?, but this is the only way I
# could thing of preventing the extension of child classes. At least it only
# has to happen once, during initialization.
extend modul if instance_of?(klass)
old_init.bind(self).call(*args)
}
self
end
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
def bam
'bug-AWWK!'
end
personalize :foo, :bam, :nometh
end
class C < B
end
a=A.new; b=B.new; c=C.new
a.foo #=> "foo"
b.foo #=> "bar"
b.bam #=> "bug-AWWK!"
c.foo #=> "foo"
C.instance_method(:foo) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError
Sometimes you don't really want an "is a" (inheritance) relationship. Sometimes what you want is "quacks like a." Sharing code among "quacks like a" classes is easily done by using modules to "mix in" methods:
#!/usr/bin/ruby1.8
module BasicFoo
def foo
"foo"
end
end
class A
include BasicFoo
end
class B
def foo
"bar"
end
end
class C
include BasicFoo
end
p A.new.foo # => "foo"
p B.new.foo # => "bar"
p C.new.foo # => "foo"

Resources