builder 1.2.4 → 2.0.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/CHANGES CHANGED
@@ -1,8 +1,16 @@
1
1
  = Change Log
2
2
 
3
+ == Version 2.0.0
4
+
5
+ * Added doc directory
6
+ * Added unit tests for XmlEvents.
7
+ * Added XChar module and used it in the _escape method.
8
+ * Attributes are now quoted by default when strings. Use Symbol
9
+ attribute values for unquoted behavior.
10
+
3
11
  == Version 1.2.4
4
12
 
5
- * Added a cdata! command to an XML Builder.
13
+ * Added a cdata! command to an XML Builder (from Josh Knowles).
6
14
 
7
15
  == Version 1.2.3
8
16
 
data/README CHANGED
@@ -37,10 +37,41 @@ or
37
37
  # <person>
38
38
  # <name>Jim</name>
39
39
  # <phone>555-1234</phone>
40
- # </person
40
+ # </person>
41
41
 
42
42
  == Compatibility
43
43
 
44
+ === Version 2.0.0 Compatibility Changes
45
+
46
+ Version 2.0.0 introduces automatically escaped attribute values for
47
+ the first time. Versions prior to 2.0.0 did not insert escape
48
+ characters into attribute values in the XML markup. This allowed
49
+ attribute values to explicitly reference entities, which was
50
+ occasionally used by a small number of developers. Since strings
51
+ could always be explicitly escaped by hand, this was not a major
52
+ restriction in functionality.
53
+
54
+ However, it did suprise most users of builder. Since the body text is
55
+ normally escaped, everybody expected the attribute values to be
56
+ escaped as well. Escaped attribute values were the number one support
57
+ request on the 1.x Builder series.
58
+
59
+ Starting with Builder version 2.0.0, all attribute values expressed as
60
+ strings will be processed and the appropriate characters will be
61
+ escaped (e.g. "&" will be tranlated to "&amp;"). Attribute values
62
+ that are expressed as Symbol values will not be processed for escaped
63
+ characters and will be unchanged in output. (Yes, this probably counts
64
+ as Symbol abuse, but the convention is convenient and flexible).
65
+
66
+ Example:
67
+
68
+ xml = Builder::XmlMarkup.new
69
+ xml.sample(:escaped=>"This&That", :unescaped=>:"Here&amp;There")
70
+ xml.target! =>
71
+ <sample escaped="This&amp;That" unescaped="Here&amp;There"/>
72
+
73
+ === Version 1.0.0 Compatibility Changes
74
+
44
75
  Version 1.0.0 introduces some changes that are not backwards
45
76
  compatible with earlier releases of builder. The main areas of
46
77
  incompatibility are:
@@ -76,14 +107,14 @@ incompatibility are:
76
107
  require_gem 'builder', "~> 0.0" # Gets the old version
77
108
  require_gem 'builder', "~> 1.0" # Gets the new version
78
109
 
79
- == New Features
110
+ == Features
80
111
 
81
- * XML Comments are now supported ...
112
+ * XML Comments are supported ...
82
113
 
83
114
  xml_markup.comment! "This is a comment"
84
115
  #=> <!-- This is a comment -->
85
116
 
86
- * XML processing instructions are now supported ...
117
+ * XML processing instructions are supported ...
87
118
 
88
119
  xml_markup.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
89
120
  #=> <?xml version="1.0" encoding="UTF-8"?>
@@ -107,18 +138,19 @@ incompatibility are:
107
138
 
108
139
  If you need to have an argument to declare! be inserted without
109
140
  quotes, but the arguement does not conform to the typical Ruby
110
- syntax for symbols, then use the +intern+ method on a string to
111
- convert it to a symbol.
141
+ syntax for symbols, then use the :"string" form to specify a symbol.
112
142
 
113
- xml_markup.declare! :ELEMENT, :chapter, "(title,para+)".intern
143
+ For example:
144
+
145
+ xml_markup.declare! :ELEMENT, :chapter, :"(title,para+)"
114
146
  #=> <!ELEMENT chapter (title,para+)>
115
147
 
116
148
  Nested entity declarations are allowed. For example:
117
149
 
118
150
  @xml_markup.declare! :DOCTYPE, :chapter do |x|
119
- x.declare! :ELEMENT, :chapter, "(title,para+)".intern
120
- x.declare! :ELEMENT, :title, "(#PCDATA)".intern
121
- x.declare! :ELEMENT, :para, "(#PCDATA)".intern
151
+ x.declare! :ELEMENT, :chapter, :"(title,para+)"
152
+ x.declare! :ELEMENT, :title, :"(#PCDATA)"
153
+ x.declare! :ELEMENT, :para, :"(#PCDATA)"
122
154
  end
123
155
 
124
156
  #=>
@@ -129,7 +161,7 @@ incompatibility are:
129
161
  <!ELEMENT para (#PCDATA)>
130
162
  ]>
131
163
 
132
- * Direct support for XML namespaces is now available. If the first
164
+ * Some support for XML namespaces is now available. If the first
133
165
  argument to a tag call is a symbol, it will be joined to the tag to
134
166
  produce a namespace:tag combination. It is easier to show this than
135
167
  describe it.
@@ -140,6 +172,36 @@ incompatibility are:
140
172
  right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
141
173
  "<tt>xml.SOAP :Envelope</tt>")
142
174
 
175
+ * String attribute values are <em>now</em> escaped by default by
176
+ Builder (<b>NOTE:</b> this is _new_ behavior as of version 2.0).
177
+
178
+ However, occasionally you need to use entities in attribute values.
179
+ Using a symbols (rather than a string) for an attribute value will
180
+ cause Builder to not run its quoting/escaping algorithm on that
181
+ particular value.
182
+
183
+ (<b>Note:</b> The +escape_attrs+ option for builder is now
184
+ obsolete).
185
+
186
+ Example:
187
+
188
+ xml = Builder::XmlMarkup.new
189
+ xml.sample(:escaped=>"This&That", :unescaped=>:"Here&amp;There")
190
+ xml.target! =>
191
+ <sample escaped="This&amp;That" unescaped="Here&amp;There"/>
192
+
193
+ * UTF-8 Support
194
+
195
+ Builder correctly translates UTF-8 characters into valid XML. (New
196
+ in version 2.0.0). Thanks to Sam Ruby for the translation code.
197
+
198
+ Example:
199
+
200
+ xml = Builder::Markup.new
201
+ xml.sample("I�t�rn�ti�n�l")
202
+ xml.target! =>
203
+ "<sample>I&#241;t&#235;rn&#226;ti&#244;n&#224;l</sample>"
204
+
143
205
  == Contact
144
206
 
145
207
  Author:: Jim Weirich
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rakefile for rake -*- ruby -*-
2
2
 
3
- # Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
3
+ # Copyright 2004, 2005, 2006 by Jim Weirich (jim@weirichhouse.org).
4
4
  # All rights reserved.
5
5
 
6
6
  # Permission is granted for use, copying, modification, distribution,
@@ -17,20 +17,29 @@ rescue Exception
17
17
  nil
18
18
  end
19
19
 
20
- PKG_VERSION = "1.2.4"
20
+ # Determine the current version of the software
21
+
22
+ CURRENT_VERSION = '2.0.0'
23
+ PKG_VERSION = ENV['REL'] ? ENV['REL'] : CURRENT_VERSION
21
24
 
22
25
  SRC_RB = FileList['lib/**/*.rb']
23
26
 
24
27
  # The default task is run if rake is given no explicit arguments.
25
28
 
26
29
  desc "Default Task"
27
- task :default => :test
30
+ task :default => :test_all
28
31
 
29
32
  # Test Tasks ---------------------------------------------------------
30
33
 
31
- Rake::TestTask.new do |t|
34
+ desc "Run all tests"
35
+ task :test_all => [:test_units]
36
+ task :ta => [:test_all]
37
+
38
+ task :tu => [:test_units]
39
+
40
+ Rake::TestTask.new("test_units") do |t|
32
41
  t.test_files = FileList['test/test*.rb']
33
- t.verbose = true
42
+ t.verbose = false
34
43
  end
35
44
 
36
45
  # Create a task to build the RDOC documentation tree.
@@ -39,8 +48,8 @@ rd = Rake::RDocTask.new("rdoc") { |rdoc|
39
48
  rdoc.rdoc_dir = 'html'
40
49
  rdoc.title = "Builder for Markup"
41
50
  rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
42
- rdoc.rdoc_files.include('lib/**/*.rb', '[A-Z]*')
43
- rdoc.template = 'jamis'
51
+ rdoc.rdoc_files.include('lib/**/*.rb', '[A-Z]*', 'doc/**/*.rdoc')
52
+ rdoc.template = 'doc/jamis.rb'
44
53
  }
45
54
 
46
55
  # ====================================================================
@@ -52,6 +61,8 @@ PKG_FILES = FileList[
52
61
  'test/**/*.rb',
53
62
  'scripts/**/*.rb'
54
63
  ]
64
+ PKG_FILES.exclude('test/testcssbuilder.rb')
65
+ PKG_FILES.exclude('lib/builder/css.rb')
55
66
 
56
67
  if ! defined?(Gem)
57
68
  puts "Package Target requires RubyGEMs"
@@ -68,15 +79,14 @@ Builder provides a number of builder objects that make creating structured data
68
79
  simple to do. Currently the following builder objects are supported:
69
80
 
70
81
  * XML Markup
71
- * XML Events
82
+ * XML Events
72
83
  EOS
73
84
 
74
85
  s.files = PKG_FILES.to_a
75
-
76
86
  s.require_path = 'lib'
77
87
  s.autorequire = 'builder'
78
88
 
79
- s.test_files = Dir['test/test*.rb']
89
+ s.test_files = PKG_FILES.select { |fn| fn =~ /^test\/test/ }
80
90
 
81
91
  s.has_rdoc = true
82
92
  s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
@@ -90,7 +100,116 @@ EOS
90
100
  s.homepage = "http://onestepback.org"
91
101
  end
92
102
 
93
- Rake::GemPackageTask.new(spec) do end
103
+ Rake::GemPackageTask.new(spec) do |t|
104
+ t.need_tar = true
105
+ end
106
+ end
107
+
108
+ desc "Look for Debugging print lines"
109
+ task :dbg do
110
+ FileList['**/*.rb'].egrep /\bDBG|\bbreakpoint\b/
111
+ end
112
+
113
+ # --------------------------------------------------------------------
114
+ # Creating a release
115
+
116
+ def announce(msg='')
117
+ STDERR.puts msg
118
+ end
119
+
120
+ desc "Make a new release"
121
+ task :release => [
122
+ :prerelease,
123
+ :clobber,
124
+ :test_all,
125
+ :update_version,
126
+ :package,
127
+ :tag] do
128
+
129
+ announce
130
+ announce "**************************************************************"
131
+ announce "* Release #{PKG_VERSION} Complete."
132
+ announce "* Packages ready to upload."
133
+ announce "**************************************************************"
134
+ announce
135
+ end
136
+
137
+ # Validate that everything is ready to go for a release.
138
+ task :prerelease do
139
+ announce
140
+ announce "**************************************************************"
141
+ announce "* Making RubyGem Release #{PKG_VERSION}"
142
+ announce "* (current version #{CURRENT_VERSION})"
143
+ announce "**************************************************************"
144
+ announce
145
+
146
+ # Is a release number supplied?
147
+ unless ENV['REL']
148
+ fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
149
+ end
150
+
151
+ # Is the release different than the current release.
152
+ # (or is REUSE set?)
153
+ if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE']
154
+ fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version"
155
+ end
156
+
157
+ # Are all source files checked in?
158
+ if ENV['RELTEST']
159
+ announce "Release Task Testing, skipping checked-in file test"
160
+ else
161
+ announce "Checking for unchecked-in files..."
162
+ data = `cvs -q update`
163
+ unless data =~ /^$/
164
+ fail "CVS update is not clean ... do you have unchecked-in files?"
165
+ end
166
+ announce "No outstanding checkins found ... OK"
167
+ end
168
+ end
169
+
170
+ task :update_version => [:prerelease] do
171
+ if PKG_VERSION == CURRENT_VERSION
172
+ announce "No version change ... skipping version update"
173
+ else
174
+ announce "Updating Builder version to #{PKG_VERSION}"
175
+ open("Rakefile") do |rakein|
176
+ open("Rakefile.new", "w") do |rakeout|
177
+ rakein.each do |line|
178
+ if line =~ /^CURRENT_VERSION\s*=\s*/
179
+ rakeout.puts "CURRENT_VERSION = '#{PKG_VERSION}'"
180
+ else
181
+ rakeout.puts line
182
+ end
183
+ end
184
+ end
185
+ end
186
+ mv "Rakefile.new", "Rakefile"
187
+ if ENV['RELTEST']
188
+ announce "Release Task Testing, skipping commiting of new version"
189
+ else
190
+ sh %{cvs commit -m "Updated to version #{PKG_VERSION}" Rakefile}
191
+ end
192
+ end
193
+ end
194
+
195
+ desc "Tag all the CVS files with the latest release number (REL=x.y.z)"
196
+ task :tag => [:prerelease] do
197
+ reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"
198
+ reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']
199
+ announce "Tagging CVS with [#{reltag}]"
200
+ if ENV['RELTEST']
201
+ announce "Release Task Testing, skipping CVS tagging"
202
+ else
203
+ sh %{cvs tag #{reltag}}
204
+ end
205
+ end
206
+
207
+ desc "Install the jamis RDoc template"
208
+ task :install_jamis_template do
209
+ require 'rbconfig'
210
+ dest_dir = File.join(Config::CONFIG['rubylibdir'], "rdoc/generators/template/html")
211
+ fail "Unabled to write to #{dest_dir}" unless File.writable?(dest_dir)
212
+ install "doc/jamis.rb", dest_dir, :verbose => true
94
213
  end
95
214
 
96
215
  require 'scripts/publish'
@@ -0,0 +1,31 @@
1
+ = Builder 1.2.4 Released.
2
+
3
+ Added a "CDATA" method to the XML Markup builder (from Josh Knowles).
4
+
5
+ == What is Builder?
6
+
7
+ Builder::XmlMarkup allows easy programmatic creation of XML markup.
8
+ For example:
9
+
10
+ builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
11
+ builder.person { |b| b.name("Jim"); b.phone("555-1234") }
12
+ puts builder.target!
13
+
14
+ will generate:
15
+
16
+ <person>
17
+ <name>Jim</name>
18
+ <phone>555-1234</phone>
19
+ </person>
20
+
21
+ == Availability
22
+
23
+ The easiest way to get and install builder is via RubyGems ...
24
+
25
+ gem install builder (you may need root/admin privileges)
26
+
27
+ == Thanks
28
+
29
+ * Josh Knowles for the cdata! patch.
30
+
31
+ -- Jim Weirich
@@ -0,0 +1,46 @@
1
+ = Builder 2.0.0 Released.
2
+
3
+ == Changes in 2.0.0
4
+
5
+ * UTF-8 characters in data are now correctly translated to their XML
6
+ equivalents. (Thanks to Sam Ruby)
7
+
8
+ * Attribute values are now escaped by default. See the README
9
+ file for details.
10
+
11
+ <b>NOTE:</b> The escaping attribute values by default is different
12
+ than in previous releases of Builder. This makes version 2.0.0
13
+ somewhat incompatible with the 1.x series of Builder. If you use "&",
14
+ "<", or ">" in attributes values, you may have to change your
15
+ code. (Essentially you remove the manual escaping. The new way is
16
+ easier, believe me).
17
+
18
+ == What is Builder?
19
+
20
+ Builder::XmlMarkup is a library that allows easy programmatic creation
21
+ of XML markup. For example:
22
+
23
+ builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
24
+ builder.person { |b| b.name("Jim"); b.phone("555-1234") }
25
+
26
+ will generate:
27
+
28
+ <person>
29
+ <name>Jim</name>
30
+ <phone>555-1234</phone>
31
+ </person>
32
+
33
+ == Availability
34
+
35
+ The easiest way to get and install builder is via RubyGems ...
36
+
37
+ gem install builder (you may need root/admin privileges)
38
+
39
+ == Thanks
40
+
41
+ * Sam Ruby for the XChar module and the related UTF-8 translation
42
+ tools.
43
+ * Also to Sam Ruby for gently persuading me to start quoting attribute
44
+ values.
45
+
46
+ -- Jim Weirich
@@ -16,6 +16,9 @@ module Builder
16
16
  # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
17
17
  class BlankSlate
18
18
  class << self
19
+
20
+ # Hide the method named +name+ in the BlankSlate class. Don't
21
+ # hide +instance_eval+ or any method beginning with "__".
19
22
  def hide(name)
20
23
  undef_method name if
21
24
  instance_methods.include?(name.to_s) and
@@ -29,10 +32,14 @@ end
29
32
 
30
33
  # Since Ruby is very dynamic, methods added to the ancestors of
31
34
  # 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
35
+ # list of available BlankSlate methods. We handle this by defining a
36
+ # hook in the Object and Kernel classes that will hide any defined
33
37
  module Kernel
34
38
  class << self
35
39
  alias_method :blank_slate_method_added, :method_added
40
+
41
+ # Detect method additions to Kernel and remove them in the
42
+ # BlankSlate class.
36
43
  def method_added(name)
37
44
  blank_slate_method_added(name)
38
45
  return if self != Kernel
@@ -44,6 +51,9 @@ end
44
51
  class Object
45
52
  class << self
46
53
  alias_method :blank_slate_method_added, :method_added
54
+
55
+ # Detect method additions to Object and remove them in the
56
+ # BlankSlate class.
47
57
  def method_added(name)
48
58
  blank_slate_method_added(name)
49
59
  return if self != Object
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # The XChar library is provided courtesy of Sam Ruby (See
4
+ # http://intertwingly.net/stories/2005/09/28/xchar.rb)
5
+
6
+ # --------------------------------------------------------------------
7
+
8
+ # If the Builder::XChar module is not currently defined, fail on any
9
+ # name clashes in standard library classes.
10
+
11
+ module Builder
12
+ def self.check_for_name_collision(klass, method_name, defined_constant=nil)
13
+ if klass.instance_methods.include?(method_name)
14
+ fail RuntimeError,
15
+ "Name Collision: Method '#{method_name}' is already defined in #{klass}"
16
+ end
17
+ end
18
+ end
19
+
20
+ if ! defined?(Builder::XChar)
21
+ Builder.check_for_name_collision(String, "to_xs")
22
+ Builder.check_for_name_collision(Fixnum, "xchr")
23
+ end
24
+
25
+ ######################################################################
26
+ module Builder
27
+
28
+ ####################################################################
29
+ # XML Character converter, from Sam Ruby:
30
+ # (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
31
+ #
32
+ module XChar # :nodoc:
33
+
34
+ # See
35
+ # http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
36
+ # for details.
37
+ CP1252 = { # :nodoc:
38
+ 128 => 8364, # euro sign
39
+ 130 => 8218, # single low-9 quotation mark
40
+ 131 => 402, # latin small letter f with hook
41
+ 132 => 8222, # double low-9 quotation mark
42
+ 133 => 8230, # horizontal ellipsis
43
+ 134 => 8224, # dagger
44
+ 135 => 8225, # double dagger
45
+ 136 => 710, # modifier letter circumflex accent
46
+ 137 => 8240, # per mille sign
47
+ 138 => 352, # latin capital letter s with caron
48
+ 139 => 8249, # single left-pointing angle quotation mark
49
+ 140 => 338, # latin capital ligature oe
50
+ 142 => 381, # latin capital letter z with caron
51
+ 145 => 8216, # left single quotation mark
52
+ 146 => 8217, # right single quotation mark
53
+ 147 => 8220, # left double quotation mark
54
+ 148 => 8221, # right double quotation mark
55
+ 149 => 8226, # bullet
56
+ 150 => 8211, # en dash
57
+ 151 => 8212, # em dash
58
+ 152 => 732, # small tilde
59
+ 153 => 8482, # trade mark sign
60
+ 154 => 353, # latin small letter s with caron
61
+ 155 => 8250, # single right-pointing angle quotation mark
62
+ 156 => 339, # latin small ligature oe
63
+ 158 => 382, # latin small letter z with caron
64
+ 159 => 376, # latin capital letter y with diaeresis
65
+ }
66
+
67
+ # See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
68
+ PREDEFINED = {
69
+ 38 => '&amp;', # ampersand
70
+ 60 => '&lt;', # left angle bracket
71
+ 62 => '&gt;', # right angle bracket
72
+ }
73
+
74
+ # See http://www.w3.org/TR/REC-xml/#charsets for details.
75
+ VALID = [
76
+ [0x9, 0xA, 0xD],
77
+ (0x20..0xD7FF),
78
+ (0xE000..0xFFFD),
79
+ (0x10000..0x10FFFF)
80
+ ]
81
+ end
82
+
83
+ end
84
+
85
+
86
+ ######################################################################
87
+ # Enhance the Fixnum class with a XML escaped character conversion.
88
+ #
89
+ class Fixnum
90
+ XChar = Builder::XChar if ! defined?(XChar)
91
+
92
+ # XML escaped version of chr
93
+ def xchr
94
+ n = XChar::CP1252[self] || self
95
+ n = 42 unless XChar::VALID.find {|range| range.include? n}
96
+ XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
97
+ end
98
+ end
99
+
100
+
101
+ ######################################################################
102
+ # Enhance the String class with a XML escaped character version of
103
+ # to_s.
104
+ #
105
+ class String
106
+ # XML escaped version of to_s
107
+ def to_xs
108
+ unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
109
+ rescue
110
+ unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
111
+ end
112
+ end
@@ -13,7 +13,7 @@ module Builder
13
13
 
14
14
  # Create an XML markup builder.
15
15
  #
16
- # out:: Object receiving the markup.1 +out+ must respond to
16
+ # out:: Object receiving the markup. +out+ must respond to
17
17
  # <tt><<</tt>.
18
18
  # indent:: Number of spaces used for indentation (0 implies no
19
19
  # indentation and no line breaks).
@@ -111,11 +111,13 @@ module Builder
111
111
 
112
112
  private
113
113
 
114
+ require 'builder/xchar'
114
115
  def _escape(text)
115
- text.
116
- gsub(%r{&}, '&amp;').
117
- gsub(%r{<}, '&lt;').
118
- gsub(%r{>}, '&gt;')
116
+ text.to_xs
117
+ end
118
+
119
+ def _escape_quote(text)
120
+ _escape(text).gsub(%r{"}, '&quot;') # " WART
119
121
  end
120
122
 
121
123
  def _capture_outer_self(block)
@@ -165,13 +165,23 @@ module Builder
165
165
  # :target=><em>target_object</em>::
166
166
  # Object receiving the markup. +out+ must respond to the
167
167
  # <tt><<</tt> operator. The default is a plain string target.
168
+ #
168
169
  # :indent=><em>indentation</em>::
169
170
  # Number of spaces used for indentation. The default is no
170
171
  # indentation and no line breaks.
172
+ #
171
173
  # :margin=><em>initial_indentation_level</em>::
172
174
  # Amount of initial indentation (specified in levels, not
173
175
  # spaces).
174
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
+ #
175
185
  def initialize(options={})
176
186
  indent = options[:indent] || 0
177
187
  margin = options[:margin] || 0
@@ -290,10 +300,19 @@ module Builder
290
300
  return if attrs.nil?
291
301
  order.each do |k|
292
302
  v = attrs[k]
293
- @target << %{ #{k}="#{v}"} if v
303
+ @target << %{ #{k}="#{_attr_value(v)}"} if v
294
304
  end
295
305
  attrs.each do |k, v|
296
- @target << %{ #{k}="#{v}"} unless order.member?(k)
306
+ @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k)
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)
297
316
  end
298
317
  end
299
318
 
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'builder/xmlmarkup'
4
+ require 'benchmark'
5
+
6
+ text = "This is a test of the new xml markup. I�t�rn�ti�n�liz�ti�n\n" * 10000
7
+
8
+ include Benchmark # we need the CAPTION and FMTSTR constants
9
+ include Builder
10
+ n = 50
11
+ Benchmark.benchmark do |bm|
12
+ tf = bm.report("base") {
13
+ n.times do
14
+ x = XmlMarkup.new
15
+ x.text(text)
16
+ x.target!
17
+ end
18
+ }
19
+ def XmlMarkup._escape(text)
20
+ text.to_xs
21
+ end
22
+ tf = bm.report("to_xs") {
23
+ n.times do
24
+ x = XmlMarkup.new
25
+ x.text(text)
26
+ x.target!
27
+ end
28
+ }
29
+ end
30
+
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'builder/xchar'
5
+
6
+ class TestXmlEscaping < Test::Unit::TestCase
7
+ def test_ascii
8
+ assert_equal 'abc', 'abc'.to_xs
9
+ end
10
+
11
+ def test_predefined
12
+ assert_equal '&amp;', '&'.to_xs # ampersand
13
+ assert_equal '&lt;', '<'.to_xs # left angle bracket
14
+ assert_equal '&gt;', '>'.to_xs # right angle bracket
15
+ end
16
+
17
+ def test_invalid
18
+ assert_equal '*', "\x00".to_xs # null
19
+ assert_equal '*', "\x0C".to_xs # form feed
20
+ assert_equal '*', "\xEF\xBF\xBF".to_xs # U+FFFF
21
+ end
22
+
23
+ def test_iso_8859_1
24
+ assert_equal '&#231;', "\xE7".to_xs # small c cedilla
25
+ assert_equal '&#169;', "\xA9".to_xs # copyright symbol
26
+ end
27
+
28
+ def test_win_1252
29
+ assert_equal '&#8217;', "\x92".to_xs # smart quote
30
+ assert_equal '&#8364;', "\x80".to_xs # euro
31
+ end
32
+
33
+ def test_utf8
34
+ assert_equal '&#8217;', "\xE2\x80\x99".to_xs # right single quote
35
+ assert_equal '&#169;', "\xC2\xA9".to_xs # copy
36
+ end
37
+ end
@@ -0,0 +1,133 @@
1
+ class TestEvents < Test::Unit::TestCase
2
+
3
+ class Target
4
+ attr_reader :events
5
+
6
+ def initialize
7
+ @events = []
8
+ end
9
+
10
+ def start_tag(tag, attrs)
11
+ @events << [:start_tag, tag, attrs]
12
+ end
13
+
14
+ def end_tag(tag)
15
+ @events << [:end_tag, tag]
16
+ end
17
+
18
+ def text(string)
19
+ @events << [:text, string]
20
+ end
21
+
22
+ end
23
+
24
+
25
+ def setup
26
+ @target = Target.new
27
+ @xml = Builder::XmlEvents.new(:target=>@target)
28
+ end
29
+
30
+ def test_simple
31
+ @xml.one
32
+ expect [:start_tag, :one, nil]
33
+ expect [:end_tag, :one]
34
+ expect_done
35
+ end
36
+
37
+ def test_nested
38
+ @xml.one { @xml.two }
39
+ expect [:start_tag, :one, nil]
40
+ expect [:start_tag, :two, nil]
41
+ expect [:end_tag, :two]
42
+ expect [:end_tag, :one]
43
+ expect_done
44
+ end
45
+
46
+ def test_text
47
+ @xml.one("a")
48
+ expect [:start_tag, :one, nil]
49
+ expect [:text, "a"]
50
+ expect [:end_tag, :one]
51
+ expect_done
52
+ end
53
+
54
+ def test_special_text
55
+ @xml.one("H&R")
56
+ expect [:start_tag, :one, nil]
57
+ expect [:text, "H&R"]
58
+ expect [:end_tag, :one]
59
+ expect_done
60
+ end
61
+
62
+ def test_text_with_entity
63
+ @xml.one("H&amp;R")
64
+ expect [:start_tag, :one, nil]
65
+ expect [:text, "H&amp;R"]
66
+ expect [:end_tag, :one]
67
+ expect_done
68
+ end
69
+
70
+ def test_attributes
71
+ @xml.a(:b=>"c", :x=>"y")
72
+ expect [:start_tag, :a, {:x => "y", :b => "c"}]
73
+ expect [:end_tag, :a]
74
+ expect_done
75
+ end
76
+
77
+ def test_moderately_complex
78
+ @xml.tag! "address-book" do |x|
79
+ x.entry :id=>"1" do
80
+ x.name {
81
+ x.first "Bill"
82
+ x.last "Smith"
83
+ }
84
+ x.address "Cincinnati"
85
+ end
86
+ x.entry :id=>"2" do
87
+ x.name {
88
+ x.first "John"
89
+ x.last "Doe"
90
+ }
91
+ x.address "Columbus"
92
+ end
93
+ end
94
+ expect [:start_tag, "address-book".intern, nil]
95
+ expect [:start_tag, :entry, {:id => "1"}]
96
+ expect [:start_tag, :name, nil]
97
+ expect [:start_tag, :first, nil]
98
+ expect [:text, "Bill"]
99
+ expect [:end_tag, :first]
100
+ expect [:start_tag, :last, nil]
101
+ expect [:text, "Smith"]
102
+ expect [:end_tag, :last]
103
+ expect [:end_tag, :name]
104
+ expect [:start_tag, :address, nil]
105
+ expect [:text, "Cincinnati"]
106
+ expect [:end_tag, :address]
107
+ expect [:end_tag, :entry]
108
+ expect [:start_tag, :entry, {:id => "2"}]
109
+ expect [:start_tag, :name, nil]
110
+ expect [:start_tag, :first, nil]
111
+ expect [:text, "John"]
112
+ expect [:end_tag, :first]
113
+ expect [:start_tag, :last, nil]
114
+ expect [:text, "Doe"]
115
+ expect [:end_tag, :last]
116
+ expect [:end_tag, :name]
117
+ expect [:start_tag, :address, nil]
118
+ expect [:text, "Columbus"]
119
+ expect [:end_tag, :address]
120
+ expect [:end_tag, :entry]
121
+ expect [:end_tag, "address-book".intern]
122
+ expect_done
123
+ end
124
+
125
+ def expect(value)
126
+ assert_equal value, @target.events.shift
127
+ end
128
+
129
+ def expect_done
130
+ assert_nil @target.events.shift
131
+ end
132
+
133
+ end
@@ -1,5 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ #--
4
+ # Portions copyright 2004 by Jim Weirich (jim@weirichhouse.org).
5
+ # Portions copyright 2005 by Sam Ruby (rubys@intertwingly.net).
6
+ # All rights reserved.
7
+
8
+ # Permission is granted for use, copying, modification, distribution,
9
+ # and distribution of modified versions of this work as long as the
10
+ # above copyright notice is included.
11
+ #++
12
+
3
13
  require 'test/unit'
4
14
  require 'test/preload'
5
15
  require 'builder'
@@ -34,6 +44,31 @@ class TestMarkup < Test::Unit::TestCase
34
44
  assert_equal %{<ref id="12"/>}, @xml.target!
35
45
  end
36
46
 
47
+ def test_string_attributes_are_quoted_by_default
48
+ @xml.ref(:id => "H&R")
49
+ assert_equal %{<ref id="H&amp;R"/>}, @xml.target!
50
+ end
51
+
52
+ def test_symbol_attributes_are_unquoted_by_default
53
+ @xml.ref(:id => :"H&amp;R")
54
+ assert_equal %{<ref id="H&amp;R"/>}, @xml.target!
55
+ end
56
+
57
+ def test_attributes_quoted_can_be_turned_on
58
+ @xml = Builder::XmlMarkup.new
59
+ @xml.ref(:id => "<H&R \"block\">")
60
+ assert_equal %{<ref id="&lt;H&amp;R &quot;block&quot;&gt;"/>}, @xml.target!
61
+ end
62
+
63
+ def test_mixed_attribute_quoting_with_nested_builders
64
+ x = Builder::XmlMarkup.new(:target=>@xml)
65
+ @xml.ref(:id=>:"H&amp;R") {
66
+ x.element(:tag=>"Long&Short")
67
+ }
68
+ assert_equal "<ref id=\"H&amp;R\"><element tag=\"Long&amp;Short\"/></ref>",
69
+ @xml.target!
70
+ end
71
+
37
72
  def test_multiple_attributes
38
73
  @xml.ref(:id => 12, :name => "bill")
39
74
  assert_match %r{^<ref( id="12"| name="bill"){2}/>$}, @xml.target!
@@ -96,7 +131,7 @@ class TestMarkup < Test::Unit::TestCase
96
131
  end
97
132
 
98
133
  def test_non_escaping
99
- @xml.div("ns:xml"=>"&xml;") { |x| x << "<h&i>"; x.em("H&R Block") }
134
+ @xml.div("ns:xml"=>:"&xml;") { |x| x << "<h&i>"; x.em("H&R Block") }
100
135
  assert_equal %{<div ns:xml="&xml;"><h&i><em>H&amp;R Block</em></div>}, @xml.target!
101
136
  end
102
137
 
@@ -116,6 +151,44 @@ class TestMarkup < Test::Unit::TestCase
116
151
  end
117
152
  end
118
153
 
154
+ class TestAttributeEscaping < Test::Unit::TestCase
155
+
156
+ def setup
157
+ @xml = Builder::XmlMarkup.new
158
+ end
159
+
160
+ def test_element_gt
161
+ @xml.title('1<2')
162
+ assert_equal '<title>1&lt;2</title>', @xml.target!
163
+ end
164
+
165
+ def test_element_amp
166
+ @xml.title('AT&T')
167
+ assert_equal '<title>AT&amp;T</title>', @xml.target!
168
+ end
169
+
170
+ def test_element_amp2
171
+ @xml.title('&amp;')
172
+ assert_equal '<title>&amp;amp;</title>', @xml.target!
173
+ end
174
+
175
+ def test_attr_less
176
+ @xml.a(:title => '2>1')
177
+ assert_equal '<a title="2&gt;1"/>', @xml.target!
178
+ end
179
+
180
+ def test_attr_amp
181
+ @xml.a(:title => 'AT&T')
182
+ assert_equal '<a title="AT&amp;T"/>', @xml.target!
183
+ end
184
+
185
+ def test_attr_quot
186
+ @xml.a(:title => '"x"')
187
+ assert_equal '<a title="&quot;x&quot;"/>', @xml.target!
188
+ end
189
+
190
+ end
191
+
119
192
  class TestNameSpaces < Test::Unit::TestCase
120
193
  def setup
121
194
  @xml = Builder::XmlMarkup.new(:indent=>2)
@@ -130,11 +203,11 @@ class TestNameSpaces < Test::Unit::TestCase
130
203
  xml = Builder::XmlMarkup.new(:indent=>2)
131
204
  xml.instruct!
132
205
  xml.rdf :RDF,
133
- "xmlns:rdf" => "&rdf;",
134
- "xmlns:rdfs" => "&rdfs;",
135
- "xmlns:xsd" => "&xsd;",
136
- "xmlns:owl" => "&owl;" do
137
- xml.owl :Class, 'rdf:ID'=>'Bird' do
206
+ "xmlns:rdf" => :"&rdf;",
207
+ "xmlns:rdfs" => :"&rdfs;",
208
+ "xmlns:xsd" => :"&xsd;",
209
+ "xmlns:owl" => :"&owl;" do
210
+ xml.owl :Class, :'rdf:ID'=>'Bird' do
138
211
  xml.rdfs :label, 'bird'
139
212
  xml.rdfs :subClassOf do
140
213
  xml.owl :Restriction do
metadata CHANGED
@@ -1,62 +1,73 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10.4
2
+ rubygems_version: 0.8.11.6
3
3
  specification_version: 1
4
4
  name: builder
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.2.4
7
- date: 2005-07-01 00:00:00 -04:00
6
+ version: 2.0.0
7
+ date: 2006-02-05 00:00:00 -05:00
8
8
  summary: Builders for MarkUp.
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: jim@weirichhouse.org
12
12
  homepage: http://onestepback.org
13
13
  rubyforge_project:
14
- description: "Builder provides a number of builder objects that make creating structured data
15
- simple to do. Currently the following builder objects are supported: * XML
16
- Markup * XML Events"
14
+ description: "Builder provides a number of builder objects that make creating structured data simple to do. Currently the following builder objects are supported: * XML Markup * XML Events"
17
15
  autorequire: builder
18
16
  default_executable:
19
17
  bindir: bin
20
18
  has_rdoc: true
21
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
22
20
  requirements:
23
- -
24
- - ">"
25
- - !ruby/object:Gem::Version
26
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
27
24
  version:
28
25
  platform: ruby
29
26
  signing_key:
30
27
  cert_chain:
31
28
  authors:
32
- - Jim Weirich
29
+ - Jim Weirich
33
30
  files:
34
- - lib/builder.rb
35
- - lib/builder/xmlmarkup.rb
36
- - lib/builder/xmlbase.rb
37
- - lib/builder/blankslate.rb
38
- - lib/builder/xmlevents.rb
39
- - test/testmarkupbuilder.rb
40
- - test/testblankslate.rb
41
- - test/preload.rb
42
- - scripts/publish.rb
43
- - README
44
- - Rakefile
45
- - CHANGES
31
+ - lib/builder.rb
32
+ - lib/builder/xmlmarkup.rb
33
+ - lib/builder/xmlbase.rb
34
+ - lib/builder/blankslate.rb
35
+ - lib/builder/xmlevents.rb
36
+ - lib/builder/xchar.rb
37
+ - test/testmarkupbuilder.rb
38
+ - test/testblankslate.rb
39
+ - test/preload.rb
40
+ - test/test_xchar.rb
41
+ - test/testeventbuilder.rb
42
+ - test/performance.rb
43
+ - scripts/publish.rb
44
+ - README
45
+ - Rakefile
46
+ - CHANGES
47
+ - doc/releases/builder-1.2.4.rdoc
48
+ - doc/releases/builder-2.0.0.rdoc
46
49
  test_files:
47
- - test/testmarkupbuilder.rb
48
- - test/testblankslate.rb
50
+ - test/testmarkupbuilder.rb
51
+ - test/testblankslate.rb
52
+ - test/test_xchar.rb
53
+ - test/testeventbuilder.rb
49
54
  rdoc_options:
50
- - "--title"
51
- - "Builder -- Easy XML Building"
52
- - "--main"
53
- - README
54
- - "--line-numbers"
55
+ - --title
56
+ - Builder -- Easy XML Building
57
+ - --main
58
+ - README
59
+ - --line-numbers
55
60
  extra_rdoc_files:
56
- - README
57
- - Rakefile
58
- - CHANGES
61
+ - README
62
+ - Rakefile
63
+ - CHANGES
64
+ - doc/releases/builder-1.2.4.rdoc
65
+ - doc/releases/builder-2.0.0.rdoc
59
66
  executables: []
67
+
60
68
  extensions: []
69
+
61
70
  requirements: []
62
- dependencies: []
71
+
72
+ dependencies: []
73
+