Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env ruby
class Module
private
def delegate(*args)
_define_delegators(caller.first, args, false)
end
def delegate_maybe(*args)
_define_delegators(caller.first, args, true)
end
def _define_delegators(from, args, fallback)
file, line = from.split(':', 2)
dest, prefix = _extract_valid_delegation_options(args.pop)
args.each do |arg|
module_eval(
_delegator_method([prefix, arg].compact.join('_'), dest, arg, fallback),
file, line.to_i - 4
)
end
end
def _delegator_method(method_name, dest, dest_method, fallback = false)
fallback_code = 'to = NilDelegate.new if to.nil?' if fallback
%{
def #{method_name}(*args, &block)
to = __send__(:#{dest})
#{fallback_code}
to.__send__(:#{dest_method}, *args, &block)
end
}
end
def _extract_valid_delegation_options(opts)
if Hash === opts && opts.has_key?(:to)
opts.values_at(:to, :prefix)
else
raise ArgumentError, 'Invalid delegation options. Delegate with :to.'
end
end
end
class NilDelegate < BasicObject
delegate *(nil.methods - [:__send__, :object_id]), :to => :__nil__
private
def __nil__
nil
end
def method_missing(method_id, *args, &block)
nil
end
def respond_to_missing?(method_id, include_private = false)
true
end
end
class Owner
attr_accessor :name
def initialize(name)
@name = name
end
def play
puts 'ZOMG PLAYTIME!'
end
end
class Cat
attr_accessor :name, :owner
delegate :name, :name=, :to => :owner, :prefix => 'servant'
delegate_maybe :play, :to => :owner
def initialize(name, owner = nil)
@name, @owner = name, owner
end
end
ernie = Owner.new('Ernie')
esther = Cat.new('Esther', ernie)
esther.name # => "Esther"
esther.servant_name # => "Ernie"
esther.play # => "ZOMG PLAYTIME!"
esther.servant_name = 'Ernest'
ernie.name # => "Ernest"
esther.owner = nil
esther.play # => nil
esther.servant_name
# => undefined method `name' for nil:NilClass (NoMethodError)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment