Skip to content

Instantly share code, notes, and snippets.

@gabrielg
Created October 7, 2009 19:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabrielg/204320 to your computer and use it in GitHub Desktop.
Save gabrielg/204320 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'rubygems'
require 'parse_tree'
require 'unified_ruby'
require 'ruby2ruby'
module ArgumentCoercion
def coerce_def(method_name, coercions)
parser = ParseTree.new(false)
unifier = Unifier.new
old_sexp = parser.parse_tree_for_method(self, method_name)
method_sexp = unifier.process(old_sexp)
splat_args = method_sexp[2][1..-1].collect do |n|
next(nil) unless n.is_a?(Symbol) && n.to_s[0] == ?*
n.to_s[1..-1]
end.compact
coercion_calls = coercions.inject([[],[]]) do |parallel,(arg_name,coercion)|
parallel[0] << arg_name.to_s
if splat_args.include?(arg_name.to_s)
parallel[1] << "#{arg_name}.collect {|a| a.#{coercion}}"
else
parallel[1] << "#{arg_name}.#{coercion}"
end
parallel
end
coercion_code = "#{coercion_calls[0].join(", ")} = #{coercion_calls[1].join(", ")}"
coerced_code = Ruby2Ruby.new.process(method_sexp).sub("\n", "\n #{coercion_code}\n")
module_eval(coerced_code)
end
end
class Module
include ArgumentCoercion
end
if $PROGRAM_NAME == __FILE__
require 'riot'
class CoercedClass
def single_def(foo, bar)
return foo, bar
end
coerce_def :single_def, :foo => :to_sym
def standard_def(foo, bar, baz)
return foo, bar, baz
end
coerce_def :standard_def, :foo => :to_i, :bar => :to_s, :baz => :to_sym
def splat_def(foo, *bar)
return foo, bar
end
coerce_def :splat_def, :foo => :to_f, :bar => :to_sym
def defaults_def(foo = 3)
return foo
end
coerce_def :defaults_def, :foo => :to_s
class << self
def class_def(foo)
return foo
end
coerce_def :class_def, :foo => :to_sym
end # self
end
module CoercedModule
def regular_def(snafu)
return snafu
end
coerce_def :regular_def, :snafu => :to_sym
def self.module_def(snafu)
return snafu
end
class << self
coerce_def :module_def, :snafu => :to_sym
end
end
context "coerce_def" do
context "given an instance of a class" do
setup do
CoercedClass.new
end
should "return a symbol and a string for single_def" do
topic.single_def("foo", "bar")
end.equals([:foo, "bar"])
should "return an integer, a string, and a symbol for standard_def" do
topic.standard_def("47", :what, "test")
end.equals([47, "what", :test])
should "return a float and array of symbols for splat_def" do
topic.splat_def("4.7", "foo", "bar", "baz")
end.equals([4.7, [:foo, :bar, :baz]])
should "also coerce defaults for defaults_def" do
topic.defaults_def
end.equals("3")
end # given an instance of a class
context "given a class" do
should "allow coercions for class methods like class_def" do
CoercedClass.class_def("ohman")
end.equals(:ohman)
end # given a class
context "given an object extended with a module" do
setup do
Object.new.extend(CoercedModule)
end
should "allow coercions for the module methods" do
topic.regular_def("what")
end.equals(:what)
end # given an object extended with a module
context "given a module" do
should "allow coercions for module methods like module_def" do
CoercedModule.module_def("huh")
end.equals(:huh)
end # given a module
end
Riot.report
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment