Class: RagTag
- Inherits:
-
Object
- Object
- RagTag
- Defined in:
- lib/ragtag.rb,
lib/ragtag/version.rb
Overview
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.
Constant Summary collapse
- VERSION =
TODO: This is only here b/c of bug in Ruby 1.8.x.
['version']
Instance Attribute Summary collapse
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
-
#xml ⇒ Object
readonly
Returns the value of attribute xml.
Class Method Summary collapse
- .compile(xml, scope = nil) ⇒ Object
-
.const_missing(name) ⇒ Object
Access to project metadata as constants.
-
.metadata ⇒ Object
Access to project metadata.
Instance Method Summary collapse
- #compile(scope = nil) ⇒ Object
-
#initialize(xml) ⇒ RagTag
constructor
A new instance of RagTag.
- #parse(node, scope) ⇒ Object
- #parse_attributes(node, scope) ⇒ Object
- #parse_content(node, scope) ⇒ Object
- #parse_each(node, scope) ⇒ Object
- #parse_if(node, scope) ⇒ Object
- #parse_nodeset(nodeset, scope) ⇒ Object
- #parse_omit(node, scope) ⇒ Object
- #parse_repeat(node, scope) ⇒ Object
- #parse_replace(node, scope) ⇒ Object
Constructor Details
#initialize(xml) ⇒ RagTag
Returns a new instance of RagTag.
77 78 79 80 81 82 83 84 |
# File 'lib/ragtag.rb', line 77 def initialize(xml) case xml when String @xml = Nokogiri::XML(xml) else @xml = xml end end |
Instance Attribute Details
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
74 75 76 |
# File 'lib/ragtag.rb', line 74 def scope @scope end |
#xml ⇒ Object (readonly)
Returns the value of attribute xml.
71 72 73 |
# File 'lib/ragtag.rb', line 71 def xml @xml end |
Class Method Details
.compile(xml, scope = nil) ⇒ Object
60 61 62 |
# File 'lib/ragtag.rb', line 60 def self.compile(xml, scope=nil) new(xml).compile(scope) end |
.const_missing(name) ⇒ Object
Access to project metadata as constants.
12 13 14 15 |
# File 'lib/ragtag/version.rb', line 12 def self.const_missing(name) key = name.to_s.downcase [key] || super(name) end |
.metadata ⇒ Object
Access to project metadata.
4 5 6 7 8 9 |
# File 'lib/ragtag/version.rb', line 4 def self. @metadata ||= ( require 'yaml' YAML.load(File.new(File.dirname(__FILE__) + '/../ragtag.yml')) ) end |
Instance Method Details
#compile(scope = nil) ⇒ Object
87 88 89 90 91 |
# File 'lib/ragtag.rb', line 87 def compile(scope=nil) scope = scope || $TOPLEVEL_BINDING parse(@xml.root, scope) xml end |
#parse(node, scope) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# 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 |
#parse_attributes(node, scope) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# 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 |
#parse_content(node, scope) ⇒ Object
160 161 162 163 164 |
# File 'lib/ragtag.rb', line 160 def parse_content(node, scope) value = node['content'] node.content = eval(value, scope) node.remove_attribute('content') end |
#parse_each(node, scope) ⇒ Object
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# 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 |
#parse_if(node, scope) ⇒ Object
193 194 195 196 197 198 199 200 201 202 |
# 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 |
#parse_nodeset(nodeset, scope) ⇒ Object
152 153 154 155 156 157 |
# File 'lib/ragtag.rb', line 152 def parse_nodeset(nodeset, scope) nodeset.each do |node| parse(node, scope) end nodeset end |
#parse_omit(node, scope) ⇒ Object
254 255 256 257 258 259 260 261 |
# 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 |
#parse_repeat(node, scope) ⇒ Object
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# 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 |
#parse_replace(node, scope) ⇒ Object
167 168 169 170 171 |
# File 'lib/ragtag.rb', line 167 def parse_replace(node, scope) value = node['replace'] node.before(eval(value, scope).to_s) node.unlink end |