views:

97

answers:

2

I'm trying to write a DSL that allows me to do

Policy.name do
 author "Foo"
 reviewed_by "Bar"
end

The following code can almost process it:

class Policy
  include Singleton
  def self.method_missing(name,&block)
      puts name
      puts "#{yield}"
  end
  def self.author(name)
   puts name
  end
  def self.reviewed_by(name)
   puts name
  end
end

Defining my method as class methods (self.method_name) i can access it using the following syntax:

Policy.name do
 Policy.author "Foo"
 Policy.reviewed_by "Bar"
end

If i remove the "self" from the method names, and try to use my desired syntax, then i receive an error "Method not Found" in the Main so it could not find my function until the module Kernel. Its ok, i understand the error. But how can i fix it? How can i fix my class to make it work with my desired syntax that?

+4  A: 

In order to control what self is in the scope of the block (since author resolves to self.author), you can use instance_eval.

class Policy
  def self.name(&block)
    PolicyNameScope.new(block)
  end

  class PolicyNameScope
    def initialize(block)
      instance_eval(&block)     
    end

    def author(author)
      @author = author
    end

    def reviewed_by(reviewed_by)
      @reviewed_by = reviewed_by
    end
  end
end

policy = Policy.name do
  author "Dawg"
  reviewed_by "Dude"
end

p policy
# => #<Policy::PolicyNameScope:0x7fb81ef9f910 @reviewed_by="Dude", @author="Dawg">

The PolicyNameScope class has the instance methods that are allowed in the name block. This is so that methods from Policy isn't available inside the block, making the DSL a whole lot tighter.

Since your example is out of context I can't help you any further - this code by itself doesn't seem very useful.

August Lilleaas
hi August, i'm playing with ruby metaprogramming and your example + explanation helped me a lot. StackOverflow Rules!!
VP
+1  A: 

The above answer suggested by August is correct, however if you want to use your Constructor for some other purpose, then the above method fails. Therefore, in that situation you have to use some other method other than that described above. Here's the solution that I have prepared without using the class constructor.

class Policy

  def self.method_missing(name,&block)
    self.class_eval(&block)
  end
  def self.author(name)
    p name
  end
  def self.reviewed_by(name)
    p name
  end

end

Policy.name do
  author "Foo"
  reviewed_by "Bar"
end

This is not an optimized solution but a solution to your stated problem.

Cody