I have several methods which have similar conditional logic and I think it's reasonable to DRY them.
class A
def parent1(val)
puts "parent1 A #{val}"
end
def parent2(val)
puts "parent2 A #{val}"
end
end
class B < A
def parent1(val)
if val
puts "foo"
else
super
end
end
def parent2(val)
if val
puts "bar"
else
super
end
end
end
Is it possible to create a #puts_or_super which would be able to call super of its caller? (B#parent1 or B#parent2) So the code will look like this:
def parent1(val)
puts_or_super(val, "foo")
end
def parent2(val)
puts_or_super(val, "bar")
end
Edit: this works but looks insane
def puts_or_super(val, text)
if val
puts text
else
self.class.superclass.instance_method(caller[0][/`.*'/][1..-2].to_sym).bind(self).call
end
end
Any better solutions?
Suppose your definitions are in class A, and the relevant super methods are defined in class B so that A inherits from B.
I think you should go the other way around. Make B a module, and prepend B to A. Then, the definitions where you have the "foo", "bar", etc. would not be the super methods, and the conditions will be in the prepended module.
module B
def parent1(val)
return super if val
... # your original logic in super class
end
def parent2(val)
return super if val
... # your original logic in super class
end
end
class A
prepend B
def parent1(_); puts "foo" end
def parent2(_); puts "bar" end
end
Related
I'm trying to create a method that passes the caller as the default last argument. According to this, I only need:
class A
def initialize(object = self)
# work with object
end
end
so that in:
class B
def initialize
A.new # self is a B instance here
end
end
self will be B rather than A;
However, this doesn't seem to work. Here's some test code:
class A
def self.test test, t=self
puts t
end
end
class B
def test test,t=self
puts t
end
end
class T
def a
A.test 'hey'
end
def b
B.new.test 'hey'
end
def self.a
A.test 'hey'
end
def self.b
B.new.test'hey'
end
end
and I get:
T.new.a # => A
T.new.b # => #<B:0x000000015fef00>
T.a # => A
T.b # => #<B:0x000000015fed98>
whereas I expect it to be T or #<T:0x000000015fdf08>. Is there a way to set the default last argument to the caller?
EDIT:
class Registry
class << self
def add(component, base=self)
self.send(component).update( base.to_s.split('::').last => base)
end
end
end
The idea is pretty simple, you would use it like this
class Asset_Manager
Registry.add :utilities
end
and you access it like:
include Registry.utilities 'Debugger'
I'm trying to de-couple classes by having a middle-man management type class that takes care of inter-class communications, auto-loading of missing classes and erroring when it doesn't exist, it works but I just want to be able to use the above rather than:
class Asset_Manager
Registry.add :utilities, self
end
It just feels cleaner, that and I wanted to know if such a thing was possible.
You can't escape the explicit self. But you can hide it with some ruby magic.
class Registry
def self.add(group, klass)
puts "registering #{klass} in #{group}"
end
end
module Registrable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def register_in(group)
Registry.add(group, self)
end
end
end
class AssetManager
include Registrable
register_in :utilities
end
# >> registering AssetManager in utilities
In short, you can't.
Ruby resolves the default arguments in the context of the receiver. That is, the object before the . in a method call. What you called the receiver should be the caller, actually.
class A
def test1(value = a)
puts a
end
def test2(value = b)
puts b
end
def a
"a"
end
end
a = A.new
a.test1 #=> a
def a.b; "b" end
a.test2 #=> b
If I were you, I would use the extended (or included) hook, where both the extending class and the extended module can be accessed. You can program what ever logic you want based on the information.
module Registry
module Utilities
def self.extended(cls)
#puts cls
::Registry.send(component).update( cls.to_s.split('::').last => cls)
end
end
end
class Asset_Manager
extend Registry::Utilities
end
Given two Classes
class A
def method_a ()
method_b()
end
def method_b ()
puts "Comes from A"
end
end
and B inheriting from A
class B < A
def method_a ()
super()
end
def method_b ()
puts "Comes from B"
end
end
When i call B.method_a the Outcome would be: Comes from B. Is there a possibility to tell A to call its method_b instead of my overwritten one? (So that the result would be Comes from A)
There isn't an equivalent of C++'s A::method_a
You could however do
class A
def method_a
A.instance_method(:method_b).bind(self).call
end
def method_b
puts "Comes from A"
end
end
class B < A
def method_a
super
end
def method_b
puts "Comes from B"
end
end
What is happening here in method_a is that we're retrieving A's implementation of method_b and calling it directly.
This does somewhat fly in the face of using inheritance in the first place though
I don't think you can do it straight forward, without putting a hack, but wouldn't it defeat the purpose of inheritance and template pattern, as there can be a C class as well inheriting from A, and you may want to call method_b function of class C only, as it has been over-written and required.
What you ask is not supported, since it defeats the point of inheritance. What you can do, is split the implementation into two methods - one that does As implementation of method_b, which is not overridden, and one that delegates to that implementation, which may be overridden:
class A
def method_a
dont_override_me
end
def method_b
dont_override_me
end
private
def dont_override_me
puts "Comes from A"
end
end
class B < A
def method_a
super
end
def method_b
puts "Comes from B"
end
end
class A
def method_a; comes_from_A end
def method_b; puts "Comes from A" end
alias comes_from_A method_b
end
class B < A
def method_a; super end
def method_b; puts "Comes from B" end
end
You could modify the child class as follows:
class B < A
def method_a
self.class.send(:remove_method, :method_b)
super()
self.class.send(:alias_method, :method_b, :method_b_alias)
end
def method_b
puts "Comes from B"
end
alias_method :method_b_alias, :method_b
end
b = B.new
b.method_b
# Comes from B
b.method_a
# Comes from A
b.method_b
# Comes from B
The alias :method_b_alias is created when the child class is created. Whenever method_a is called on an instance of of B, B's method method_b is removed before super is invoked, so A's method_a will not find it and use its own method_a instead (since the receiver of A's method_a (self) is the instance of B.) After super returns, alias_method is used to once again make B's method_a available to instances of B.
We need to use Module#remove_method rather than Module#undef_method. If the latter were used, A's method_a would give up looking for method_b after finding that B did not have that method.
If you prefer, you could change two lines to use the keyword alias rather than alias_method:
self.class.send(:alias :method_b :method_b_alias)
alias :method_b_alias :method_b
I want to call instance_eval on this class:
class A
attr_reader :att
end
passing this method b:
class B
def b(*args)
att
end
end
but this is happening:
a = A.new
bb = B.new
a.instance_eval(&bb.method(:b)) # NameError: undefined local variable or method `att' for #<B:0x007fb39ad0d568>
When b is a block it works, but b as a method isn't working. How can I make it work?
It's not clear exactly what you goal is. You can easily share methods between classes by defining them in a module and including the module in each class
module ABCommon
def a
'a'
end
end
class A
include ABCommon
end
Anything = Hash
class B < Anything
include ABCommon
def b(*args)
a
end
def run
puts b
end
end
This answer does not use a real method as asked, but I didn't need to return a Proc or change A. This is a DSL, def_b should have a meaningful name to the domain, like configure, and it is more likely to be defined in a module or base class.
class B
class << self
def def_b(&block)
(#b_blocks ||= []) << block
end
def run
return if #b_blocks.nil?
a = A.new
#b_blocks.each { |block| a.instance_eval(&block) }
end
end
def_b do
a
end
end
And it accepts multiple definitions. It could be made accept only a single definition like this:
class B
class << self
def def_b(&block)
raise "b defined twice!" unless #b_block.nil?
#b_block = block
end
def run
A.new.instance_eval(&#b_block) unless #b_block.nil?
end
end
def_b do
a
end
end
In the code below, I would like to call the class method done of the class that includes the module from inside self.hello
Explanation:
A::bonjour will call Mod::hello so will B::ciao
I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done
module Mod
def self.hello
puts "saying hello..."
end
end
class A
include Mod
def self.bonjour
Mod::hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
Mod::hello
end
def self.done
puts "finitto"
end
end
While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.
(I prefer not having to pass an argument to mixin methods like this when other means exist.)
The output:
pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto
The guts:
module Mod
module ClassMethods
def hello
puts "saying hello..."
done
end
end
def self.included(clazz)
clazz.extend ClassMethods
end
end
The modified class declarations, removing the explicit module reference:
class A
include Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
hello
end
def self.done
puts "finitto"
end
end
You may also supply a default implementation of done:
module Mod
module ModMethods
def hello
puts "saying hello..."
done
end
def done
throw "Missing implementation of 'done'"
end
end
def self.included(clazz)
clazz.extend ModMethods
end
end
As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:
module Mod
def hello
puts "saying hello..."
done
end
def done
raise NotImplementError("Missing implementation of 'done'")
end
end
And the classes using extend:
class A
extend Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
Heres what I have/want:
module Observable
def observers; #observers; end
def trigger(event, *args)
good = true
return good unless (#observers ||= {})[event]
#obersvers[event].each { |e| good = false and break unless e.call(self, args) }
good
end
def on(event, &block)
#obersvers ||= {}
#obersvers[event] ||= []
#observers[event] << block
end
end
class Item < Thing
include Observable
def pickup(pickuper)
return unless trigger(:before_pick_up, pickuper)
pickuper.add_to_pocket self
trigger(:after_pick_up, pickuper)
end
def drop(droper)
return unless trigger(:before_drop, droper)
droper.remove_from_pocket self
trigger(:after_drop, droper)
end
# Lots of other methods
end
# How it all should work
Item.new.on(:before_pickup) do |item, pickuper|
puts "Hey #{pickuper} thats my #{item}"
return false # The pickuper never picks up the object
end
While starting on trying to create a game in Ruby, I thought it would be great if it could be based all around Observers and Events. The problem is have to write all of these triggers seems to be a waste, as it seems like a lot of duplicated code. I feel there must be some meta programming method out there to wrap methods with functionality.
Ideal Sceanrio:
class CustomBaseObject
class << self
### Replace with correct meta magic
def public_method_called(name, *args, &block)
return unless trigger(:before_+name.to_sym, args)
yield block
trigger(:after_+name.to_sym, args)
end
###
end
end
And then I have all of my object inherit from this Class.
I'm still new to Ruby's more advanced meta programming subjects, so any knowledge about this type of thing would be awesome.
There are a several ways to do it with the help of metaprogramming magic. For example, you can define a method like this:
def override_public_methods(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
end
override_public_methods(CustomBaseObject)
foo = CustomBaseObject.new
foo.test(2) { puts 'Block!' }
# => Foo
Test: 2
Block!
Bar
In this case, you figure out all the required methods defined in the class by using instance_methods and then override them.
Another way is to use so-called 'hook' methods:
module Overrideable
def self.included(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
include Overrideable
end
The included hook, defined in this module, is called when you include that module. This requires that you include the module at the end of the class definition, because included should know about all the already defined methods. I think it's rather ugly :)