Froze rails gems
[depot.git] / vendor / rails / activesupport / lib / active_support / vendor / builder-2.1.2 / builder / xmlbase.rb
1 #!/usr/bin/env ruby
2
3 require 'builder/blankslate'
4
5 module Builder
6
7 # Generic error for builder
8 class IllegalBlockError < RuntimeError; end
9
10 # XmlBase is a base class for building XML builders. See
11 # Builder::XmlMarkup and Builder::XmlEvents for examples.
12 class XmlBase < BlankSlate
13
14 # Create an XML markup builder.
15 #
16 # out:: Object receiving the markup. +out+ must respond to
17 # <tt><<</tt>.
18 # indent:: Number of spaces used for indentation (0 implies no
19 # indentation and no line breaks).
20 # initial:: Level of initial indentation.
21 #
22 def initialize(indent=0, initial=0)
23 @indent = indent
24 @level = initial
25 end
26
27 # Create a tag named +sym+. Other than the first argument which
28 # is the tag name, the arguments are the same as the tags
29 # implemented via <tt>method_missing</tt>.
30 def tag!(sym, *args, &block)
31 method_missing(sym.to_sym, *args, &block)
32 end
33
34 # Create XML markup based on the name of the method. This method
35 # is never invoked directly, but is called for each markup method
36 # in the markup block.
37 def method_missing(sym, *args, &block)
38 text = nil
39 attrs = nil
40 sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
41 args.each do |arg|
42 case arg
43 when Hash
44 attrs ||= {}
45 attrs.merge!(arg)
46 else
47 text ||= ''
48 text << arg.to_s
49 end
50 end
51 if block
52 unless text.nil?
53 raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
54 end
55 _indent
56 _start_tag(sym, attrs)
57 _newline
58 _nested_structures(block)
59 _indent
60 _end_tag(sym)
61 _newline
62 elsif text.nil?
63 _indent
64 _start_tag(sym, attrs, true)
65 _newline
66 else
67 _indent
68 _start_tag(sym, attrs)
69 text! text
70 _end_tag(sym)
71 _newline
72 end
73 @target
74 end
75
76 # Append text to the output target. Escape any markup. May be
77 # used within the markup brackets as:
78 #
79 # builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
80 def text!(text)
81 _text(_escape(text))
82 end
83
84 # Append text to the output target without escaping any markup.
85 # May be used within the markup brackets as:
86 #
87 # builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
88 #
89 # This is useful when using non-builder enabled software that
90 # generates strings. Just insert the string directly into the
91 # builder without changing the inserted markup.
92 #
93 # It is also useful for stacking builder objects. Builders only
94 # use <tt><<</tt> to append to the target, so by supporting this
95 # method/operation builders can use other builders as their
96 # targets.
97 def <<(text)
98 _text(text)
99 end
100
101 # For some reason, nil? is sent to the XmlMarkup object. If nil?
102 # is not defined and method_missing is invoked, some strange kind
103 # of recursion happens. Since nil? won't ever be an XML tag, it
104 # is pretty safe to define it here. (Note: this is an example of
105 # cargo cult programming,
106 # cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
107 def nil?
108 false
109 end
110
111 private
112
113 require 'builder/xchar'
114 def _escape(text)
115 text.to_xs
116 end
117
118 def _escape_quote(text)
119 _escape(text).gsub(%r{"}, '&quot;') # " WART
120 end
121
122 def _newline
123 return if @indent == 0
124 text! "\n"
125 end
126
127 def _indent
128 return if @indent == 0 || @level == 0
129 text!(" " * (@level * @indent))
130 end
131
132 def _nested_structures(block)
133 @level += 1
134 block.call(self)
135 ensure
136 @level -= 1
137 end
138 end
139 end