Froze rails gems
[depot.git] / vendor / rails / activesupport / lib / active_support / vendor / builder-2.1.2 / builder / xmlmarkup.rb
1 #!/usr/bin/env ruby
2 #--
3 # Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
4 # All rights reserved.
5
6 # Permission is granted for use, copying, modification, distribution,
7 # and distribution of modified versions of this work as long as the
8 # above copyright notice is included.
9 #++
10
11 # Provide a flexible and easy to use Builder for creating XML markup.
12 # See XmlBuilder for usage details.
13
14 require 'builder/xmlbase'
15
16 module Builder
17
18 # Create XML markup easily. All (well, almost all) methods sent to
19 # an XmlMarkup object will be translated to the equivalent XML
20 # markup. Any method with a block will be treated as an XML markup
21 # tag with nested markup in the block.
22 #
23 # Examples will demonstrate this easier than words. In the
24 # following, +xm+ is an +XmlMarkup+ object.
25 #
26 # xm.em("emphasized") # => <em>emphasized</em>
27 # xm.em { xmm.b("emp & bold") } # => <em><b>emph &amp; bold</b></em>
28 # xm.a("A Link", "href"=>"http://onestepback.org")
29 # # => <a href="http://onestepback.org">A Link</a>
30 # xm.div { br } # => <div><br/></div>
31 # xm.target("name"=>"compile", "option"=>"fast")
32 # # => <target option="fast" name="compile"\>
33 # # NOTE: order of attributes is not specified.
34 #
35 # xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
36 # xm.html { # <html>
37 # xm.head { # <head>
38 # xm.title("History") # <title>History</title>
39 # } # </head>
40 # xm.body { # <body>
41 # xm.comment! "HI" # <! -- HI -->
42 # xm.h1("Header") # <h1>Header</h1>
43 # xm.p("paragraph") # <p>paragraph</p>
44 # } # </body>
45 # } # </html>
46 #
47 # == Notes:
48 #
49 # * The order that attributes are inserted in markup tags is
50 # undefined.
51 #
52 # * Sometimes you wish to insert text without enclosing tags. Use
53 # the <tt>text!</tt> method to accomplish this.
54 #
55 # Example:
56 #
57 # xm.div { # <div>
58 # xm.text! "line"; xm.br # line<br/>
59 # xm.text! "another line"; xmbr # another line<br/>
60 # } # </div>
61 #
62 # * The special XML characters <, >, and & are converted to &lt;,
63 # &gt; and &amp; automatically. Use the <tt><<</tt> operation to
64 # insert text without modification.
65 #
66 # * Sometimes tags use special characters not allowed in ruby
67 # identifiers. Use the <tt>tag!</tt> method to handle these
68 # cases.
69 #
70 # Example:
71 #
72 # xml.tag!("SOAP:Envelope") { ... }
73 #
74 # will produce ...
75 #
76 # <SOAP:Envelope> ... </SOAP:Envelope>"
77 #
78 # <tt>tag!</tt> will also take text and attribute arguments (after
79 # the tag name) like normal markup methods. (But see the next
80 # bullet item for a better way to handle XML namespaces).
81 #
82 # * Direct support for XML namespaces is now available. If the
83 # first argument to a tag call is a symbol, it will be joined to
84 # the tag to produce a namespace:tag combination. It is easier to
85 # show this than describe it.
86 #
87 # xml.SOAP :Envelope do ... end
88 #
89 # Just put a space before the colon in a namespace to produce the
90 # right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
91 # "<tt>xml.SOAP :Envelope</tt>")
92 #
93 # * XmlMarkup builds the markup in any object (called a _target_)
94 # that accepts the <tt><<</tt> method. If no target is given,
95 # then XmlMarkup defaults to a string target.
96 #
97 # Examples:
98 #
99 # xm = Builder::XmlMarkup.new
100 # result = xm.title("yada")
101 # # result is a string containing the markup.
102 #
103 # buffer = ""
104 # xm = Builder::XmlMarkup.new(buffer)
105 # # The markup is appended to buffer (using <<)
106 #
107 # xm = Builder::XmlMarkup.new(STDOUT)
108 # # The markup is written to STDOUT (using <<)
109 #
110 # xm = Builder::XmlMarkup.new
111 # x2 = Builder::XmlMarkup.new(:target=>xm)
112 # # Markup written to +x2+ will be send to +xm+.
113 #
114 # * Indentation is enabled by providing the number of spaces to
115 # indent for each level as a second argument to XmlBuilder.new.
116 # Initial indentation may be specified using a third parameter.
117 #
118 # Example:
119 #
120 # xm = Builder.new(:indent=>2)
121 # # xm will produce nicely formatted and indented XML.
122 #
123 # xm = Builder.new(:indent=>2, :margin=>4)
124 # # xm will produce nicely formatted and indented XML with 2
125 # # spaces per indent and an over all indentation level of 4.
126 #
127 # builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
128 # builder.name { |b| b.first("Jim"); b.last("Weirich) }
129 # # prints:
130 # # <name>
131 # # <first>Jim</first>
132 # # <last>Weirich</last>
133 # # </name>
134 #
135 # * The instance_eval implementation which forces self to refer to
136 # the message receiver as self is now obsolete. We now use normal
137 # block calls to execute the markup block. This means that all
138 # markup methods must now be explicitly send to the xml builder.
139 # For instance, instead of
140 #
141 # xml.div { strong("text") }
142 #
143 # you need to write:
144 #
145 # xml.div { xml.strong("text") }
146 #
147 # Although more verbose, the subtle change in semantics within the
148 # block was found to be prone to error. To make this change a
149 # little less cumbersome, the markup block now gets the markup
150 # object sent as an argument, allowing you to use a shorter alias
151 # within the block.
152 #
153 # For example:
154 #
155 # xml_builder = Builder::XmlMarkup.new
156 # xml_builder.div { |xml|
157 # xml.stong("text")
158 # }
159 #
160 class XmlMarkup < XmlBase
161
162 # Create an XML markup builder. Parameters are specified by an
163 # option hash.
164 #
165 # :target=><em>target_object</em>::
166 # Object receiving the markup. +out+ must respond to the
167 # <tt><<</tt> operator. The default is a plain string target.
168 #
169 # :indent=><em>indentation</em>::
170 # Number of spaces used for indentation. The default is no
171 # indentation and no line breaks.
172 #
173 # :margin=><em>initial_indentation_level</em>::
174 # Amount of initial indentation (specified in levels, not
175 # spaces).
176 #
177 # :escape_attrs=><b>OBSOLETE</em>::
178 # The :escape_attrs option is no longer supported by builder
179 # (and will be quietly ignored). String attribute values are
180 # now automatically escaped. If you need unescaped attribute
181 # values (perhaps you are using entities in the attribute
182 # values), then give the value as a Symbol. This allows much
183 # finer control over escaping attribute values.
184 #
185 def initialize(options={})
186 indent = options[:indent] || 0
187 margin = options[:margin] || 0
188 super(indent, margin)
189 @target = options[:target] || ""
190 end
191
192 # Return the target of the builder.
193 def target!
194 @target
195 end
196
197 def comment!(comment_text)
198 _ensure_no_block block_given?
199 _special("<!-- ", " -->", comment_text, nil)
200 end
201
202 # Insert an XML declaration into the XML markup.
203 #
204 # For example:
205 #
206 # xml.declare! :ELEMENT, :blah, "yada"
207 # # => <!ELEMENT blah "yada">
208 def declare!(inst, *args, &block)
209 _indent
210 @target << "<!#{inst}"
211 args.each do |arg|
212 case arg
213 when String
214 @target << %{ "#{arg}"} # " WART
215 when Symbol
216 @target << " #{arg}"
217 end
218 end
219 if block_given?
220 @target << " ["
221 _newline
222 _nested_structures(block)
223 @target << "]"
224 end
225 @target << ">"
226 _newline
227 end
228
229 # Insert a processing instruction into the XML markup. E.g.
230 #
231 # For example:
232 #
233 # xml.instruct!
234 # #=> <?xml version="1.0" encoding="UTF-8"?>
235 # xml.instruct! :aaa, :bbb=>"ccc"
236 # #=> <?aaa bbb="ccc"?>
237 #
238 def instruct!(directive_tag=:xml, attrs={})
239 _ensure_no_block block_given?
240 if directive_tag == :xml
241 a = { :version=>"1.0", :encoding=>"UTF-8" }
242 attrs = a.merge attrs
243 end
244 _special(
245 "<?#{directive_tag}",
246 "?>",
247 nil,
248 attrs,
249 [:version, :encoding, :standalone])
250 end
251
252 # Insert a CDATA section into the XML markup.
253 #
254 # For example:
255 #
256 # xml.cdata!("text to be included in cdata")
257 # #=> <![CDATA[text to be included in cdata]]>
258 #
259 def cdata!(text)
260 _ensure_no_block block_given?
261 _special("<![CDATA[", "]]>", text, nil)
262 end
263
264 private
265
266 # NOTE: All private methods of a builder object are prefixed when
267 # a "_" character to avoid possible conflict with XML tag names.
268
269 # Insert text directly in to the builder's target.
270 def _text(text)
271 @target << text
272 end
273
274 # Insert special instruction.
275 def _special(open, close, data=nil, attrs=nil, order=[])
276 _indent
277 @target << open
278 @target << data if data
279 _insert_attributes(attrs, order) if attrs
280 @target << close
281 _newline
282 end
283
284 # Start an XML tag. If <tt>end_too</tt> is true, then the start
285 # tag is also the end tag (e.g. <br/>
286 def _start_tag(sym, attrs, end_too=false)
287 @target << "<#{sym}"
288 _insert_attributes(attrs)
289 @target << "/" if end_too
290 @target << ">"
291 end
292
293 # Insert an ending tag.
294 def _end_tag(sym)
295 @target << "</#{sym}>"
296 end
297
298 # Insert the attributes (given in the hash).
299 def _insert_attributes(attrs, order=[])
300 return if attrs.nil?
301 order.each do |k|
302 v = attrs[k]
303 @target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
304 end
305 attrs.each do |k, v|
306 @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
307 end
308 end
309
310 def _attr_value(value)
311 case value
312 when Symbol
313 value.to_s
314 else
315 _escape_quote(value.to_s)
316 end
317 end
318
319 def _ensure_no_block(got_block)
320 if got_block
321 fail IllegalBlockError,
322 "Blocks are not allowed on XML instructions"
323 end
324 end
325
326 end
327
328 end