xbuilder 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/README.rdoc +96 -0
- data/Rakefile +44 -0
- data/lib/xbuilder.rb +185 -0
- data/lib/xbuilder_template.rb +49 -0
- data/test/helper.rb +22 -0
- data/test/performance.rb +71 -0
- data/test/test_xbuilder.rb +384 -0
- data/xbuilder.gemspec +66 -0
- metadata +145 -0
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "blankslate"
|
4
|
+
gem "libxml-ruby", ">= 2.3.3"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "rdoc", "~> 3.12"
|
8
|
+
gem "bundler", "~> 1.0.0"
|
9
|
+
gem "jeweler", "~> 1.8.3"
|
10
|
+
gem "simplecov", require: false
|
11
|
+
|
12
|
+
# For performance comparison:
|
13
|
+
gem "nokogiri"
|
14
|
+
gem "builder"
|
15
|
+
end
|
data/README.rdoc
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
= Xbuilder
|
2
|
+
|
3
|
+
Xbuilder is an API-compatible Builder implementation using <tt>libxml</tt>. Also it's almost <b>2 times faster</b> than Builder and Nokogiri (see <tt>test/performance.rb</tt> output for details).
|
4
|
+
|
5
|
+
Feel free to open an issue on any incompatibility with Builder API. The list of known incompatibilities is given below.
|
6
|
+
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
Xbuilder supports almost all of the Builder's features. Here is a small example:
|
10
|
+
|
11
|
+
xml = Xbuilder.new(indent: 2)
|
12
|
+
xml.node attr: 1 do |xml| #=> <node attr="1">
|
13
|
+
xml.ns :child, attr: 2 #=> <ns:child attr="2"/>
|
14
|
+
end #=> </node>
|
15
|
+
|
16
|
+
== With Rails
|
17
|
+
|
18
|
+
# some.xml.xbuilder
|
19
|
+
xml.node do |xml|
|
20
|
+
# The first argument is your partial name, the second is a hash of locals.
|
21
|
+
xml.partial!("partial", value: 1)
|
22
|
+
end
|
23
|
+
|
24
|
+
# _partial.xml.xbuilder
|
25
|
+
xml.child(value)
|
26
|
+
|
27
|
+
will render:
|
28
|
+
|
29
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
30
|
+
<node><child>1</child></node>
|
31
|
+
|
32
|
+
== Notes
|
33
|
+
|
34
|
+
* Child blocks with 0 arguments are not supported. So, instead of:
|
35
|
+
|
36
|
+
xml.node do
|
37
|
+
xml.node
|
38
|
+
end
|
39
|
+
|
40
|
+
write:
|
41
|
+
|
42
|
+
xml.node do |xml|
|
43
|
+
xml.node
|
44
|
+
end
|
45
|
+
|
46
|
+
* XML instruction will be added automatically.
|
47
|
+
|
48
|
+
xml = Xbuilder.new(encoding: "ISO-8859-1")
|
49
|
+
xml.node "test"
|
50
|
+
puts xml.target!
|
51
|
+
|
52
|
+
will print:
|
53
|
+
|
54
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
55
|
+
<node>test</node>
|
56
|
+
|
57
|
+
* <tt>libxml-ruby</tt> gem has an issue ({#46}[https://github.com/xml4r/libxml-ruby/pull/46]) with node content encoding:
|
58
|
+
|
59
|
+
xml.esc "escaped & text"
|
60
|
+
#=> without fix: <esc>escaped </esc>
|
61
|
+
#=> with fix: <esc>escaped & text</esc>
|
62
|
+
|
63
|
+
For now I've used temporary workaround (see <tt>Xbuilder#__escape</tt>).
|
64
|
+
|
65
|
+
|
66
|
+
== Incompatibilities With Builder
|
67
|
+
|
68
|
+
* Builder-like unescaped symbol attributes are not supported.
|
69
|
+
It's currently a restriction of <tt>libxml</tt> (see <tt>xmlAttrSerializeContent</tt> implementation).
|
70
|
+
|
71
|
+
xml.node(attr: :"&esc") #=> <node attr="&esc"/>
|
72
|
+
|
73
|
+
* Custom XML processing instructions are not supported yet. Instruction will be auto-generated depending on
|
74
|
+
encoding and version provided.
|
75
|
+
|
76
|
+
* XML entity declarations are not yet supported.
|
77
|
+
|
78
|
+
== Contributing to Xbuilder
|
79
|
+
|
80
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
81
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
82
|
+
* Fork the project.
|
83
|
+
* Start a feature/bugfix branch.
|
84
|
+
* Commit and push until you are happy with your contribution.
|
85
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
86
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
87
|
+
|
88
|
+
== Authors
|
89
|
+
|
90
|
+
Builder gem was created by Jim Weirich.
|
91
|
+
|
92
|
+
Xbuilder gem was created by Nikita Afanasenko. The project is hosted on Github: http://github.com/nikitug/xbuilder.
|
93
|
+
|
94
|
+
Copyright © 2012 Nikita Afanasenko, released under the MIT license.
|
95
|
+
|
96
|
+
* http://www.opensource.org/licenses/MIT
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "xbuilder"
|
18
|
+
gem.version = "0.9"
|
19
|
+
gem.homepage = "http://github.com/nikitug/xbuilder"
|
20
|
+
gem.summary = %[API-compatible Builder implementation using libxml.]
|
21
|
+
gem.email = "nikita@afanasenko.name"
|
22
|
+
gem.authors = ["Nikita Afanasenko"]
|
23
|
+
# dependencies defined in Gemfile
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :test
|
35
|
+
|
36
|
+
require 'rdoc/task'
|
37
|
+
Rake::RDocTask.new do |rdoc|
|
38
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
39
|
+
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = "Xbuilder #{version}"
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
data/lib/xbuilder.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
require 'libxml'
|
3
|
+
|
4
|
+
# == Usage
|
5
|
+
#
|
6
|
+
# Xbuilder supports almost all of the Builder's features. Here is a small example:
|
7
|
+
#
|
8
|
+
# xml = Xbuilder.new(indent: 2)
|
9
|
+
# xml.node attr: 1 do |xml| #=> <node attr="1">
|
10
|
+
# xml.ns :child, attr: 2 #=> <ns:child attr="2"/>
|
11
|
+
# end #=> </node>
|
12
|
+
class Xbuilder < BlankSlate
|
13
|
+
XML = ::LibXML::XML #:nodoc:
|
14
|
+
define_method(:__class, find_hidden_method(:class))
|
15
|
+
|
16
|
+
# Create an XML builder. Available options are:
|
17
|
+
#
|
18
|
+
# :root::
|
19
|
+
# Root element. All nodes created by the builder will be attached to it.
|
20
|
+
#
|
21
|
+
# :encoding::
|
22
|
+
# Document encoding, e.g. "UTF-8".
|
23
|
+
# Will be used in XML instruction: <tt><?xml version="1.0" encoding="UTF-8"?></tt>
|
24
|
+
#
|
25
|
+
# Builder compatibility options:
|
26
|
+
# :indent:: Number of spaces used for indentation. Default is 0.
|
27
|
+
# :margin:: Amount of initial indentation (specified in levels, not spaces).
|
28
|
+
def initialize(options = {})
|
29
|
+
if options[:target]
|
30
|
+
::Kernel.raise ::ArgumentError, "':target' option is not supported."
|
31
|
+
end
|
32
|
+
|
33
|
+
@indent = options[:indent].to_i
|
34
|
+
@margin = options[:margin].to_i
|
35
|
+
@root = options[:root] || XML::Document.new
|
36
|
+
@encoding = options[:encoding] || "UTF-8"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Append a tag with the method's name to the output.
|
40
|
+
# xml.node { |xml| xml.child } #=> <node><child/></node>
|
41
|
+
def method_missing(name, *args, &block)
|
42
|
+
name = "#{name}:#{args.shift}" if args.first.kind_of?(::Symbol)
|
43
|
+
node = XML::Node.new(name.to_s)
|
44
|
+
text = nil
|
45
|
+
|
46
|
+
args.each do |arg|
|
47
|
+
case arg
|
48
|
+
when ::Hash
|
49
|
+
arg.each do |key, val|
|
50
|
+
k = key.to_s
|
51
|
+
v = val.to_s
|
52
|
+
node[k] = v
|
53
|
+
end
|
54
|
+
else
|
55
|
+
text ||= ''
|
56
|
+
text << arg.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if block && text
|
61
|
+
::Kernel.raise ::ArgumentError, "Cannot mix a text argument with a block"
|
62
|
+
end
|
63
|
+
|
64
|
+
# FIXME `__escape` is a temp solution till bugfix release.
|
65
|
+
# https://github.com/xml4r/libxml-ruby/pull/46
|
66
|
+
node.content = __escape(text) if text
|
67
|
+
|
68
|
+
if block
|
69
|
+
unless block.arity > 0
|
70
|
+
::Kernel.raise ::ArgumentError, "Provide at least 1 block argument: `xml.node { |xml| xml.child }'"
|
71
|
+
end
|
72
|
+
block.call(__new_instance(root: node))
|
73
|
+
end
|
74
|
+
|
75
|
+
__append_node(node)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Append a tag to the output. The first argument is a tag name.
|
79
|
+
# The rest of arguments are the same as <tt>method_missing</tt> ones.
|
80
|
+
# xml.tag!("node") { |xml| xml.tag!("child") } #=> <node><child/></node>
|
81
|
+
def tag!(name, *args, &block)
|
82
|
+
method_missing(name, *args, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Append text to the output. Escape by default.
|
86
|
+
#
|
87
|
+
# xml.node { xml.text!("escaped & text") } #=> <node>escaped & text</node>
|
88
|
+
def text!(text, escape = true)
|
89
|
+
__ensure_no_block(::Kernel.block_given?)
|
90
|
+
node = XML::Node.new_text(text)
|
91
|
+
node.output_escaping = escape
|
92
|
+
__append_node(node)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Append text to the output. Do not escape by default.
|
96
|
+
# xml.node { xml << "unescaped & text" } #=> <node>unescaped & text</node>
|
97
|
+
def <<(text, escape = false)
|
98
|
+
__ensure_no_block(::Kernel.block_given?)
|
99
|
+
text!(text, escape)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the target XML string.
|
103
|
+
def target!
|
104
|
+
# FIXME Temp solution for encoding constant lookup.
|
105
|
+
# (till bugfix release https://github.com/xml4r/libxml-ruby/pull/45 to be published)
|
106
|
+
const_name = @encoding.upcase.gsub!("-", "_")
|
107
|
+
encoding = XML::Encoding.const_get(const_name)
|
108
|
+
|
109
|
+
XML.indent_tree_output = (@indent > 0)
|
110
|
+
XML.default_tree_indent_string = (" " * @indent)
|
111
|
+
|
112
|
+
@root.to_s(encoding: encoding, indent: XML.indent_tree_output).tap do |xml|
|
113
|
+
if @margin > 0
|
114
|
+
xml.gsub!(/^/, (" " * @indent) * @margin)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Insert comment node.
|
120
|
+
def comment!(comment_text)
|
121
|
+
__ensure_no_block(::Kernel.block_given?)
|
122
|
+
node = XML::Node.new_comment(comment_text)
|
123
|
+
__append_node(node)
|
124
|
+
end
|
125
|
+
|
126
|
+
# XML declarations are not yet supported.
|
127
|
+
def declare!(inst, *args, &block)
|
128
|
+
__warn("XML declarations are not yet supported. Pull requests are welcome!")
|
129
|
+
end
|
130
|
+
|
131
|
+
# Custom XML instructions are not supported.
|
132
|
+
# Left here for Builder API compatibility.
|
133
|
+
def instruct!(*args)
|
134
|
+
# TODO should we switch XML instruction off if `instruct!` is not called?
|
135
|
+
__warn("Custom XML instructions are not supported")
|
136
|
+
end
|
137
|
+
|
138
|
+
# Insert CDATA node.
|
139
|
+
def cdata!(text)
|
140
|
+
__ensure_no_block(::Kernel.block_given?)
|
141
|
+
node = XML::Node.new_cdata(text)
|
142
|
+
__append_node(node)
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def __escape(text)
|
148
|
+
text.tap do |t|
|
149
|
+
t.gsub!(/&(?![a-zA-Z]+;)|<|>|'|"/) do |match|
|
150
|
+
case match
|
151
|
+
when "&" then "&"
|
152
|
+
when "<" then "<"
|
153
|
+
when ">" then ">"
|
154
|
+
when "'" then "'"
|
155
|
+
when '"' then """
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def __append_node(node)
|
162
|
+
if @root.kind_of?(XML::Document)
|
163
|
+
@root.root = node
|
164
|
+
else
|
165
|
+
@root << node
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def __new_instance(root)
|
170
|
+
__class.new(root)
|
171
|
+
end
|
172
|
+
|
173
|
+
def __ensure_no_block(given)
|
174
|
+
if given
|
175
|
+
::Kernel.raise ArgumentError.new("Blocks are not allowed on XML instructions")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def __warn(msg)
|
180
|
+
::Kernel.warn("Xbuilder WARNING: #{msg}")
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
require "xbuilder/template" if defined?(ActionView::Template)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class XbuilderTemplate < Xbuilder
|
2
|
+
define_method(:__instance_variable_set, find_hidden_method(:instance_variable_set))
|
3
|
+
|
4
|
+
def self.encode(context)
|
5
|
+
xml = self.new
|
6
|
+
xml.__instance_variable_set(:@context, context)
|
7
|
+
yield xml
|
8
|
+
xml.target!
|
9
|
+
end
|
10
|
+
|
11
|
+
def partial!(options, locals = {})
|
12
|
+
case options
|
13
|
+
when Hash
|
14
|
+
options[:locals] ||= {}
|
15
|
+
options[:locals].merge!(xml: self)
|
16
|
+
@context.render(options)
|
17
|
+
else
|
18
|
+
@context.render(options, locals.merge(xml: self))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def __new_instance(*args)
|
25
|
+
super.tap do |xml|
|
26
|
+
xml.__instance_variable_set(:@context, @context)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class XbuilderHandler
|
33
|
+
cattr_accessor :default_format
|
34
|
+
self.default_format = Mime::XML
|
35
|
+
|
36
|
+
def self.call(template)
|
37
|
+
%[
|
38
|
+
if defined?(xml)
|
39
|
+
#{template.source}
|
40
|
+
else
|
41
|
+
XbuilderTemplate.encode(self) do |xml|
|
42
|
+
#{template.source}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ActionView::Template.register_template_handler :xbuilder, XbilderHandler
|
data/test/helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'test/unit'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
+
require 'xbuilder'
|
17
|
+
|
18
|
+
class Test::Unit::TestCase
|
19
|
+
def without_instruct(xml)
|
20
|
+
xml.gsub(/^\s*<\?xml[^>]*>\n/, "").rstrip
|
21
|
+
end
|
22
|
+
end
|
data/test/performance.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'xbuilder'
|
3
|
+
require 'builder'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'benchmark'
|
6
|
+
|
7
|
+
xml_block = proc do |xml|
|
8
|
+
xml.root do |xml|
|
9
|
+
10.times do
|
10
|
+
xml.node "text"
|
11
|
+
xml.node2 do |xml|
|
12
|
+
xml.test args: 1, args2: 4 do |xml|
|
13
|
+
xml.child arg: "text" do |xml|
|
14
|
+
xml.one_more arg: 1, arg: "2123"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
repeat = 200
|
23
|
+
xmls = []
|
24
|
+
Benchmark.bm do |x|
|
25
|
+
x.report "builder" do
|
26
|
+
repeat.times do
|
27
|
+
xml = Builder::XmlMarkup.new
|
28
|
+
xml.instruct!
|
29
|
+
xml_block.call(xml)
|
30
|
+
xmls << xml.target!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
x.report "nokogiri" do
|
34
|
+
repeat.times do
|
35
|
+
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
36
|
+
xml_block.call(xml)
|
37
|
+
end
|
38
|
+
xmls << builder.to_xml(indent: 0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
x.report "xbuilder" do
|
42
|
+
repeat.times do
|
43
|
+
xml = Xbuilder.new
|
44
|
+
xml_block.call(xml)
|
45
|
+
xmls << xml.target!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Enshure all xmls are the same.
|
51
|
+
xml = xmls.first.split.join
|
52
|
+
xmls.each do |xml2|
|
53
|
+
if xml != xml2.split.join
|
54
|
+
puts xml
|
55
|
+
puts xml2
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
require 'ruby-prof'
|
61
|
+
|
62
|
+
RubyProf.start
|
63
|
+
repeat.times do
|
64
|
+
xml = Xbuilder.new
|
65
|
+
xml_block.call(xml)
|
66
|
+
xmls << xml.target!
|
67
|
+
end
|
68
|
+
result = RubyProf.stop
|
69
|
+
|
70
|
+
printer = RubyProf::FlatPrinter.new(result)
|
71
|
+
printer.print(STDOUT)
|
@@ -0,0 +1,384 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
#--
|
4
|
+
# To enshure compatibility, a significant part of this test was taken from Builder gem.
|
5
|
+
#
|
6
|
+
# Portions copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
7
|
+
# Portions copyright 2005 by Sam Ruby (rubys@intertwingly.net).
|
8
|
+
# Portions copyright 2012 by Nikita Afanasenko (nikita@afanasenko.name).
|
9
|
+
# All rights reserved.
|
10
|
+
|
11
|
+
# Permission is granted for use, copying, modification, distribution,
|
12
|
+
# and distribution of modified versions of this work as long as the
|
13
|
+
# above copyright notice is included.
|
14
|
+
#++
|
15
|
+
|
16
|
+
class TestXbuilder < Test::Unit::TestCase
|
17
|
+
def setup
|
18
|
+
@xml = Xbuilder.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_simple
|
22
|
+
@xml.simple
|
23
|
+
assert_equal "<simple/>", without_instruct(@xml.target!)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_value
|
27
|
+
@xml.value("hi")
|
28
|
+
assert_equal "<value>hi</value>", without_instruct(@xml.target!)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_nested
|
32
|
+
@xml.outer { |x| x.inner("x") }
|
33
|
+
assert_equal "<outer><inner>x</inner></outer>", without_instruct(@xml.target!)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_attributes
|
37
|
+
@xml.ref(:id => 12)
|
38
|
+
assert_equal %{<ref id="12"/>}, without_instruct(@xml.target!)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_string_attributes_are_quoted_by_default
|
42
|
+
@xml.ref(:id => "H&R")
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_symbol_attributes_are_unquoted_by_default
|
46
|
+
skip "Unquoted symbol attributes are not supported."
|
47
|
+
@xml.ref(:id => :"H&R")
|
48
|
+
assert_equal %{<ref id="H&R"/>}, without_instruct(@xml.target!)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_attributes_quoted_can_be_turned_on
|
52
|
+
@xml.ref(:id => "<H&R \"block\">")
|
53
|
+
assert_equal %{<ref id="<H&R "block">"/>}, without_instruct(@xml.target!)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_multiple_attributes
|
57
|
+
@xml.ref(:id => 12, :name => "bill")
|
58
|
+
assert_match %r{^<ref( id="12"| name="bill"){2}/>$}, without_instruct(@xml.target!)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_attributes_with_text
|
62
|
+
@xml.a("link", :href=>"http://onestepback.org")
|
63
|
+
assert_equal %{<a href="http://onestepback.org">link</a>}, without_instruct(@xml.target!)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_complex
|
67
|
+
@xml.body(:bg=>"#ffffff") { |x|
|
68
|
+
x.title("T", :style=>"red")
|
69
|
+
}
|
70
|
+
assert_equal %{<body bg="#ffffff"><title style="red">T</title></body>}, without_instruct(@xml.target!)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_funky_symbol
|
74
|
+
@xml.tag!("non-ruby-token", :id=>1) { |x| x.ok }
|
75
|
+
assert_equal %{<non-ruby-token id="1"><ok/></non-ruby-token>}, without_instruct(@xml.target!)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_tag_can_handle_private_method
|
79
|
+
@xml.tag!("loop", :id=>1) { |x| x.ok }
|
80
|
+
assert_equal %{<loop id="1"><ok/></loop>}, without_instruct(@xml.target!)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_no_explicit_marker
|
84
|
+
@xml.p { |x| x.b("HI") }
|
85
|
+
assert_equal "<p><b>HI</b></p>", without_instruct(@xml.target!)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_reference_local_vars
|
89
|
+
n = 3
|
90
|
+
@xml.ol { |x| n.times { x.li(n) } }
|
91
|
+
assert_equal "<ol><li>3</li><li>3</li><li>3</li></ol>", without_instruct(@xml.target!)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_reference_methods
|
95
|
+
@xml.title { |x| x.a { |x| x.b(name) } }
|
96
|
+
assert_equal "<title><a><b>bob</b></a></title>", without_instruct(@xml.target!)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_append_text
|
100
|
+
@xml.p { |x| x.br; x.text! "HI" }
|
101
|
+
assert_equal "<p><br/>HI</p>", without_instruct(@xml.target!)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_ambiguous_markup
|
105
|
+
ex = assert_raise(ArgumentError) {
|
106
|
+
@xml.h1("data1") { b }
|
107
|
+
}
|
108
|
+
assert_match /\btext\b/, ex.message
|
109
|
+
assert_match /\bblock\b/, ex.message
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_capitalized_method
|
113
|
+
@xml.P { |x| x.B("hi"); x.BR(); x.EM { |x| x.text! "world" } }
|
114
|
+
assert_equal "<P><B>hi</B><BR/><EM>world</EM></P>", without_instruct(@xml.target!)
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_escaping
|
118
|
+
@xml.div { |x| x.text! "<hi>"; x.em("H&R Block") }
|
119
|
+
assert_equal %{<div><hi><em>H&R Block</em></div>}, without_instruct(@xml.target!)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_non_escaping
|
123
|
+
@xml.div("ns:xml"=>"xml") { |x| x << "<h&i>"; x.em("H&R Block") }
|
124
|
+
assert_equal %{<div ns:xml="xml"><h&i><em>H&R Block</em></div>}, without_instruct(@xml.target!)
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_content_non_escaping
|
128
|
+
@xml.div { |x| x << "<h&i>" }
|
129
|
+
assert_equal %{<div><h&i></div>}, without_instruct(@xml.target!)
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_content_escaping
|
133
|
+
@xml.div { |x| x.text!("<hi>") }
|
134
|
+
assert_equal %{<div><hi></div>}, without_instruct(@xml.target!)
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_return_value
|
138
|
+
str = @xml.x("men").to_s
|
139
|
+
assert_equal without_instruct(@xml.target!), str
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_stacked_builders
|
143
|
+
ex = assert_raise(ArgumentError) { Xbuilder.new( :target => @xml ) }
|
144
|
+
assert_match /target/, ex.message
|
145
|
+
end
|
146
|
+
|
147
|
+
def name
|
148
|
+
"bob"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class TestAttributeEscaping < Test::Unit::TestCase
|
153
|
+
def setup
|
154
|
+
@xml = Xbuilder.new
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_element_gt
|
158
|
+
@xml.title('1<2')
|
159
|
+
assert_equal '<title>1<2</title>', without_instruct(@xml.target!)
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_element_amp
|
163
|
+
@xml.title('AT&T')
|
164
|
+
assert_equal '<title>AT&T</title>', without_instruct(@xml.target!)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_element_amp2
|
168
|
+
@xml.title('&')
|
169
|
+
assert_equal '<title>&</title>', without_instruct(@xml.target!)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_attr_less
|
173
|
+
@xml.a(:title => '2>1')
|
174
|
+
assert_equal '<a title="2>1"/>', without_instruct(@xml.target!)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_attr_amp
|
178
|
+
@xml.a(:title => 'AT&T')
|
179
|
+
assert_equal '<a title="AT&T"/>', without_instruct(@xml.target!)
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_attr_quot
|
183
|
+
@xml.a(:title => '"x"')
|
184
|
+
assert_equal '<a title=""x""/>', without_instruct(@xml.target!)
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
class TestNameSpaces < Test::Unit::TestCase
|
190
|
+
def setup
|
191
|
+
@xml = Xbuilder.new(:indent=>2)
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_simple_name_spaces
|
195
|
+
@xml.rdf :RDF
|
196
|
+
assert_equal "<rdf:RDF/>", without_instruct(@xml.target!)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class TestSpecialMarkup < Test::Unit::TestCase
|
201
|
+
def setup
|
202
|
+
@xml = Xbuilder.new(:indent=>2)
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_comment
|
206
|
+
@xml.comment!("COMMENT")
|
207
|
+
assert_equal "<!--COMMENT-->", without_instruct(@xml.target!)
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_indented_comment
|
211
|
+
@xml.p { |x| x.comment! "OK" }
|
212
|
+
assert_equal "<p>\n <!--OK-->\n</p>", without_instruct(@xml.target!)
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_instruct
|
216
|
+
skip "Custom instructs are not supported yet."
|
217
|
+
@xml.instruct! :abc, :version=>"0.9"
|
218
|
+
assert_equal "<?abc version=\"0.9\"?>\n", without_instruct(@xml.target!)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_indented_instruct
|
222
|
+
skip "Custom instructs are not supported yet."
|
223
|
+
@xml.p { @xml.instruct! :xml }
|
224
|
+
assert_match %r{<p>\n <\?xml version="1.0" encoding="UTF-8"\?>\n</p>\n},
|
225
|
+
without_instruct(@xml.target!)
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_instruct_without_attributes
|
229
|
+
skip "Custom instructs are not supported yet."
|
230
|
+
@xml.instruct! :zz
|
231
|
+
assert_equal "<?zz?>\n", without_instruct(@xml.target!)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_xml_instruct
|
235
|
+
#@xml.instruct!
|
236
|
+
assert_match /^<\?xml version="1.0" encoding="UTF-8"\?>$/, @xml.target!
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_xml_instruct_with_overrides
|
240
|
+
skip "Custom instructs are not supported yet."
|
241
|
+
# FIXME shoud deal with encodings
|
242
|
+
@xml.instruct! :xml, :encoding=>"UCS-2"
|
243
|
+
assert_match /^<\?xml version="1.0" encoding="UCS-2"\?>$/, without_instruct(@xml.target!)
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_xml_instruct_with_standalong
|
247
|
+
skip "Custom instructs are not supported yet."
|
248
|
+
@xml.instruct! :xml, :encoding=>"UCS-2", :standalone=>"yes"
|
249
|
+
assert_match /^<\?xml version="1.0" encoding="UCS-2" standalone="yes"\?>$/, without_instruct(@xml.target!)
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_no_blocks
|
253
|
+
assert_raise(ArgumentError) do
|
254
|
+
@xml.cdata!("test") { |x| x.hi }
|
255
|
+
end
|
256
|
+
assert_raise(ArgumentError) do
|
257
|
+
@xml.comment!(:element) { |x| x.hi }
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_block_arity_check
|
262
|
+
assert_raise(ArgumentError) do
|
263
|
+
@xml.node { x.hi }
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_cdata
|
268
|
+
@xml.cdata!("TEST")
|
269
|
+
assert_equal "<![CDATA[TEST]]>", without_instruct(@xml.target!)
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_cdata_with_ampersand
|
273
|
+
@xml.cdata!("TEST&CHECK")
|
274
|
+
assert_equal "<![CDATA[TEST&CHECK]]>", without_instruct(@xml.target!)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class TestIndentedXmlMarkup < Test::Unit::TestCase
|
279
|
+
def setup
|
280
|
+
@xml = Xbuilder.new(:indent=>2)
|
281
|
+
end
|
282
|
+
|
283
|
+
def test_one_level
|
284
|
+
@xml.ol { |x| x.li "text" }
|
285
|
+
assert_equal "<ol>\n <li>text</li>\n</ol>", without_instruct(@xml.target!)
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_two_levels
|
289
|
+
@xml.p { |x|
|
290
|
+
x.ol { |x| x.li "text" }
|
291
|
+
x.br
|
292
|
+
}
|
293
|
+
assert_equal "<p>\n <ol>\n <li>text</li>\n </ol>\n <br/>\n</p>", without_instruct(@xml.target!)
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_initial_level
|
297
|
+
@xml = Xbuilder.new(:indent=>2, :margin=>4)
|
298
|
+
@xml.name { |x| x.first("Jim") }
|
299
|
+
assert_equal " <name>\n <first>Jim</first>\n </name>", without_instruct(@xml.target!)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# FIXME should it be supported?
|
304
|
+
#
|
305
|
+
#class TestUtfMarkup < Test::Unit::TestCase
|
306
|
+
#if ! String.method_defined?(:encode)
|
307
|
+
#def setup
|
308
|
+
#@old_kcode = $KCODE
|
309
|
+
#end
|
310
|
+
|
311
|
+
#def teardown
|
312
|
+
#$KCODE = @old_kcode
|
313
|
+
#end
|
314
|
+
|
315
|
+
#def test_use_entities_if_no_encoding_is_given_and_kcode_is_none
|
316
|
+
#$KCODE = 'NONE'
|
317
|
+
#xml = Xbuilder.new
|
318
|
+
#xml.p("\xE2\x80\x99")
|
319
|
+
#assert_match(%r(<p>’</p>), xml.target!) #
|
320
|
+
#end
|
321
|
+
|
322
|
+
#def test_use_entities_if_encoding_is_utf_but_kcode_is_not
|
323
|
+
#$KCODE = 'NONE'
|
324
|
+
#xml = Xbuilder.new
|
325
|
+
#xml.instruct!(:xml, :encoding => 'UTF-8')
|
326
|
+
#xml.p("\xE2\x80\x99")
|
327
|
+
#assert_match(%r(<p>’</p>), xml.target!) #
|
328
|
+
#end
|
329
|
+
#else
|
330
|
+
## change in behavior. As there is no $KCODE anymore, the default
|
331
|
+
## moves from "does not understand utf-8" to "supports utf-8".
|
332
|
+
|
333
|
+
#def test_use_entities_if_no_encoding_is_given_and_kcode_is_none
|
334
|
+
#xml = Xbuilder.new
|
335
|
+
#xml.p("\xE2\x80\x99")
|
336
|
+
#assert_match("<p>\u2019</p>", xml.target!) #
|
337
|
+
#end
|
338
|
+
|
339
|
+
#def test_use_entities_if_encoding_is_utf_but_kcode_is_not
|
340
|
+
#xml = Xbuilder.new
|
341
|
+
#xml.instruct!(:xml, :encoding => 'UTF-8')
|
342
|
+
#xml.p("\xE2\x80\x99")
|
343
|
+
#assert_match("<p>\u2019</p>", xml.target!) #
|
344
|
+
#end
|
345
|
+
#end
|
346
|
+
|
347
|
+
#def encode string, encoding
|
348
|
+
#if !String.method_defined?(:encode)
|
349
|
+
#$KCODE = encoding
|
350
|
+
#string
|
351
|
+
#elsif encoding == 'UTF8'
|
352
|
+
#string.force_encoding('UTF-8')
|
353
|
+
#else
|
354
|
+
#string
|
355
|
+
#end
|
356
|
+
#end
|
357
|
+
|
358
|
+
#def test_use_entities_if_kcode_is_utf_but_encoding_is_something_else
|
359
|
+
#xml = Xbuilder.new
|
360
|
+
#xml.instruct!(:xml, :encoding => 'UTF-16')
|
361
|
+
#xml.p(encode("\xE2\x80\x99", 'UTF8'))
|
362
|
+
#assert_match(%r(<p>’</p>), xml.target!) #
|
363
|
+
#end
|
364
|
+
|
365
|
+
#def test_use_utf8_if_encoding_defaults_and_kcode_is_utf8
|
366
|
+
#xml = Xbuilder.new
|
367
|
+
#xml.p(encode("\xE2\x80\x99",'UTF8'))
|
368
|
+
#assert_equal encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
|
369
|
+
#end
|
370
|
+
|
371
|
+
#def test_use_utf8_if_both_encoding_and_kcode_are_utf8
|
372
|
+
#xml = Xbuilder.new
|
373
|
+
#xml.instruct!(:xml, :encoding => 'UTF-8')
|
374
|
+
#xml.p(encode("\xE2\x80\x99",'UTF8'))
|
375
|
+
#assert_match encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
|
376
|
+
#end
|
377
|
+
|
378
|
+
#def test_use_utf8_if_both_encoding_and_kcode_are_utf8_with_lowercase
|
379
|
+
#xml = Xbuilder.new
|
380
|
+
#xml.instruct!(:xml, :encoding => 'utf-8')
|
381
|
+
#xml.p(encode("\xE2\x80\x99",'UTF8'))
|
382
|
+
#assert_match encode("<p>\xE2\x80\x99</p>",'UTF8'), xml.target!
|
383
|
+
#end
|
384
|
+
#end
|
data/xbuilder.gemspec
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "xbuilder"
|
8
|
+
s.version = "0.9"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Nikita Afanasenko"]
|
12
|
+
s.date = "2012-08-26"
|
13
|
+
s.email = "nikita@afanasenko.name"
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
"Gemfile",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"lib/xbuilder.rb",
|
22
|
+
"lib/xbuilder_template.rb",
|
23
|
+
"test/helper.rb",
|
24
|
+
"test/performance.rb",
|
25
|
+
"test/test_xbuilder.rb",
|
26
|
+
"xbuilder.gemspec"
|
27
|
+
]
|
28
|
+
s.homepage = "http://github.com/nikitug/xbuilder"
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = "1.8.15"
|
31
|
+
s.summary = "API-compatible Builder implementation using libxml."
|
32
|
+
|
33
|
+
if s.respond_to? :specification_version then
|
34
|
+
s.specification_version = 3
|
35
|
+
|
36
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
37
|
+
s.add_runtime_dependency(%q<blankslate>, [">= 0"])
|
38
|
+
s.add_runtime_dependency(%q<libxml-ruby>, [">= 2.3.3"])
|
39
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
40
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
41
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
42
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
43
|
+
s.add_development_dependency(%q<nokogiri>, [">= 0"])
|
44
|
+
s.add_development_dependency(%q<builder>, [">= 0"])
|
45
|
+
else
|
46
|
+
s.add_dependency(%q<blankslate>, [">= 0"])
|
47
|
+
s.add_dependency(%q<libxml-ruby>, [">= 2.3.3"])
|
48
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
49
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
50
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
51
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
52
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
53
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<blankslate>, [">= 0"])
|
57
|
+
s.add_dependency(%q<libxml-ruby>, [">= 2.3.3"])
|
58
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
59
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
61
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
62
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
63
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xbuilder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.9'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nikita Afanasenko
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: blankslate
|
16
|
+
requirement: &70319835213640 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70319835213640
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: libxml-ruby
|
27
|
+
requirement: &70319835224860 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.3.3
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70319835224860
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rdoc
|
38
|
+
requirement: &70319835220600 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.12'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70319835220600
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: &70319835218740 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.0.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70319835218740
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: jeweler
|
60
|
+
requirement: &70319835238360 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.8.3
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70319835238360
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: &70319835236620 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70319835236620
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: nokogiri
|
82
|
+
requirement: &70319835235820 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70319835235820
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: builder
|
93
|
+
requirement: &70319835234380 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70319835234380
|
102
|
+
description:
|
103
|
+
email: nikita@afanasenko.name
|
104
|
+
executables: []
|
105
|
+
extensions: []
|
106
|
+
extra_rdoc_files:
|
107
|
+
- README.rdoc
|
108
|
+
files:
|
109
|
+
- Gemfile
|
110
|
+
- README.rdoc
|
111
|
+
- Rakefile
|
112
|
+
- lib/xbuilder.rb
|
113
|
+
- lib/xbuilder_template.rb
|
114
|
+
- test/helper.rb
|
115
|
+
- test/performance.rb
|
116
|
+
- test/test_xbuilder.rb
|
117
|
+
- xbuilder.gemspec
|
118
|
+
homepage: http://github.com/nikitug/xbuilder
|
119
|
+
licenses: []
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ! '>='
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
hash: -2434364586004529730
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
135
|
+
requirements:
|
136
|
+
- - ! '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 1.8.15
|
142
|
+
signing_key:
|
143
|
+
specification_version: 3
|
144
|
+
summary: API-compatible Builder implementation using libxml.
|
145
|
+
test_files: []
|