#
# Stupid bit of ruby muckin
#
# Was trying to do something like this:
#
# "svn co #{src} #{dest}".to(:puts).to(:system)
#
# It's pretty hard if you want the method calls in the #to to be passed to the
# receiver where the line resides. It would be easy if we had a proper #caller
# or Binding.of_caller
#
# But we don't (yet), so I thought I could achieve the same effect by keeping
# a hold of the receiver in a proxy object. So, it goes like this:
#
# pass("svn co #{src} #{dest}").to(:puts).to(:system)
#
# I added a few niceties to make the return value sensible, and also added #into
# which modifies the passed object in place
#
# pass("Foo").into("bar.+").to(:puts)
#
# I have no idea if this is useful, if you find yourself passing the same argument into
# some methods over and over, then you could use this.
#
# Feb 2008 - Ian White http://blog.ardes.com/ian
#
# p.s. I was trying to get a nice sugary syntax rush,
# there's already a bunch of constructs for doing this.
#
# Compare:
#
# # plain ruby
# msg = "oooh!"
# MyObj.foo msg
# MyObj.far msg
# MyObj.faz msg
#
# # #returning from ActiveSupport
# returning "oooh!" do |s|
# MyObj.foo s
# MyObj.far s
# MyObj.faz s
# end
#
# # #pass (this code)
# MyObj.pass("oooh!").to :foo, :far, :faz
#
require 'rubygems'
require 'active_support'
#
# Usage:
#
# Object#pass(obj) creates a PassProxy which delegates methods to obj
#
# except
# #to: which sends obj to the specified methods, returning the PassProxy
# #into: which sends obj to the specified methods, updating the PassProxy with the results
#
# The receiver of #pass is used to determine where the methods are sent in #to and #into
#
# pass("str").to :puts, :system
# # calls main.puts("str")
# # calls main.system("str")
# # => "str"
#
# my_obj.pass(1).into(:times_2)
# # calls my_obj.times_2(1)
# # => 2
#
# Can be chained
#
# pass(1).into("2.+").to(:puts)
# # 2.+(1)
# # => 3
# # main.puts(3)
# # => 3
class Object
def pass(*object, &block)
PassProxy.new(self, *object, &block)
end
end
class PassProxy < BasicObject
def initialize(context, *object, &block)
object = object.first if object.size == 1
@__context__, @__object__, @__block__ = context, object, block
end
def method_missing(method, *args, &block)
@__object__.send(method, *args, &block)
end
def respond_to?(method)
@__object__.respond_to?(method)
end
def to(*methods, &block)
methods << block if block_given?
methods.each {|method| __call_method__(method)}
self
end
def into(*methods, &block)
methods << block if block_given?
methods.each {|method| @__object__ = __call_method__(method)}
self
end
private
def __call_method__(method)
if method.is_a?(Symbol)
@__context__.send method, *@__object__, &@__block__
elsif method.respond_to?(:call)
@__context__.instance_exec *@__object__, &method
elsif method.is_a?(String)
@__context__.instance_exec *@__object__, &eval("proc{|*a| #{method}(*a)}")
else
raise "I can't figure out how to call #{method.inspect}"
end
end
end