class RagTag
RAGTAG - A Tag Attribute Language for Ruby¶ ↑
RubyTals is a Ruby variation on Zope Page Templates and it’s TAL specification. It differs from TAL in that it is specifically geared for use by Ruby.
Usage¶ ↑
s = %q{ <html> <body> <h1 r:content="x">[X]</h1> <div r:each="animal" r:do="a"> <b r:content="a">[ANIMAL]</b> </div> <div r:if="animal.size > 1"> There are <b r:content="animal.size">[ANIMAL SIZE]</b> animals. </div> </body> </html> } x = 'Our Little Zoo' animal = ['Zebra', 'Monkey', 'Tiger' ] puts Ragtag.compile(s, binding)
Note¶ ↑
Presently RagTag
clauses can run arbitraty Ruby code. Although upping the safety level before executing a compiled template should be sufficiently protective in most cases, perhaps it would be better to limit valid expressions to single object references, ie. ‘this.that’, and then use a substitution of ‘.’ for ‘/’. Not only would this be highly protective, it would also be more compatible with the original TAL spec; albeit this isn’t exacty how TALs interprets the ‘/’ divider.
On the other hand perhaps it is too much restraint. For instance it would require the if-clause in the above exmaple to be something like:
<div r:if="animal/plenty">
and have a definition in the evaluating code:
def animal.plenty size > 1 end
It’s a classic Saftey vs. Usability trade-off. Something to consider for the future.
Constants
- VERSION
TODO: This is only here b/c of bug in Ruby 1.8.x.
Attributes
Public Class Methods
# File lib/ragtag.rb, line 60 def self.compile(xml, scope=nil) new(xml).compile(scope) end
Access to project metadata as constants.
# File lib/ragtag/version.rb, line 12 def self.const_missing(name) key = name.to_s.downcase metadata[key] || super(name) end
Access to project metadata.
# File lib/ragtag/version.rb, line 4 def self.metadata @metadata ||= ( require 'yaml' YAML.load(File.new(File.dirname(__FILE__) + '/../ragtag.yml')) ) end
# File lib/ragtag.rb, line 77 def initialize(xml) case xml when String @xml = Nokogiri::XML(xml) else @xml = xml end end
Public Instance Methods
# File lib/ragtag.rb, line 87 def compile(scope=nil) scope = scope || $TOPLEVEL_BINDING parse(@xml.root, scope) xml end
# File lib/ragtag.rb, line 103 def parse(node, scope) case node when Nokogiri::XML::Text # nothing when Nokogiri::XML::NodeSet parse_nodeset(node, scope) when Nokogiri::XML::Element if value = node['define'] eval(value, scope) end if node['if'] parse_if(node, scope) #elsif node['condition'] # parse_condition(node, scope) end if node['content'] parse_content(node, scope) elsif node['replace'] parse_replace(node, scope) end if node['attr'] || node['attributes'] parse_attributes(node, scope) end if node['each'] parse_each(node, scope) return elsif node['repeat'] parse_repeat(node, scope) return end node.children.each do |child| parse(child, scope) end if node['omit'] && node['omit'] != 'false' parse_omit(node, scope) end else raise node.inspect end return node end
# File lib/ragtag.rb, line 174 def parse_attributes(node, scope) if attrs = node['attr'] assoc = attrs.split(',').map{ |e| e.strip.split(':') } assoc.each do |(k,v)| node[k] = eval(v, scope).to_s end node.remove_attribute('attr') end if attrs = node['attributes'] assoc = attrs.split(',').map{ |e| e.strip.split(':') } assoc.each do |(k,v)| node[k] = eval(v, scope).to_s end node.remove_attribute('attributes') end node end
# File lib/ragtag.rb, line 160 def parse_content(node, scope) value = node['content'] node.content = eval(value, scope) node.remove_attribute('content') end
# File lib/ragtag.rb, line 219 def parse_each(node, scope) value = node['each'] args = node['do'] || 'x' copy = node.dup node.children.remove bindings = eval("#{value}.map{ |#{args}| binding }", scope) bindings.each do |each_scope| sect = parse(copy.dup.children, each_scope) sect.each do |x| node << x end end node.remove_attribute('each') node.remove_attribute('do') value end
# File lib/ragtag.rb, line 193 def parse_if(node, scope) value = node['if'] if eval(value, scope) node.remove_attribute('if') parse(node.children, scope) else node.unlink end node end
# File lib/ragtag.rb, line 152 def parse_nodeset(nodeset, scope) nodeset.each do |node| parse(node, scope) end nodeset end
# File lib/ragtag.rb, line 254 def parse_omit(node, scope) #parse(node.children, scope).each do |x| node.children.each do |x| x.unlink node.add_previous_sibling(x) end node.unlink end
# File lib/ragtag.rb, line 237 def parse_repeat(node, scope) value = node['repeat'] args = node['do'] || 'x' copy = node.dup copy.remove_attribute('repeat') copy.remove_attribute('do') bindings = eval("#{value}.map{ |#{args}| binding }", scope) bindings.each do |each_scope| sect = parse(copy.dup, each_scope) node.add_previous_sibling(sect) # parse_omit(sect, scope) if sect['omit'] && sect['omit'] != 'false' end node.unlink value end
# File lib/ragtag.rb, line 167 def parse_replace(node, scope) value = node['replace'] node.before(eval(value, scope).to_s) node.unlink end