On Tue, May 31, 2011 at 8:05 AM, Ilias Lazaridis <ilias / lazaridis.com>wrote:

> ruby 1.9
>
> (this is about the *abilities* or the ruby 1.9 language)
>
> class Persnon
>  attrib name  String opt1 opt2
>  attrib age Integer
> end
>
> The main questions:
> a) Can such an "attrib" method be implemented with ruby code?
> b) Can such an "attrib" functionality be implemented on the C-level
> (as extension, not as modification of source)?
>
> Any examples?
>
> Please notice the requirements:
>
> 1) "name" and not ":name"
> 2) no comma between "name" and "String" and "opt1" ...
>
>
>
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