Created
October 7, 2009 19:50
-
-
Save gabrielg/204377 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
def singleton_method_added(name) | |
Thread.current['last_method_added'] = {:name => name.to_sym, :scope => class << self; self; end} | |
end | |
def method_added(name) | |
Thread.current['last_method_added'] = {:name => name.to_sym, :scope => self} | |
end | |
end | |
class NilClass | |
def coerce(coercions) | |
details = Thread.current['last_method_added'] | |
details[:scope].coerce_def(details[:name], coercions) | |
end | |
end | |
if $PROGRAM_NAME == __FILE__ | |
require 'riot' | |
class CoercedClass | |
def single_def(foo, bar) | |
return foo, bar | |
end.coerce(:foo => :to_sym) | |
def standard_def(foo, bar, baz) | |
return foo, bar, baz | |
end.coerce(:foo => :to_i, :bar => :to_s, :baz => :to_sym) | |
def splat_def(foo, *bar) | |
return foo, bar | |
end.coerce(:foo => :to_f, :bar => :to_sym) | |
def defaults_def(foo = 3) | |
return foo | |
end.coerce(:foo => :to_s) | |
class << self | |
def class_def(foo) | |
return foo | |
end.coerce(:foo => :to_sym) | |
end # self | |
end | |
module CoercedModule | |
def regular_def(snafu) | |
return snafu | |
end.coerce(:snafu => :to_sym) | |
class << self | |
def module_def(snafu) | |
return snafu | |
end.coerce(:snafu => :to_sym) | |
end | |
def self.module_def_outside_reopening_self(snafu) | |
return snafu | |
end.coerce(:snafu => :to_f) | |
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) | |
should "allow coercions for module methods like module_def_outside_reopening_self" do | |
CoercedModule.module_def_outside_reopening_self("4.7") | |
end.equals(4.7) | |
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