feedtools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
5
+ # All rights reserved.
6
+
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #++
11
+
12
+ require 'builder/xmlmarkup'
13
+ require 'builder/xmlevents'
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright 2004 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
+ module Builder #:nodoc:
12
+
13
+ # BlankSlate provides an abstract base class with no predefined
14
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
15
+ # BlankSlate is useful as a base class when writing classes that
16
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
17
+ class BlankSlate #:nodoc:
18
+ class << self
19
+ def hide(name)
20
+ undef_method name if
21
+ instance_methods.include?(name.to_s) and
22
+ name !~ /^(__|instance_eval)/
23
+ end
24
+ end
25
+
26
+ instance_methods.each { |m| hide(m) }
27
+ end
28
+ end
29
+
30
+ # Since Ruby is very dynamic, methods added to the ancestors of
31
+ # BlankSlate <em>after BlankSlate is defined</em> will show up in the
32
+ # list of available BlankSlate methods. We handle this by defining a hook in the Object and Kernel classes that will hide any defined
33
+ module Kernel #:nodoc:
34
+ class << self
35
+ alias_method :blank_slate_method_added, :method_added
36
+ def method_added(name)
37
+ blank_slate_method_added(name)
38
+ return if self != Kernel
39
+ Builder::BlankSlate.hide(name)
40
+ end
41
+ end
42
+ end
43
+
44
+ class Object #:nodoc:
45
+ class << self
46
+ alias_method :blank_slate_method_added, :method_added
47
+ def method_added(name)
48
+ blank_slate_method_added(name)
49
+ return if self != Object
50
+ Builder::BlankSlate.hide(name)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'builder/blankslate'
4
+
5
+ module Builder #:nodoc:
6
+
7
+ # Generic error for builder
8
+ class IllegalBlockError < RuntimeError #:nodoc:
9
+ end
10
+
11
+ # XmlBase is a base class for building XML builders. See
12
+ # Builder::XmlMarkup and Builder::XmlEvents for examples.
13
+ class XmlBase < BlankSlate #:nodoc:
14
+
15
+ # Create an XML markup builder.
16
+ #
17
+ # out:: Object receiving the markup.1 +out+ must respond to
18
+ # <tt><<</tt>.
19
+ # indent:: Number of spaces used for indentation (0 implies no
20
+ # indentation and no line breaks).
21
+ # initial:: Level of initial indentation.
22
+ #
23
+ def initialize(indent=0, initial=0)
24
+ @indent = indent
25
+ @level = initial
26
+ end
27
+
28
+ # Create a tag named +sym+. Other than the first argument which
29
+ # is the tag name, the arguements are the same as the tags
30
+ # implemented via <tt>method_missing</tt>.
31
+ def tag!(sym, *args, &block)
32
+ self.__send__(sym, *args, &block)
33
+ end
34
+
35
+ # Create XML markup based on the name of the method. This method
36
+ # is never invoked directly, but is called for each markup method
37
+ # in the markup block.
38
+ def method_missing(sym, *args, &block)
39
+ text = nil
40
+ attrs = nil
41
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
42
+ args.each do |arg|
43
+ case arg
44
+ when Hash
45
+ attrs ||= {}
46
+ attrs.merge!(arg)
47
+ else
48
+ text ||= ''
49
+ text << arg.to_s
50
+ end
51
+ end
52
+ if block
53
+ unless text.nil?
54
+ raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
55
+ end
56
+ _capture_outer_self(block) if @self.nil?
57
+ _indent
58
+ _start_tag(sym, attrs)
59
+ _newline
60
+ _nested_structures(block)
61
+ _indent
62
+ _end_tag(sym)
63
+ _newline
64
+ elsif text.nil?
65
+ _indent
66
+ _start_tag(sym, attrs, true)
67
+ _newline
68
+ else
69
+ _indent
70
+ _start_tag(sym, attrs)
71
+ text! text
72
+ _end_tag(sym)
73
+ _newline
74
+ end
75
+ @target
76
+ end
77
+
78
+ # Append text to the output target. Escape any markup. May be
79
+ # used within the markup brackets as:
80
+ #
81
+ # builder.p { br; text! "HI" } #=> <p><br/>HI</p>
82
+ def text!(text)
83
+ _text(_escape(text))
84
+ end
85
+
86
+ # Append text to the output target without escaping any markup.
87
+ # May be used within the markup brackets as:
88
+ #
89
+ # builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
90
+ #
91
+ # This is useful when using non-builder enabled software that
92
+ # generates strings. Just insert the string directly into the
93
+ # builder without changing the inserted markup.
94
+ #
95
+ # It is also useful for stacking builder objects. Builders only
96
+ # use <tt><<</tt> to append to the target, so by supporting this
97
+ # method/operation builders can use other builders as their
98
+ # targets.
99
+ def <<(text)
100
+ _text(text)
101
+ end
102
+
103
+ # For some reason, nil? is sent to the XmlMarkup object. If nil?
104
+ # is not defined and method_missing is invoked, some strange kind
105
+ # of recursion happens. Since nil? won't ever be an XML tag, it
106
+ # is pretty safe to define it here. (Note: this is an example of
107
+ # cargo cult programming,
108
+ # cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
109
+ def nil?
110
+ false
111
+ end
112
+
113
+ private
114
+
115
+ def _escape(text)
116
+ text.
117
+ gsub(%r{&}, '&amp;').
118
+ gsub(%r{<}, '&lt;').
119
+ gsub(%r{>}, '&gt;')
120
+ end
121
+
122
+ def _capture_outer_self(block)
123
+ @self = eval("self", block)
124
+ end
125
+
126
+ def _newline
127
+ return if @indent == 0
128
+ text! "\n"
129
+ end
130
+
131
+ def _indent
132
+ return if @indent == 0 || @level == 0
133
+ text!(" " * (@level * @indent))
134
+ end
135
+
136
+ def _nested_structures(block)
137
+ @level += 1
138
+ block.call(self)
139
+ ensure
140
+ @level -= 1
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
5
+ # All rights reserved.
6
+
7
+ # Permission is granted for use, copying, modification, distribution,
8
+ # and distribution of modified versions of this work as long as the
9
+ # above copyright notice is included.
10
+ #++
11
+
12
+ require 'builder/xmlmarkup'
13
+
14
+ module Builder
15
+
16
+ # Create a series of SAX-like XML events (e.g. start_tag, end_tag)
17
+ # from the markup code. XmlEvent objects are used in a way similar
18
+ # to XmlMarkup objects, except that a series of events are generated
19
+ # and passed to a handler rather than generating character-based
20
+ # markup.
21
+ #
22
+ # Usage:
23
+ # xe = Builder::XmlEvents.new(hander)
24
+ # xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
25
+ #
26
+ # Indentation may also be selected by providing value for the
27
+ # indentation size and initial indentation level.
28
+ #
29
+ # xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
30
+ #
31
+ # == XML Event Handler
32
+ #
33
+ # The handler object must expect the following events.
34
+ #
35
+ # [<tt>start_tag(tag, attrs)</tt>]
36
+ # Announces that a new tag has been found. +tag+ is the name of
37
+ # the tag and +attrs+ is a hash of attributes for the tag.
38
+ #
39
+ # [<tt>end_tag(tag)</tt>]
40
+ # Announces that an end tag for +tag+ has been found.
41
+ #
42
+ # [<tt>text(text)</tt>]
43
+ # Announces that a string of characters (+text+) has been found.
44
+ # A series of characters may be broken up into more than one
45
+ # +text+ call, so the client cannot assume that a single
46
+ # callback contains all the text data.
47
+ #
48
+ class XmlEvents < XmlMarkup #:nodoc:
49
+ def text!(text)
50
+ @target.text(text)
51
+ end
52
+
53
+ def _start_tag(sym, attrs, end_too=false)
54
+ @target.start_tag(sym, attrs)
55
+ _end_tag(sym) if end_too
56
+ end
57
+
58
+ def _end_tag(sym)
59
+ @target.end_tag(sym)
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright 2004 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(:ident=>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
+ # :indent=><em>indentation</em>::
169
+ # Number of spaces used for indentation. The default is no
170
+ # indentation and no line breaks.
171
+ # :margin=><em>initial_indentation_level</em>::
172
+ # Amount of initial indentation (specified in levels, not
173
+ # spaces).
174
+ #
175
+ def initialize(options={})
176
+ indent = options[:indent] || 0
177
+ margin = options[:margin] || 0
178
+ super(indent, margin)
179
+ @target = options[:target] || ""
180
+ end
181
+
182
+ # Return the target of the builder.
183
+ def target!
184
+ @target
185
+ end
186
+
187
+ def comment!(comment_text)
188
+ _ensure_no_block block_given?
189
+ _special("<!-- ", " -->", comment_text, nil)
190
+ end
191
+
192
+ # Insert an XML declaration into the XML markup.
193
+ #
194
+ # For example:
195
+ #
196
+ # xml.declare! :ELEMENT, :blah, "yada"
197
+ # # => <!ELEMENT blah "yada">
198
+ def declare!(inst, *args, &block)
199
+ _indent
200
+ @target << "<!#{inst}"
201
+ args.each do |arg|
202
+ case arg
203
+ when String
204
+ @target << %{ "#{arg}"}
205
+ when Symbol
206
+ @target << " #{arg}"
207
+ end
208
+ end
209
+ if block_given?
210
+ @target << " ["
211
+ _newline
212
+ _nested_structures(block)
213
+ @target << "]"
214
+ end
215
+ @target << ">"
216
+ _newline
217
+ end
218
+
219
+ # Insert a processing instruction into the XML markup. E.g.
220
+ #
221
+ # For example:
222
+ #
223
+ # xml.instruct!
224
+ # #=> <?xml version="1.0" encoding="UTF-8"?>
225
+ # xml.instruct! :aaa, :bbb=>"ccc"
226
+ # #=> <?aaa bbb="ccc"?>
227
+ #
228
+ def instruct!(directive_tag=:xml, attrs={})
229
+ _ensure_no_block block_given?
230
+ if directive_tag == :xml
231
+ a = { :version=>"1.0", :encoding=>"UTF-8" }
232
+ attrs = a.merge attrs
233
+ end
234
+ _special(
235
+ "<?#{directive_tag}",
236
+ "?>",
237
+ nil,
238
+ attrs,
239
+ [:version, :encoding, :standalone])
240
+ end
241
+
242
+ private
243
+
244
+ # NOTE: All private methods of a builder object are prefixed when
245
+ # a "_" character to avoid possible conflict with XML tag names.
246
+
247
+ # Insert text directly in to the builder's target.
248
+ def _text(text)
249
+ @target << text
250
+ end
251
+
252
+ # Insert special instruction.
253
+ def _special(open, close, data=nil, attrs=nil, order=[])
254
+ _indent
255
+ @target << open
256
+ @target << data if data
257
+ _insert_attributes(attrs, order) if attrs
258
+ @target << close
259
+ _newline
260
+ end
261
+
262
+ # Start an XML tag. If <tt>end_too</tt> is true, then the start
263
+ # tag is also the end tag (e.g. <br/>
264
+ def _start_tag(sym, attrs, end_too=false)
265
+ @target << "<#{sym}"
266
+ _insert_attributes(attrs)
267
+ @target << "/" if end_too
268
+ @target << ">"
269
+ end
270
+
271
+ # Insert an ending tag.
272
+ def _end_tag(sym)
273
+ @target << "</#{sym}>"
274
+ end
275
+
276
+ # Insert the attributes (given in the hash).
277
+ def _insert_attributes(attrs, order=[])
278
+ return if attrs.nil?
279
+ order.each do |k|
280
+ v = attrs[k]
281
+ @target << %{ #{k}="#{v}"} if v
282
+ end
283
+ attrs.each do |k, v|
284
+ @target << %{ #{k}="#{v}"} unless order.member?(k)
285
+ end
286
+ end
287
+
288
+ def _ensure_no_block(got_block)
289
+ if got_block
290
+ fail IllegalBlockError,
291
+ "Blocks are not allowed on XML instructions"
292
+ end
293
+ end
294
+
295
+ end
296
+
297
+ end