This post originated from an RSS feed registered with Ruby Buzz
by Red Handed.
Original Post: One Small Flask of Metaprogramming Elixir
Feed Title: RedHanded
Feed URL: http://redhanded.hobix.com/index.xml
Feed Description: sneaking Ruby through the system
We could make metaprogramming easier for the common streetsfolk. I’m not giving a triumphant answer here. I’m just wondering what else you knobbly ducks out there have done like this.
So you have this class which defines a structure for scripts within a larger program (MouseHole in this case):
class UserScript
attr_accessor :title, :description, :version, :mount,
:register_uri, :rewrite
def initialize
@register_uri, @rewrite = [], {}
end
end
Nothing special. But the initialize does tell us that register_uri and rewrite are an array and a hash, respectively.
Use a method_missing to set the accessors based on their defaults in initialize:
meta_make(UserScript) do
title "MouseCommand"
description "A-wiki-and-a-shell-in-one."
version "2.0"
mount :cmd do
%{ .. }
end
rewrite HTML, XML do
document.change_someways!
end
rewrite CSS do
document.gsub! '.title', '.titleSUPERIOR'
end
# allow methods to be defined
def to_gravy; end
end
So, since rewrite was set up in initialize as a hash, the definition above will result in:
It’s very Railsish, but as people are now so accustomed to that syntax, it could ease transition of Railsers to your project, whatever it be.
Here’s the meta_make method:
def meta_make(klass, &blk)
o = klass.new
(class << o; self; end).instance_eval { @__obj = o }
class << o
def self.method_missing(m, *args, &blk)
set_attr(m, *args, &blk)
end
def self.set_attr(m, *args, &blk)
if blk
args << nil if args.empty?
args << blk
end
v = @__obj.instance_variable_get("@#{m}")
if v.respond_to? :to_ary
(v = v.to_ary).push args
elsif v.respond_to? :to_hash
v = v.to_hash
while args.length > 1
v[args.unshift] = args.last
end
elsif args.length <= 1
v = args.first
else
v = args
end
@__obj.instance_variable_set("@#{m}", v)
end
end
(class << o; self; end).class_eval &blk
o
end