builder 1.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of builder might be problematic. Click here for more details.

data/README ADDED
@@ -0,0 +1,79 @@
1
+ = Project: Builder
2
+
3
+ = Goal
4
+
5
+ Provide a simple way to create XML markup and data structures.
6
+
7
+ = Classes
8
+
9
+ Builder::XmlMarkup:: Generate XML markup notiation
10
+ Builder::XmlTree:: Generate an XML tree (i.e. DOM-like) structure
11
+ (not yet implemented).
12
+ Builder::XmlEvents:: Generate XML events (i.e. SAX-like)
13
+
14
+ = Usage
15
+
16
+ require 'rubygems'
17
+ require_gem 'builder', '>= 1.0"
18
+
19
+ builder = Builder::XmlMarkup.new
20
+ xml = builder.person { |b| b.name("Jim"); b.phone("555-1234") }
21
+ xml #=> <person><name>Jim</name><phone>555-1234</phone></person>
22
+
23
+ or
24
+
25
+ require 'rubygems'
26
+ require_gem 'builder'
27
+
28
+ builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
29
+ builder.person { |b| b.name("Jim"); b.phone("555-1234") }
30
+ #
31
+ # Prints:
32
+ # <person>
33
+ # <name>Jim</name>
34
+ # <phone>555-1234</phone>
35
+ # </person
36
+
37
+ = Compatibility
38
+
39
+ Version 1.0.0 introduces some changes that are not backwards
40
+ compatible with earlier releases of builder. The main areas of
41
+ incompatibility are:
42
+
43
+ * Keyword based arguments to +new+ (rather than positional based). It
44
+ was found that a developer would often like to specify indentation
45
+ without providing an explicit target, or specify a target without
46
+ indentation. Keyword based arguments handle this situation nicely.
47
+
48
+ * Builder must now be an explicit target for markup tags. Instead of
49
+ writing
50
+
51
+ xml_markup = Builder::XmlMarkup.new
52
+ xml_markup.div { strong("text") }
53
+
54
+ you need to write
55
+
56
+ xml_markup = Builder::XmlMarkup.new
57
+ xml_markup.div { xml_markup.strong("text") }
58
+
59
+ * The builder object is passed as a parameter to all nested markup
60
+ blocks. This allows you to create a short alias for the builder
61
+ object that can be used within the block. For example, the previous
62
+ example can be written as:
63
+
64
+ xml_markup = Builder::XmlMarkup.new
65
+ xml_markup.div { |xml| xml.strong("text") }
66
+
67
+ * If you have both a pre-1.0 and a post-1.0 gem of builder installed,
68
+ you can choose which version to use through the RubyGems
69
+ +require_gem+ facility.
70
+
71
+ require_gem 'builder', "~> 0.0" # Gets the old version
72
+ require_gem 'builder', "~> 1.0" # Gets the new version
73
+
74
+ = Contact
75
+
76
+ Author:: Jim Weirich
77
+ Email:: jim@weirichhouse.org
78
+ Home Page:: http://onestepback.org
79
+ License:: Open Source
@@ -0,0 +1,86 @@
1
+ # Rakefile for rake -*- 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
+ require 'rake/clean'
11
+ require 'rake/testtask'
12
+ require 'rake/rdoctask'
13
+ begin
14
+ require 'rubygems'
15
+ require 'rake/gempackagetask'
16
+ rescue Exception
17
+ nil
18
+ end
19
+
20
+ PKG_VERSION = "1.1.0"
21
+
22
+ SRC_RB = FileList['lib/**/*.rb']
23
+
24
+ # The default task is run if rake is given no explicit arguments.
25
+
26
+ desc "Default Task"
27
+ task :default => :test
28
+
29
+ # Test Tasks ---------------------------------------------------------
30
+
31
+ Rake::TestTask.new do |t|
32
+ t.test_files = FileList['test/test*.rb']
33
+ t.verbose = true
34
+ end
35
+
36
+ # Create a task to build the RDOC documentation tree.
37
+
38
+ rd = Rake::RDocTask.new("rdoc") { |rdoc|
39
+ rdoc.rdoc_dir = 'html'
40
+ rdoc.title = "Builder for Markup"
41
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
42
+ rdoc.rdoc_files.include('lib/**/*.rb', '[A-Z]*')
43
+ }
44
+
45
+ # ====================================================================
46
+ # Create a task that will package the Rake software into distributable
47
+ # gem files.
48
+
49
+ PKG_FILES = FileList[
50
+ 'lib/**/*.rb',
51
+ 'test/**/*.rb',
52
+ ]
53
+
54
+ if ! defined?(Gem)
55
+ puts "Package Target requires RubyGEMs"
56
+ else
57
+ spec = Gem::Specification.new do |s|
58
+
59
+ #### Basic information.
60
+
61
+ s.name = 'builder'
62
+ s.version = PKG_VERSION
63
+ s.summary = "Builders for MarkUp."
64
+ s.description = %{Simple builder classes for creating markup.}
65
+
66
+ s.files = PKG_FILES.to_a
67
+
68
+ s.require_path = 'lib'
69
+ s.autorequire = 'builder'
70
+
71
+ s.test_files = Dir['test/test*.rb']
72
+
73
+ s.has_rdoc = true
74
+ s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
75
+ s.rdoc_options <<
76
+ '--title' << 'Builder -- Easy XML Building' <<
77
+ '--main' << 'README' <<
78
+ '--line-numbers'
79
+
80
+ s.author = "Jim Weirich"
81
+ s.email = "jim@weirichhouse.org"
82
+ s.homepage = "http://onestepback.org"
83
+ end
84
+
85
+ Rake::GemPackageTask.new(spec) do end
86
+ end
@@ -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,21 @@
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
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
18
+ instance_methods.each { |m| undef_method m unless m =~ /^(__|instance_eval)/ }
19
+ end
20
+
21
+ end
@@ -0,0 +1,139 @@
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 arguements are the same as the tags
29
+ # implemented via <tt>method_missing</tt>.
30
+ def tag!(sym, *args, &block)
31
+ self.__send__(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
+ args.each do |arg|
41
+ case arg
42
+ when Hash
43
+ attrs ||= {}
44
+ attrs.merge!(arg)
45
+ else
46
+ text ||= ''
47
+ text << arg.to_s
48
+ end
49
+ end
50
+ if block
51
+ unless text.nil?
52
+ raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
53
+ end
54
+ _capture_outer_self(block) if @self.nil?
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 brakets as:
78
+ #
79
+ # builder.p { br; 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 brakets 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 oother 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.
105
+ def nil?
106
+ false
107
+ end
108
+
109
+ private
110
+
111
+ def _escape(text)
112
+ text.
113
+ gsub(%r{&}, '&amp;').
114
+ gsub(%r{<}, '&lt;').
115
+ gsub(%r{>}, '&gt;')
116
+ end
117
+
118
+ def _capture_outer_self(block)
119
+ @self = eval("self", block)
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
@@ -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
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,260 @@
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.
80
+ #
81
+ # * XmlMarkup builds the markup in any object (called a _target_)
82
+ # that accepts the <tt><<</tt> method. If no target is given,
83
+ # then XmlMarkup defaults to a string target.
84
+ #
85
+ # Examples:
86
+ #
87
+ # xm = Builder::XmlMarkup.new
88
+ # result = xm.title("yada")
89
+ # # result is a string containing the markup.
90
+ #
91
+ # buffer = ""
92
+ # xm = Builder::XmlMarkup.new(buffer)
93
+ # # The markup is appended to buffer (using <<)
94
+ #
95
+ # xm = Builder::XmlMarkup.new(STDOUT)
96
+ # # The markup is written to STDOUT (using <<)
97
+ #
98
+ # xm = Builder::XmlMarkup.new
99
+ # x2 = Builder::XmlMarkup.new(:target=>xm)
100
+ # # Markup written to +x2+ will be send to +xm+.
101
+ #
102
+ # * Indentation is enabled by providing the number of spaces to
103
+ # indent for each level as a second argument to XmlBuilder.new.
104
+ # Initial indentation may be specified using a third parameter.
105
+ #
106
+ # Example:
107
+ #
108
+ # xm = Builder.new(:ident=>2)
109
+ # # xm will produce nicely formatted and indented XML.
110
+ #
111
+ # xm = Builder.new(:indent=>2, :margin=>4)
112
+ # # xm will produce nicely formatted and indented XML with 2
113
+ # # spaces per indent and an over all indentation level of 4.
114
+ #
115
+ # builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
116
+ # builder.name { |b| b.first("Jim"); b.last("Weirich) }
117
+ # # prints:
118
+ # # <name>
119
+ # # <first>Jim</first>
120
+ # # <last>Weirich</last>
121
+ # # </name>
122
+ #
123
+ # * The instance_eval implementation which forces self to refer to
124
+ # the message receiver as self is now obsolete. We now use normal
125
+ # block calls to execute the markup block. This means that all
126
+ # markup methods must now be explicitly send to the xml builder.
127
+ # For instance, instead of
128
+ #
129
+ # xml.div { strong("text") }
130
+ #
131
+ # you need to write:
132
+ #
133
+ # xml.div { xml.strong("text") }
134
+ #
135
+ # Although more verbose, the subtle change in semantics within the
136
+ # block was found to be prone to error. To make this change a
137
+ # little less cumbersome, the markup block now gets the markup
138
+ # object sent as an argument, allowing you to use a shorter alias
139
+ # within the block.
140
+ #
141
+ # For example:
142
+ #
143
+ # xml_builder = Builder::XmlMarkup.new
144
+ # xml_builder.div { |xml|
145
+ # xml.stong("text")
146
+ # }
147
+ #
148
+ class XmlMarkup < XmlBase
149
+
150
+ # Create an XML markup builder. Parameters are specified by an
151
+ # option hash.
152
+ #
153
+ # :target=><em>target_object</em>::
154
+ # Object receiving the markup. +out+ must respond to the
155
+ # <tt><<</tt> operator. The default is a plain string target.
156
+ # :indent=><em>indentation</em>::
157
+ # Number of spaces used for indentation. The default is no
158
+ # indentation and no line breaks.
159
+ # :margin=><em>initial_indentation_level</em>::
160
+ # Amount of initial indentation (specified in levels, not
161
+ # spaces).
162
+ #
163
+ def initialize(options={})
164
+ indent = options[:indent] || 0
165
+ margin = options[:margin] || 0
166
+ super(indent, margin)
167
+ @target = options[:target] || ""
168
+ end
169
+
170
+ # Return the target of the builder.
171
+ def target!
172
+ @target
173
+ end
174
+
175
+ def comment!(comment_text)
176
+ _ensure_no_block block_given?
177
+ _special("<!-- ", " -->", comment_text, nil)
178
+ end
179
+
180
+ # Insert an XML declaration into the XML markup.
181
+ #
182
+ # For example:
183
+ #
184
+ # xml.declare! :element, :blah=>"yada"
185
+ # # => <?xml blah="yada"?>
186
+ def declare!(inst, attrs={})
187
+ _ensure_no_block block_given?
188
+ _special("<!#{inst}", ">", nil, attrs)
189
+ end
190
+
191
+ # Insert a processing instruction into the XML markup. E.g.
192
+ #
193
+ # For example:
194
+ #
195
+ # xml.instruct!
196
+ # #=> <?xml encoding="UTF-8" version="1.0"?>
197
+ # xml.instruct! :aaa, :bbb=>"ccc"
198
+ # #=> <?aaa bbb="ccc"?>
199
+ #
200
+ def instruct!(directive_tag=:xml, attrs={})
201
+ _ensure_no_block block_given?
202
+ if directive_tag == :xml
203
+ a = { :version=>"1.0", :encoding=>"UTF-8" }
204
+ attrs = a.merge attrs
205
+ end
206
+ _special("<?#{directive_tag}", "?>", nil, attrs)
207
+ end
208
+
209
+ private
210
+
211
+ # NOTE: All private methods of a builder object are prefixed when
212
+ # a "_" character to avoid possible conflict with XML tag names.
213
+
214
+ # Insert text directly in to the builder's target.
215
+ def _text(text)
216
+ @target << text
217
+ end
218
+
219
+ # Insert special instruction.
220
+ def _special(open, close, data=nil, attrs=nil)
221
+ _indent
222
+ @target << open
223
+ @target << data if data
224
+ _insert_attributes(attrs) if attrs
225
+ @target << close
226
+ _newline
227
+ end
228
+
229
+ # Start an XML tag. If <tt>end_too</tt> is true, then the start
230
+ # tag is also the end tag (e.g. <br/>
231
+ def _start_tag(sym, attrs, end_too=false)
232
+ @target << "<#{sym}"
233
+ _insert_attributes(attrs)
234
+ @target << "/" if end_too
235
+ @target << ">"
236
+ end
237
+
238
+ # Insert an ending tag.
239
+ def _end_tag(sym)
240
+ @target << "</#{sym}>"
241
+ end
242
+
243
+ # Insert the attributes (given in the hash).
244
+ def _insert_attributes(attrs)
245
+ return if attrs.nil?
246
+ attrs.each do |k, v|
247
+ @target << %{ #{_escape(k.to_s)}="#{_escape(v.to_s)}"}
248
+ end
249
+ end
250
+
251
+ def _ensure_no_block(got_block)
252
+ if got_block
253
+ fail IllegalBlockError,
254
+ "Blocks are not allowed on XML instructions"
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ end
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'builder'
5
+ require 'builder/xmlmarkup'
6
+
7
+ class TestMarkup < Test::Unit::TestCase
8
+ def setup
9
+ @xm = Builder::XmlMarkup.new
10
+ end
11
+
12
+ def test_create
13
+ assert_not_nil @xm
14
+ end
15
+
16
+ def test_simple
17
+ @xm.simple
18
+ assert_equal "<simple/>", @xm.target!
19
+ end
20
+
21
+ def test_value
22
+ @xm.value("hi")
23
+ assert_equal "<value>hi</value>", @xm.target!
24
+ end
25
+
26
+ def test_nested
27
+ @xm.outer { |x| x.inner("x") }
28
+ assert_equal "<outer><inner>x</inner></outer>", @xm.target!
29
+ end
30
+
31
+ def test_attributes
32
+ @xm.ref(:id => 12)
33
+ assert_equal %{<ref id="12"/>}, @xm.target!
34
+ end
35
+
36
+ def test_multiple_attributes
37
+ @xm.ref(:id => 12, :name => "bill")
38
+ assert_match %r{^<ref( id="12"| name="bill"){2}/>$}, @xm.target!
39
+ end
40
+
41
+ def test_attributes_with_text
42
+ @xm.a("link", :href=>"http://onestepback.org")
43
+ assert_equal %{<a href="http://onestepback.org">link</a>}, @xm.target!
44
+ end
45
+
46
+ def test_complex
47
+ @xm.body(:bg=>"#ffffff") { |x|
48
+ x.title("T", :style=>"red")
49
+ }
50
+ assert_equal %{<body bg="#ffffff"><title style="red">T</title></body>}, @xm.target!
51
+ end
52
+
53
+ def test_funky_symbol
54
+ @xm.tag!("non-ruby-token", :id=>1) { |x| x.ok }
55
+ assert_equal %{<non-ruby-token id="1"><ok/></non-ruby-token>}, @xm.target!
56
+ end
57
+
58
+ def test_no_explicit_marker
59
+ @xm.p { |x| x.b("HI") }
60
+ assert_equal "<p><b>HI</b></p>", @xm.target!
61
+ end
62
+
63
+ def test_reference_local_vars
64
+ n = 3
65
+ @xm.ol { |x| n.times { x.li(n) } }
66
+ assert_equal "<ol><li>3</li><li>3</li><li>3</li></ol>", @xm.target!
67
+ end
68
+
69
+ def test_reference_methods
70
+ @xm.title { |x| x.a { x.b(name) } }
71
+ assert_equal "<title><a><b>bob</b></a></title>", @xm.target!
72
+ end
73
+
74
+ def test_append_text
75
+ @xm.p { |x| x.br; x.text! "HI" }
76
+ assert_equal "<p><br/>HI</p>", @xm.target!
77
+ end
78
+
79
+ def test_ambiguous_markup
80
+ ex = assert_raises(ArgumentError) {
81
+ @xm.h1("data1") { b }
82
+ }
83
+ assert_match /\btext\b/, ex.message
84
+ assert_match /\bblock\b/, ex.message
85
+ end
86
+
87
+ def test_capitalized_method
88
+ @xm.P { |x| x.B("hi"); x.BR(); x.EM { x.text! "world" } }
89
+ assert_equal "<P><B>hi</B><BR/><EM>world</EM></P>", @xm.target!
90
+ end
91
+
92
+ def test_escaping
93
+ @xm.div("H&R"=>"<BLOCK>") { |x| x.text! "<hi>"; x.em("H&R Block") }
94
+ assert_equal %{<div H&amp;R="&lt;BLOCK&gt;">&lt;hi&gt;<em>H&amp;R Block</em></div>}, @xm.target!
95
+ end
96
+
97
+ def test_non_escaping
98
+ @xm.div("H&R"=>"<BLOCK>") { |x| x << "<h&i>"; x.em("H&R Block") }
99
+ assert_equal %{<div H&amp;R="&lt;BLOCK&gt;"><h&i><em>H&amp;R Block</em></div>}, @xm.target!
100
+ end
101
+
102
+ def test_return_value
103
+ str = @xm.x("men")
104
+ assert_equal @xm.target!, str
105
+ end
106
+
107
+ def test_stacked_builders
108
+ b = Builder::XmlMarkup.new( :target => @xm )
109
+ b.div { @xm.span { @xm.a("text", :href=>"ref") } }
110
+ assert_equal "<div><span><a href=\"ref\">text</a></span></div>", @xm.target!
111
+ end
112
+
113
+ def name
114
+ "bob"
115
+ end
116
+ end
117
+
118
+ class TestSpecialMarkup < Test::Unit::TestCase
119
+ def setup
120
+ @xm = Builder::XmlMarkup.new(:indent=>2)
121
+ end
122
+
123
+ def test_comment
124
+ @xm.comment!("COMMENT")
125
+ assert_equal "<!-- COMMENT -->\n", @xm.target!
126
+ end
127
+
128
+ def test_indented_comment
129
+ @xm.p { @xm.comment! "OK" }
130
+ assert_equal "<p>\n <!-- OK -->\n</p>\n", @xm.target!
131
+ end
132
+
133
+ def test_declare
134
+ @xm.declare! :element, :x=>"AB"
135
+ assert_equal "<!element x=\"AB\">\n", @xm.target!
136
+ end
137
+
138
+ def test_indented_declare
139
+ @xm.p { @xm.declare! :element, :x=>"y" }
140
+ assert_equal "<p>\n <!element x=\"y\">\n</p>\n", @xm.target!
141
+ end
142
+
143
+ def test_instruct
144
+ @xm.instruct! :abc, :version=>"0.9"
145
+ assert_equal "<?abc version=\"0.9\"?>\n", @xm.target!
146
+ end
147
+
148
+ def test_indented_instruct
149
+ @xm.p { @xm.instruct! :xml }
150
+ assert_match %r{<p>\n <\?xml( version="1.0"| encoding="UTF-8"){2}\?>\n</p>\n},
151
+ @xm.target!
152
+ end
153
+
154
+ def test_instruct_without_attributes
155
+ @xm.instruct! :zz
156
+ assert_equal "<?zz?>\n", @xm.target!
157
+ end
158
+
159
+ def test_xml_instruct
160
+ @xm.instruct!
161
+ assert_match /^<\?xml( version="1.0"| encoding="UTF-8"){2}\?>$/, @xm.target!
162
+ end
163
+
164
+ def test_xml_instruct_with_overrides
165
+ @xm.instruct! :xml, :encoding=>"UCS-2"
166
+ assert_match /^<\?xml( version="1.0"| encoding="UCS-2"){2}\?>$/, @xm.target!
167
+ end
168
+
169
+ def test_no_blocks
170
+ assert_raises(Builder::IllegalBlockError) do
171
+ @xm.instruct! { |x| x.hi }
172
+ end
173
+ assert_raises(Builder::IllegalBlockError) do
174
+ @xm.declare!(:element) { |x| x.hi }
175
+ end
176
+ assert_raises(Builder::IllegalBlockError) do
177
+ @xm.comment!(:element) { |x| x.hi }
178
+ end
179
+ end
180
+ end
181
+
182
+ class TestIndentedXmlMarkup < Test::Unit::TestCase
183
+ def setup
184
+ @xm = Builder::XmlMarkup.new(:indent=>2)
185
+ end
186
+
187
+ def test_one_level
188
+ @xm.ol { |x| x.li "text" }
189
+ assert_equal "<ol>\n <li>text</li>\n</ol>\n", @xm.target!
190
+ end
191
+
192
+ def test_two_levels
193
+ @xm.p { |x|
194
+ x.ol { x.li "text" }
195
+ x.br
196
+ }
197
+ assert_equal "<p>\n <ol>\n <li>text</li>\n </ol>\n <br/>\n</p>\n", @xm.target!
198
+ end
199
+
200
+ def test_initial_level
201
+ @xm = Builder::XmlMarkup.new(:indent=>2, :margin=>4)
202
+ @xm.name { |x| x.first("Jim") }
203
+ assert_equal " <name>\n <first>Jim</first>\n </name>\n", @xm.target!
204
+ end
205
+
206
+ class TestXmlEvents < Test::Unit::TestCase
207
+ def setup
208
+ @handler = EventHandler.new
209
+ @xe = Builder::XmlEvents.new(:target=>@handler)
210
+ end
211
+
212
+ def test_simple
213
+ @xe.p
214
+ assert_equal [:start, :p, nil], @handler.events.shift
215
+ assert_equal [:end, :p], @handler.events.shift
216
+ end
217
+
218
+ def test_text
219
+ @xe.p("HI")
220
+ assert_equal [:start, :p, nil], @handler.events.shift
221
+ assert_equal [:text, "HI"], @handler.events.shift
222
+ assert_equal [:end, :p], @handler.events.shift
223
+ end
224
+
225
+ def test_attributes
226
+ @xe.p("id"=>"2")
227
+ ev = @handler.events.shift
228
+ assert_equal [:start, :p], ev[0,2]
229
+ assert_equal "2", ev[2]['id']
230
+ assert_equal [:end, :p], @handler.events.shift
231
+ end
232
+
233
+ def test_indented
234
+ @xe = Builder::XmlEvents.new(:indent=>2, :target=>@handler)
235
+ @xe.p { |x| x.b("HI") }
236
+ assert_equal [:start, :p, nil], @handler.events.shift
237
+ assert_equal "\n ", pop_text
238
+ assert_equal [:start, :b, nil], @handler.events.shift
239
+ assert_equal "HI", pop_text
240
+ assert_equal [:end, :b], @handler.events.shift
241
+ assert_equal "\n", pop_text
242
+ assert_equal [:end, :p], @handler.events.shift
243
+ end
244
+
245
+ def pop_text
246
+ result = ''
247
+ while ! @handler.events.empty? && @handler.events[0][0] == :text
248
+ result << @handler.events[0][1]
249
+ @handler.events.shift
250
+ end
251
+ result
252
+ end
253
+
254
+ class EventHandler
255
+ attr_reader :events
256
+ def initialize
257
+ @events = []
258
+ end
259
+
260
+ def start_tag(sym, attrs)
261
+ @events << [:start, sym, attrs]
262
+ end
263
+
264
+ def end_tag(sym)
265
+ @events << [:end, sym]
266
+ end
267
+
268
+ def text(txt)
269
+ @events << [:text, txt]
270
+ end
271
+ end
272
+ end
273
+
274
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.1
3
+ specification_version: 1
4
+ name: builder
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.1.0
7
+ date: 2004-10-16
8
+ summary: Builders for MarkUp.
9
+ require_paths:
10
+ - lib
11
+ author: Jim Weirich
12
+ email: jim@weirichhouse.org
13
+ homepage: http://onestepback.org
14
+ rubyforge_project:
15
+ description: Simple builder classes for creating markup.
16
+ autorequire: builder
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ files:
29
+ - lib/builder.rb
30
+ - lib/builder/blankslate.rb
31
+ - lib/builder/xmlbase.rb
32
+ - lib/builder/xmlevents.rb
33
+ - lib/builder/xmlmarkup.rb
34
+ - test/testmarkupbuilder.rb
35
+ - README
36
+ - Rakefile
37
+ test_files:
38
+ - test/testmarkupbuilder.rb
39
+ rdoc_options:
40
+ - "--title"
41
+ - "Builder -- Easy XML Building"
42
+ - "--main"
43
+ - README
44
+ - "--line-numbers"
45
+ extra_rdoc_files:
46
+ - README
47
+ - Rakefile
48
+ executables: []
49
+ extensions: []
50
+ requirements: []
51
+ dependencies: []