Ruby Metaprogramming
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?
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.
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.
精彩评论