On Tue, May 31, 2011 at 8:05 AM, Ilias L.
[email protected]wrote:
a) Can such an “attrib” method be implemented with ruby code?
Almost certain this can’t be done. You could do something somewhat close
to
that, as seen in the below example. But it relies on hacking
method_missing
and defining constants as methods which catch their call and store them
for
later evaluation, eventually being evaluated when arriving at the attrib
method.
However, you would be susceptible to all the method_missing bugs. In
fact,
your example even hit one, because Class.new.respond_to?(:name) is true,
it
wouldn’t hit method_missing. This is why in the example, it must be
called
name2. Also, to implement this, you must interfere with the natural flow
of
method missing, so if you actually do invoke a missing method anywhere,
then
this code will think it is part of a signature for some method you’re
passing to attrib.
module ImDoingThisForTheChallengeEvenThoughItsAHorribleIdea
def next_meth_sig
@next_meth_sig ||= []
end
def attrib(signature)
methname, *to_return = signature.reverse
signature.clear
define_method(methname) { to_return }
end
def method_missing(meth, *args, &block)
# when empty, args may be [String], [Integer], etc
# because they get treated as const lookup when doing attrib a
Integer
# but get treated as method call when doing attrib a Integer b
return next_meth_sig << meth unless next_meth_sig.empty?
next_meth_sig.concat args << meth
end
[String, Integer].each do |const|
define_method const.to_s do |args|
next_meth_sig << const
end
end
end
require ‘rspec’
describe “ImDoingThisForTheChallengeEvenThoughItsAHorribleIdea#attrib”
do
before :each do
@class = Class.new do
extend ImDoingThisForTheChallengeEvenThoughItsAHorribleIdea
end
end
context ‘when given “attrib name2”’ do
before { @class.instance_eval { attrib name2 } }
subject { @class.new }
it { should respond_to :name2 }
its(:name2) { should == [] }
end
context ‘when given “attrib name2 String”’ do
before { @class.instance_eval { attrib name2 String } }
subject { @class.new }
its(:name2) { should == [String] }
end
context ‘when given “attrib name2 String opt1 opt2”’ do
before { @class.instance_eval { attrib name2 String opt1 opt2 } }
subject { @class.new }
it { should respond_to :name2 }
its(:name2) { should == [String, :opt1, :opt2] }
end
specify ‘“attrib age Integer” should define #age which returns
[Integer]’
do
@class.instance_eval { attrib age Integer }
@class.new.age.should == [Integer]
end
it “should work with Illias’ example” do
class Persnon
extend ImDoingThisForTheChallengeEvenThoughItsAHorribleIdea
attrib name2 String opt1 opt2
attrib age Integer
end
p = Persnon.new
p.name2.should == [String, :opt1, :opt2]
p.age.should == [Integer]
end
end