jexml 0.1.0-jruby
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/Manifest.txt +28 -0
- data/README.rdoc +108 -0
- data/Rakefile +28 -0
- data/lib/core_ext/blank.rb +58 -0
- data/lib/jdom.LICENSE.txt +56 -0
- data/lib/jdom.jar +0 -0
- data/lib/jexml.rb +20 -0
- data/lib/jexml/document.rb +116 -0
- data/lib/jexml/element.rb +21 -0
- data/lib/jexml/node.rb +161 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/tasks/jruby.rake +7 -0
- data/test/mydoc.xml +27 -0
- data/test/mydoc_unformatted.xml +2 -0
- data/test/mydoc_updated_item.xml +27 -0
- data/test/test_format.rb +14 -0
- data/test/test_helper.rb +3 -0
- data/test/test_new_doc_file.rb +12 -0
- data/test/test_new_doc_string.rb +12 -0
- data/test/test_query_item_names.rb +16 -0
- data/test/test_query_item_stock_number.rb +24 -0
- data/test/test_query_item_upcs.rb +15 -0
- data/test/test_query_sections.rb +15 -0
- data/test/test_update_change_item.rb +21 -0
- data/test/test_validate_bad_xml.rb +17 -0
- metadata +124 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
lib/core_ext/blank.rb
|
6
|
+
lib/jdom.LICENSE.txt
|
7
|
+
lib/jdom.jar
|
8
|
+
lib/jexml.rb
|
9
|
+
lib/jexml/document.rb
|
10
|
+
lib/jexml/element.rb
|
11
|
+
lib/jexml/node.rb
|
12
|
+
script/console
|
13
|
+
script/destroy
|
14
|
+
script/generate
|
15
|
+
tasks/jruby.rake
|
16
|
+
test/mydoc.xml
|
17
|
+
test/mydoc_unformatted.xml
|
18
|
+
test/mydoc_updated_item.xml
|
19
|
+
test/test_format.rb
|
20
|
+
test/test_helper.rb
|
21
|
+
test/test_new_doc_file.rb
|
22
|
+
test/test_new_doc_string.rb
|
23
|
+
test/test_query_item_names.rb
|
24
|
+
test/test_query_item_stock_number.rb
|
25
|
+
test/test_query_item_upcs.rb
|
26
|
+
test/test_query_sections.rb
|
27
|
+
test/test_update_change_item.rb
|
28
|
+
test/test_validate_bad_xml.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
= jexml
|
2
|
+
|
3
|
+
* http://github.com/gboersma/jexml
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
JEXML is a JRuby gem that provides a thin Ruby wrapper around the XML APIs in the JDK.
|
8
|
+
|
9
|
+
== Features
|
10
|
+
|
11
|
+
* Most compelling feature is the JDK support for XML document validation.
|
12
|
+
* Full XPath support.
|
13
|
+
* Supports parsing and updating XML documents.
|
14
|
+
* Currently only DOM support; SAX support not yet implemented.
|
15
|
+
* 3 to 5 times better performance than REXML under JRuby.
|
16
|
+
* Uses JDOM to properly format a document, due to bug in JDK.
|
17
|
+
|
18
|
+
The implementation approach is to keep the Ruby classes as thin as possible. Currently, it
|
19
|
+
represents what was required for a specific project; the interface will get fleshed out
|
20
|
+
to more completely cover JDK functionality in later releases.
|
21
|
+
|
22
|
+
Check out Nick Sieger's JREXML gem at http://github.com/nicksieger/jrexml/
|
23
|
+
for a different approach for improving REXML performance under JRuby.
|
24
|
+
This is a better way to go if you want to maintain REXML compatibility
|
25
|
+
and do not need XML document validation.
|
26
|
+
|
27
|
+
== Synopsis
|
28
|
+
|
29
|
+
Create a new JEXML::Document object, either from a File:
|
30
|
+
|
31
|
+
file = File.new("mydoc.xml")
|
32
|
+
doc = JEXML::Document.new file
|
33
|
+
|
34
|
+
or a String:
|
35
|
+
|
36
|
+
str = File.new("mydoc.xml").read
|
37
|
+
doc = JEXML::Document.new str
|
38
|
+
|
39
|
+
Use the methods on Document and XPath expressions to access nodes within the document:
|
40
|
+
|
41
|
+
doc.nodes("//inventory/section").each do |node|
|
42
|
+
puts "Found section #{node.name}..."
|
43
|
+
end
|
44
|
+
|
45
|
+
To validate the XML (i.e. against the DTD):
|
46
|
+
|
47
|
+
str = File.new("mydoc.xml").read
|
48
|
+
doc = JEXML::Document.validate(str)
|
49
|
+
|
50
|
+
To generate formatted XML as a String for a document:
|
51
|
+
|
52
|
+
doc.format
|
53
|
+
|
54
|
+
which only accepts a boolean parameter to indicate if the XML declaration should be omitted.
|
55
|
+
|
56
|
+
There are also methods for updating a document and nodes within a document. Check out the rdocs.
|
57
|
+
|
58
|
+
== Requirements
|
59
|
+
|
60
|
+
JRuby / Java. Tested on JRuby 1.4.0 using Java 1.6.0_17 under Mac OS/X.
|
61
|
+
|
62
|
+
This product includes software developed by the JDOM Project (http://www.jdom.org/).
|
63
|
+
The JDOM 1.1.1 library is included as part of the gem.
|
64
|
+
|
65
|
+
Includes software developed by the ActiveSupport Project (http://as.rubyonrails.org/). MIT license.
|
66
|
+
The core_ext/blank.rb is included as part of the gem (unmodified from ActiveSupport 2.2.2).
|
67
|
+
|
68
|
+
== Install
|
69
|
+
|
70
|
+
Install the gem under JRuby:
|
71
|
+
|
72
|
+
jruby -S gem install jexml
|
73
|
+
|
74
|
+
And require 'jexml'.
|
75
|
+
|
76
|
+
gem 'jexml'
|
77
|
+
require 'jexml'
|
78
|
+
|
79
|
+
== Source
|
80
|
+
|
81
|
+
You can get the JEXML source using Git:
|
82
|
+
|
83
|
+
git clone git://github.com/gboersma/jexml.git
|
84
|
+
|
85
|
+
== License
|
86
|
+
|
87
|
+
(The MIT License)
|
88
|
+
|
89
|
+
Copyright (c) 2010 Gerald Boersma (gerald.boersma@gmail.com)
|
90
|
+
|
91
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
92
|
+
a copy of this software and associated documentation files (the
|
93
|
+
'Software'), to deal in the Software without restriction, including
|
94
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
95
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
96
|
+
permit persons to whom the Software is furnished to do so, subject to
|
97
|
+
the following conditions:
|
98
|
+
|
99
|
+
The above copyright notice and this permission notice shall be
|
100
|
+
included in all copies or substantial portions of the Software.
|
101
|
+
|
102
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
103
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
104
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
105
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
106
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
107
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
108
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/jexml'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'jexml' do
|
14
|
+
self.developer 'Gerald Boersma', 'gerald.boersma@gmail.com'
|
15
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
+
self.rubyforge_name = self.name # TODO this is default value
|
17
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
18
|
+
|
19
|
+
self.spec_extras['platform'] = 'jruby' # JRuby gem created, e.g. jexml-X.Y.Z-jruby.gem
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks'
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# remove_task :default
|
28
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Object
|
2
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
3
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
4
|
+
#
|
5
|
+
# This simplifies
|
6
|
+
#
|
7
|
+
# if !address.nil? && !address.empty?
|
8
|
+
#
|
9
|
+
# to
|
10
|
+
#
|
11
|
+
# if !address.blank?
|
12
|
+
def blank?
|
13
|
+
respond_to?(:empty?) ? empty? : !self
|
14
|
+
end
|
15
|
+
|
16
|
+
# An object is present if it's not blank.
|
17
|
+
def present?
|
18
|
+
!blank?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class NilClass #:nodoc:
|
23
|
+
def blank?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class FalseClass #:nodoc:
|
29
|
+
def blank?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TrueClass #:nodoc:
|
35
|
+
def blank?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Array #:nodoc:
|
41
|
+
alias_method :blank?, :empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
class Hash #:nodoc:
|
45
|
+
alias_method :blank?, :empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
class String #:nodoc:
|
49
|
+
def blank?
|
50
|
+
self !~ /\S/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Numeric #:nodoc:
|
55
|
+
def blank?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/*--
|
2
|
+
|
3
|
+
$Id: LICENSE.txt,v 1.11 2004/02/06 09:32:57 jhunter Exp $
|
4
|
+
|
5
|
+
Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
|
6
|
+
All rights reserved.
|
7
|
+
|
8
|
+
Redistribution and use in source and binary forms, with or without
|
9
|
+
modification, are permitted provided that the following conditions
|
10
|
+
are met:
|
11
|
+
|
12
|
+
1. Redistributions of source code must retain the above copyright
|
13
|
+
notice, this list of conditions, and the following disclaimer.
|
14
|
+
|
15
|
+
2. Redistributions in binary form must reproduce the above copyright
|
16
|
+
notice, this list of conditions, and the disclaimer that follows
|
17
|
+
these conditions in the documentation and/or other materials
|
18
|
+
provided with the distribution.
|
19
|
+
|
20
|
+
3. The name "JDOM" must not be used to endorse or promote products
|
21
|
+
derived from this software without prior written permission. For
|
22
|
+
written permission, please contact <request_AT_jdom_DOT_org>.
|
23
|
+
|
24
|
+
4. Products derived from this software may not be called "JDOM", nor
|
25
|
+
may "JDOM" appear in their name, without prior written permission
|
26
|
+
from the JDOM Project Management <request_AT_jdom_DOT_org>.
|
27
|
+
|
28
|
+
In addition, we request (but do not require) that you include in the
|
29
|
+
end-user documentation provided with the redistribution and/or in the
|
30
|
+
software itself an acknowledgement equivalent to the following:
|
31
|
+
"This product includes software developed by the
|
32
|
+
JDOM Project (http://www.jdom.org/)."
|
33
|
+
Alternatively, the acknowledgment may be graphical using the logos
|
34
|
+
available at http://www.jdom.org/images/logos.
|
35
|
+
|
36
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
37
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
38
|
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
39
|
+
DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
|
40
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
41
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
42
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
43
|
+
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
44
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
45
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
46
|
+
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
47
|
+
SUCH DAMAGE.
|
48
|
+
|
49
|
+
This software consists of voluntary contributions made by many
|
50
|
+
individuals on behalf of the JDOM Project and was originally
|
51
|
+
created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
|
52
|
+
Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
|
53
|
+
on the JDOM Project, please see <http://www.jdom.org/>.
|
54
|
+
|
55
|
+
*/
|
56
|
+
|
data/lib/jdom.jar
ADDED
Binary file
|
data/lib/jexml.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module JEXML
|
5
|
+
VERSION = '0.1.0'
|
6
|
+
end
|
7
|
+
|
8
|
+
# Always need Java.
|
9
|
+
require 'java'
|
10
|
+
|
11
|
+
# We use JDOM for formatting. Java SDK formatting is broken.
|
12
|
+
require "jdom.jar"
|
13
|
+
|
14
|
+
# Handy extension borrowed from Active Support.
|
15
|
+
require 'core_ext/blank'
|
16
|
+
|
17
|
+
# JEXML components.
|
18
|
+
require 'jexml/node'
|
19
|
+
require 'jexml/element'
|
20
|
+
require 'jexml/document'
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module JEXML
|
2
|
+
# Wraps an underlying org.w3c.dom.Document (http://java.sun.com/javase/6/docs/api/org/w3c/dom/Document.html).
|
3
|
+
class Document < Node
|
4
|
+
# Create a new Document from either a file or a String containing the XML content.
|
5
|
+
def self.new(source)
|
6
|
+
if source.kind_of?(File)
|
7
|
+
new_from_string(source.read)
|
8
|
+
elsif source.kind_of?(String)
|
9
|
+
new_from_string(source)
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a new Document from a String containing the XML content.
|
16
|
+
# This uses a non-validating parser.
|
17
|
+
def self.new_from_string(str)
|
18
|
+
is = java.io.ByteArrayInputStream.new(java.lang.String.new(str).getBytes)
|
19
|
+
factory = javax.xml.parsers.DocumentBuilderFactory.newInstance
|
20
|
+
factory.setNamespaceAware(true)
|
21
|
+
builder = factory.newDocumentBuilder
|
22
|
+
doc = builder.parse(is)
|
23
|
+
# Create a new object where the document is the node.
|
24
|
+
Node.create(doc)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Validate the XML in the provided string.
|
28
|
+
# Will raise an exception with the validation error if not valid.
|
29
|
+
# This uses a validating parser.
|
30
|
+
def self.validate(str)
|
31
|
+
is = java.io.ByteArrayInputStream.new(java.lang.String.new(str).getBytes)
|
32
|
+
factory = javax.xml.parsers.DocumentBuilderFactory.newInstance
|
33
|
+
factory.setValidating(true)
|
34
|
+
factory.setNamespaceAware(true)
|
35
|
+
builder = factory.newDocumentBuilder
|
36
|
+
builder.setErrorHandler(MyErrorHandler.new)
|
37
|
+
builder.parse(is)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Initialize the Ruby Document object with the corresponding Java Document object.
|
42
|
+
def initialize(java_node = nil)
|
43
|
+
# If a java_node is not specified, create a new one.
|
44
|
+
if java_node == nil
|
45
|
+
factory = javax.xml.parsers.DocumentBuilderFactory.newInstance
|
46
|
+
builder = factory.newDocumentBuilder
|
47
|
+
java_node = builder.newDocument
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create the node as normal.
|
51
|
+
super(java_node)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return the document in nice format (as a String).
|
55
|
+
# Because the Java JDK cannot properly format the XML, we will give it to JDOM and have it do it.
|
56
|
+
# Set the optional parameter omit_xml_decl to false to generate the XML declaration for the document.
|
57
|
+
def format(omit_xml_decl = true)
|
58
|
+
builder = org.jdom.input.DOMBuilder.new
|
59
|
+
doc = builder.build(@java_node)
|
60
|
+
out = org.jdom.output.XMLOutputter.new
|
61
|
+
format = org.jdom.output.Format.getPrettyFormat
|
62
|
+
format.setOmitDeclaration(omit_xml_decl)
|
63
|
+
out.setFormat(format)
|
64
|
+
out.outputString(doc)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get the root element for this document.
|
68
|
+
def root
|
69
|
+
Node.create(@java_node.getDocumentElement)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Import the specified node into this document.
|
73
|
+
def import_node(src_node)
|
74
|
+
Node.create(@java_node.importNode(src_node.java_node, true))
|
75
|
+
end
|
76
|
+
|
77
|
+
# Create an element with the specified tag name, attributes, and text content.
|
78
|
+
def create_element(tag_name, attrs = {}, text = nil)
|
79
|
+
element = Node.create(@java_node.createElement(tag_name))
|
80
|
+
if attrs != nil
|
81
|
+
attrs.each do |name, value|
|
82
|
+
element.set_attribute(name.to_s, value.to_s)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
element.text = text if text != nil
|
86
|
+
element
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a CDATA node with the specified contents.
|
90
|
+
def create_cdata_node(contents)
|
91
|
+
Node.create(@java_node.createCDATASection(contents))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the provided element as the root of the document.
|
95
|
+
# This assumes that the element is an element, and that the
|
96
|
+
# document does not yet have any children.
|
97
|
+
def root=(element)
|
98
|
+
@java_node.appendChild(element.java_node)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Implementation class for catching validation errors.
|
103
|
+
class MyErrorHandler < org.xml.sax.helpers.DefaultHandler
|
104
|
+
def fatalError(e)
|
105
|
+
raise "#{e}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def error(e)
|
109
|
+
raise "#{e}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def warning(e)
|
113
|
+
raise "#{e}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module JEXML
|
2
|
+
# Wraps an underlying org.w3c.dom.Element (http://java.sun.com/javase/6/docs/api/org/w3c/dom/Element.html).
|
3
|
+
class Element < Node
|
4
|
+
# Get the value of the attribute with the specified name.
|
5
|
+
def attribute(name)
|
6
|
+
value = java_node.getAttribute(name.to_s)
|
7
|
+
# For some reason, the Java SDK returns a blank string if an attribute is not found.
|
8
|
+
# Let's map that back to the more meaningful nil value.
|
9
|
+
if not value.blank?
|
10
|
+
value
|
11
|
+
else
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get the value of the attribute with the specified name.
|
17
|
+
def set_attribute(name, value)
|
18
|
+
java_node.setAttribute(name.to_s, value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/jexml/node.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
module JEXML
|
2
|
+
# Wraps an underlying org.w3c.dom.Node (http://java.sun.com/javase/6/docs/api/org/w3c/dom/Node.html).
|
3
|
+
# Generic Ruby representation of an underlying Java node, if no special handling provided.
|
4
|
+
class Node
|
5
|
+
# Allow read access to the underlying Java Node object.
|
6
|
+
attr_reader :java_node
|
7
|
+
|
8
|
+
# Instantiate the correct JEXML object depending on the type of the Node object.
|
9
|
+
def self.create(java_node)
|
10
|
+
# Comparing to the code values rather than the constants in Node can be dangerous.
|
11
|
+
# Possible that the actual values can change. However, unlikely.
|
12
|
+
# Difficult to get to the Interface constants using JRuby.
|
13
|
+
if java_node.getNodeType == 1
|
14
|
+
# Element
|
15
|
+
Element.new(java_node)
|
16
|
+
elsif java_node.getNodeType == 9
|
17
|
+
# Document
|
18
|
+
Document.new(java_node)
|
19
|
+
else
|
20
|
+
# No special handling. Treat as a generic node.
|
21
|
+
Node.new(java_node)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Initialize the Ruby Node object with the corresponding Java Node object.
|
26
|
+
def initialize(java_node)
|
27
|
+
@java_node = java_node
|
28
|
+
@nodeset_constant = javax.xml.xpath.XPathConstants.java_class.declared_field('NODESET').static_value
|
29
|
+
@xpath_factory = javax.xml.xpath.XPathFactory.newInstance
|
30
|
+
end
|
31
|
+
|
32
|
+
# Search for nodes within the context of this node according to the XPath expression.
|
33
|
+
def nodes(xpath)
|
34
|
+
nodes = []
|
35
|
+
xpath_expr = @xpath_factory.newXPath.compile(xpath)
|
36
|
+
xpath_nodes = xpath_expr.evaluate(@java_node, @nodeset_constant)
|
37
|
+
(0..xpath_nodes.getLength - 1).each do |index|
|
38
|
+
xpath_node = xpath_nodes.item(index)
|
39
|
+
nodes << Node.create(xpath_node)
|
40
|
+
end
|
41
|
+
nodes
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the first matching node, if any, for the provided XPath expression.
|
45
|
+
def node(xpath)
|
46
|
+
nodes = nodes(xpath)
|
47
|
+
nodes.size > 0 ? nodes[0] : nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get all the child nodes of this node.
|
51
|
+
# If a name is specified, only the child nodes with the same name are returned.
|
52
|
+
def child_nodes(name = nil)
|
53
|
+
nodes = []
|
54
|
+
child_nodes = @java_node.getChildNodes
|
55
|
+
(0..child_nodes.getLength - 1).each do |index|
|
56
|
+
child_node = child_nodes.item(index)
|
57
|
+
if name != nil
|
58
|
+
nodes << Node.create(child_node) if child_node.getNodeName == name
|
59
|
+
else
|
60
|
+
nodes << Node.create(child_node)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
nodes
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get the first child node with the specified name.
|
67
|
+
def first_child_node(name = nil)
|
68
|
+
child_nodes = child_nodes(name)
|
69
|
+
child_nodes.size > 0 ? child_nodes[0] : nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# Remove all the child nodes for this node with the specified name.
|
73
|
+
def remove_child_nodes(name)
|
74
|
+
@java_node.getChildNodes.each do |child_node|
|
75
|
+
@java_node.removeChild(child_node) if child_node.getNodeName == name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Replace the specified node with the new one provided.
|
80
|
+
def replace_child_node(old_node, new_node)
|
81
|
+
@java_node.replaceChild(new_node.java_node, old_node.java_node)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add the provided node as a child of this node.
|
85
|
+
def add_child_node(node)
|
86
|
+
@java_node.appendChild(node.java_node)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the elements of this node according to the provided XPath expression.
|
90
|
+
def elements(xpath)
|
91
|
+
extract_elements(nodes(xpath))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get the first element of this node according to the provided XPath expression.
|
95
|
+
def element(xpath)
|
96
|
+
elements = elements(xpath)
|
97
|
+
elements.size > 0 ? elements[0] : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get the child elements of this node with the specified name (if provided).
|
101
|
+
def child_elements(name = nil)
|
102
|
+
extract_elements(child_nodes(name))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the first child elements of this node with the specified name (if provided).
|
106
|
+
def first_child_element(name = nil)
|
107
|
+
child_elements = child_elements(name)
|
108
|
+
child_elements.size > 0 ? child_elements[0] : nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# Get the name of the node.
|
112
|
+
def name
|
113
|
+
@java_node.getNodeName
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get the value of the node.
|
117
|
+
def value
|
118
|
+
@java_node.getNodeValue
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the type code for the node.
|
122
|
+
def type_code
|
123
|
+
@java_node.getNodeType
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get the text content for the node.
|
127
|
+
def text
|
128
|
+
@java_node.getTextContent
|
129
|
+
end
|
130
|
+
|
131
|
+
# Set the text content for the node.
|
132
|
+
def text=(text)
|
133
|
+
@java_node.setTextContent(text)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Normalize the node.
|
137
|
+
def normalize
|
138
|
+
@java_node.normalize
|
139
|
+
end
|
140
|
+
|
141
|
+
# Append the specified node to the children of this node.
|
142
|
+
def <<(node)
|
143
|
+
@java_node.appendChild(node.java_node)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get the owner document for this node.
|
147
|
+
def document
|
148
|
+
Node.create(@java_node.getOwnerDocument)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def extract_elements(nodes)
|
154
|
+
elements = []
|
155
|
+
nodes.each do |node|
|
156
|
+
elements << node if node.class == JEXML::Element
|
157
|
+
end
|
158
|
+
elements
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/jexml.rb'}"
|
9
|
+
puts "Loading jexml gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/tasks/jruby.rake
ADDED
data/test/mydoc.xml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
<inventory title="OmniCorp Store #45x10^3">
|
2
|
+
<section name="health">
|
3
|
+
<item upc="123456789" stock="12">
|
4
|
+
<name>Invisibility Cream</name>
|
5
|
+
<price>14.50</price>
|
6
|
+
<description>Makes you invisible</description>
|
7
|
+
</item>
|
8
|
+
<item upc="445322344" stock="18">
|
9
|
+
<name>Levitation Salve</name>
|
10
|
+
<price>23.99</price>
|
11
|
+
<description>Levitate yourself for up to 3 hours per application</description>
|
12
|
+
</item>
|
13
|
+
</section>
|
14
|
+
<section name="food">
|
15
|
+
<item upc="485672034" stock="653">
|
16
|
+
<name>Blork and Freen Instameal</name>
|
17
|
+
<price>4.95</price>
|
18
|
+
<description>A tasty meal in a tablet; just add water</description>
|
19
|
+
</item>
|
20
|
+
<item upc="132957764" stock="44">
|
21
|
+
<name>Grob winglets</name>
|
22
|
+
<price>3.56</price>
|
23
|
+
<description>Tender winglets of Grob. Just add water</description>
|
24
|
+
</item>
|
25
|
+
</section>
|
26
|
+
</inventory>
|
27
|
+
|
@@ -0,0 +1,2 @@
|
|
1
|
+
<inventory title="OmniCorp Store #45x10^3"><section name="health"><item upc="123456789" stock="12"><name>Invisibility Cream</name><price>14.50</price><description>Makes you invisible</description></item><item upc="445322344" stock="18"><name>Levitation Salve</name><price>23.99</price><description>Levitate yourself for up to 3 hours per application</description></item></section><section name="food"><item upc="485672034" stock="653"><name>Blork and Freen Instameal</name><price>4.95</price><description>A tasty meal in a tablet; just add water</description></item><item upc="132957764" stock="44"><name>Grob winglets</name><price>3.56</price><description>Tender winglets of Grob. Just add water</description></item></section></inventory>
|
2
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<inventory title="OmniCorp Store #45x10^3">
|
2
|
+
<section name="health">
|
3
|
+
<item upc="123456789" stock="12">
|
4
|
+
<name>Invisibility Cream</name>
|
5
|
+
<price>14.50</price>
|
6
|
+
<description>Makes you invisible</description>
|
7
|
+
</item>
|
8
|
+
<item upc="445322344" stock="18">
|
9
|
+
<name>Levitation Salve</name>
|
10
|
+
<price>23.99</price>
|
11
|
+
<description>Levitate yourself for up to 3 hours per application</description>
|
12
|
+
</item>
|
13
|
+
</section>
|
14
|
+
<section name="food">
|
15
|
+
<item upc="485672034" stock="653">
|
16
|
+
<name>Blork and Freen Instameal</name>
|
17
|
+
<price>4.95</price>
|
18
|
+
<description>A tasty meal in a tablet; just add water</description>
|
19
|
+
</item>
|
20
|
+
<item upc="132957765" stock="45">
|
21
|
+
<name>Grob leglets</name>
|
22
|
+
<price>3.57</price>
|
23
|
+
<description>Tender leglets of Grob. Just add water</description>
|
24
|
+
</item>
|
25
|
+
</section>
|
26
|
+
</inventory>
|
27
|
+
|
data/test/test_format.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestFormat < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc_unformatted.xml")
|
6
|
+
@doc = JEXML::Document.new(file)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
formatted_xml = @doc.format
|
11
|
+
file = File.new("test/mydoc.xml")
|
12
|
+
formatted_xml == file.read
|
13
|
+
end
|
14
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestNewDocFile < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
@doc != nil and @doc.root.attribute("title") == "OmniCorp Store #45x10^3"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestNewDocString < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file.read)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
@doc != nil and @doc.root.attribute("title") == "OmniCorp Store #45x10^3"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestQueryItemNames < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file.read)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
item_names = @doc.nodes("//inventory/section/item").collect do |node|
|
11
|
+
name_node = node.first_child_node("name")
|
12
|
+
name_node.text
|
13
|
+
end
|
14
|
+
item_names == ["Invisibility Cream", "Levitation Salve", "Blork and Freen Instameal", "Grob winglets"]
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestQueryItemStockNumber < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file.read)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
item_upcs = @doc.nodes("//inventory/section/item[@stock='44']").collect do |node|
|
11
|
+
node.attribute("upc")
|
12
|
+
end
|
13
|
+
item_upcs += @doc.nodes("//inventory/section/item[@stock='653']").collect do |node|
|
14
|
+
node.attribute("upc")
|
15
|
+
end
|
16
|
+
item_upcs += @doc.nodes("//inventory/section/item[@stock='18']").collect do |node|
|
17
|
+
node.attribute("upc")
|
18
|
+
end
|
19
|
+
item_upcs += @doc.nodes("//inventory/section/item[@stock='12']").collect do |node|
|
20
|
+
node.attribute("upc")
|
21
|
+
end
|
22
|
+
item_upcs == ["123456789", "445322344", "485672034", "132957764"].reverse
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestQueryItemUpcs < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file.read)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
item_upcs = @doc.nodes("//inventory/section/item").collect do |node|
|
11
|
+
node.attribute("upc")
|
12
|
+
end
|
13
|
+
item_upcs == ["123456789", "445322344", "485672034", "132957764"]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestQuerySections < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file.read)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
sections = @doc.nodes("//inventory/section").collect do |node|
|
11
|
+
node.attribute("name")
|
12
|
+
end
|
13
|
+
sections == ["health", "food"]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestUpdateChangeItem < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
file = File.new("test/mydoc.xml")
|
6
|
+
@doc = JEXML::Document.new(file)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_truth
|
10
|
+
item_node = @doc.node("//inventory/section/item[@upc='132957764']")
|
11
|
+
item_node.set_attribute("upc", "132957765")
|
12
|
+
item_node.set_attribute("stock", "45")
|
13
|
+
name_node = item_node.first_child_node("name")
|
14
|
+
name_node.text = "Grob leglets"
|
15
|
+
price_node = item_node.first_child_node("price")
|
16
|
+
price_node.text = "3.57"
|
17
|
+
description_node = item_node.first_child_node("description")
|
18
|
+
description_node.text = "Tender leglets of Grob. Just add water"
|
19
|
+
@doc.format == File.new("test/mydoc_updated_item.xml").read
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestValidateBadXml < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_truth
|
8
|
+
error = false
|
9
|
+
begin
|
10
|
+
xml = "This is bad XML."
|
11
|
+
JEXML::Document.validate(xml)
|
12
|
+
rescue
|
13
|
+
error = true
|
14
|
+
end
|
15
|
+
error
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jexml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: jruby
|
6
|
+
authors:
|
7
|
+
- Gerald Boersma
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-03 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rubyforge
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: gemcutter
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.3.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.5.0
|
44
|
+
version:
|
45
|
+
description: JEXML is a JRuby gem that provides a thin Ruby wrapper around the XML APIs in the JDK.
|
46
|
+
email:
|
47
|
+
- gerald.boersma@gmail.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- lib/jdom.LICENSE.txt
|
56
|
+
files:
|
57
|
+
- History.txt
|
58
|
+
- Manifest.txt
|
59
|
+
- README.rdoc
|
60
|
+
- Rakefile
|
61
|
+
- lib/core_ext/blank.rb
|
62
|
+
- lib/jdom.LICENSE.txt
|
63
|
+
- lib/jdom.jar
|
64
|
+
- lib/jexml.rb
|
65
|
+
- lib/jexml/document.rb
|
66
|
+
- lib/jexml/element.rb
|
67
|
+
- lib/jexml/node.rb
|
68
|
+
- script/console
|
69
|
+
- script/destroy
|
70
|
+
- script/generate
|
71
|
+
- tasks/jruby.rake
|
72
|
+
- test/mydoc.xml
|
73
|
+
- test/mydoc_unformatted.xml
|
74
|
+
- test/mydoc_updated_item.xml
|
75
|
+
- test/test_format.rb
|
76
|
+
- test/test_helper.rb
|
77
|
+
- test/test_new_doc_file.rb
|
78
|
+
- test/test_new_doc_string.rb
|
79
|
+
- test/test_query_item_names.rb
|
80
|
+
- test/test_query_item_stock_number.rb
|
81
|
+
- test/test_query_item_upcs.rb
|
82
|
+
- test/test_query_sections.rb
|
83
|
+
- test/test_update_change_item.rb
|
84
|
+
- test/test_validate_bad_xml.rb
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://github.com/gboersma/jexml
|
87
|
+
licenses: []
|
88
|
+
|
89
|
+
post_install_message: PostInstall.txt
|
90
|
+
rdoc_options:
|
91
|
+
- --main
|
92
|
+
- README.rdoc
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: "0"
|
100
|
+
version:
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: "0"
|
106
|
+
version:
|
107
|
+
requirements: []
|
108
|
+
|
109
|
+
rubyforge_project: jexml
|
110
|
+
rubygems_version: 1.3.5
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: JEXML is a JRuby gem that provides a thin Ruby wrapper around the XML APIs in the JDK.
|
114
|
+
test_files:
|
115
|
+
- test/test_format.rb
|
116
|
+
- test/test_helper.rb
|
117
|
+
- test/test_new_doc_file.rb
|
118
|
+
- test/test_new_doc_string.rb
|
119
|
+
- test/test_query_item_names.rb
|
120
|
+
- test/test_query_item_stock_number.rb
|
121
|
+
- test/test_query_item_upcs.rb
|
122
|
+
- test/test_query_sections.rb
|
123
|
+
- test/test_update_change_item.rb
|
124
|
+
- test/test_validate_bad_xml.rb
|