XmlEasy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +8 -0
- data/README.txt +51 -0
- data/Rakefile +18 -0
- data/bin/xml_easy +0 -0
- data/lib/xml_easy.rb +246 -0
- data/test/test.xml +5 -0
- data/test/test_xml_easy.rb +122 -0
- metadata +64 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
XmlEasy
|
2
|
+
by ANDO Yasushi
|
3
|
+
andyjpn@gmail.com
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
ruby-easy offers you a way to handle xml quite easily.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* handle xml
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
xml_easy = XmlEasy::Document.new 'text.html'
|
16
|
+
|
17
|
+
# return a link target for the 3rd item in sidebar
|
18
|
+
xml_easy.html.body.div{|e| e[:class] == 'sidebar'}.li[3].a[:href]
|
19
|
+
|
20
|
+
== REQUIREMENTS:
|
21
|
+
|
22
|
+
* cosmetics
|
23
|
+
|
24
|
+
== INSTALL:
|
25
|
+
|
26
|
+
* sudo gem install xml-easy
|
27
|
+
|
28
|
+
== LICENSE:
|
29
|
+
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2007 FIX
|
33
|
+
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
35
|
+
a copy of this software and associated documentation files (the
|
36
|
+
'Software'), to deal in the Software without restriction, including
|
37
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
38
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
39
|
+
permit persons to whom the Software is furnished to do so, subject to
|
40
|
+
the following conditions:
|
41
|
+
|
42
|
+
The above copyright notice and this permission notice shall be
|
43
|
+
included in all copies or substantial portions of the Software.
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
46
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
47
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
48
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
49
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
50
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
51
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/xml_easy.rb'
|
6
|
+
|
7
|
+
#Hoe.new('XmlEasy', XmlEasy::VERSION) do |p|
|
8
|
+
Hoe.new('XmlEasy', XmlEasy::VERSION) do |p|
|
9
|
+
p.rubyforge_name = 'xml_easy'
|
10
|
+
p.author = 'ANDO Yasushi'
|
11
|
+
p.email = 'andyjpn@gmail.com'
|
12
|
+
p.summary = 'handle xml easily'
|
13
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
14
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
15
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/bin/xml_easy
ADDED
File without changes
|
data/lib/xml_easy.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'rexml/parsers/streamparser'
|
4
|
+
require 'rexml/parsers/baseparser'
|
5
|
+
require 'rexml/streamlistener'
|
6
|
+
|
7
|
+
class Array
|
8
|
+
def method_missing(name, *args, &block)
|
9
|
+
if all? {|e| nil? or e.class == XmlEasy::Element}
|
10
|
+
elms = map{|e| e.send(name, *args, &block) rescue nil}.flatten.compact
|
11
|
+
unless block.nil?
|
12
|
+
elms = elms.select &block
|
13
|
+
end
|
14
|
+
elms
|
15
|
+
else
|
16
|
+
raise NoMethodError.new(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class HashWithIndifferentAccess < Hash
|
22
|
+
def get_with_indifferent_access(key)
|
23
|
+
self[key.to_s] || self[key.to_sym]
|
24
|
+
end
|
25
|
+
alias get_with_indifferent_access []
|
26
|
+
|
27
|
+
def set_with_indifferent_access(key, val)
|
28
|
+
self[key.to_s] = val
|
29
|
+
end
|
30
|
+
alias set_with_indifferent_access []=
|
31
|
+
end
|
32
|
+
|
33
|
+
class XmlEasy
|
34
|
+
VERSION = '0.0.1'
|
35
|
+
|
36
|
+
class EasyListener
|
37
|
+
include REXML::StreamListener
|
38
|
+
def initialize(doc)
|
39
|
+
@doc = doc
|
40
|
+
@current = doc.root
|
41
|
+
end
|
42
|
+
|
43
|
+
def attlistdecl(elm_name, attrs, content); end
|
44
|
+
def cdata(content); end
|
45
|
+
def comment(content); end
|
46
|
+
def elementdecl(content); end
|
47
|
+
def entity(content); end
|
48
|
+
def instruction(name, inst); end
|
49
|
+
def notationdecl(content); end
|
50
|
+
|
51
|
+
def xmldecl(version, encoding, standalone)
|
52
|
+
@doc.xml_dec = "<?xml#{
|
53
|
+
version ? " version='#{version}'" : ''
|
54
|
+
}#{
|
55
|
+
encoding ? " encoding='#{encoding}'" : ''
|
56
|
+
}#{
|
57
|
+
standalone ? " standalone='#{standalone}'" : ''
|
58
|
+
}?>"
|
59
|
+
end
|
60
|
+
|
61
|
+
def doctype(name, pub_sys, long_name, uri)
|
62
|
+
#TODO: temporary
|
63
|
+
@doc.doc_type = "<!DOCTYPE window [<!ENTITY nl '&#10;'>]>"
|
64
|
+
end
|
65
|
+
|
66
|
+
def entitydecl(content)
|
67
|
+
end
|
68
|
+
|
69
|
+
def doctype_end
|
70
|
+
end
|
71
|
+
|
72
|
+
def tag_start(name, attrs)
|
73
|
+
element = Element.new name
|
74
|
+
attrs.each{|k, v| element[k] = v.gsub('"', '"').inspect}
|
75
|
+
@current << element
|
76
|
+
@current = element
|
77
|
+
end
|
78
|
+
|
79
|
+
def tag_end(name)
|
80
|
+
if @current.name == name.to_sym
|
81
|
+
@current = @current.parent
|
82
|
+
else
|
83
|
+
raise RuntimeError.new("unmatch end-tag: #{name.inspect}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def text(content)
|
88
|
+
@current.body = content
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Document
|
93
|
+
attr_accessor :xml_dec, :doc_type, :root
|
94
|
+
|
95
|
+
def initialize(filename=nil)
|
96
|
+
@root = Element.new
|
97
|
+
@filename = filename
|
98
|
+
if @filename =~ /^<.*$/
|
99
|
+
REXML::Document.parse_stream StringIO.new(filename), EasyListener.new(self)
|
100
|
+
else
|
101
|
+
REXML::Document.parse_stream File.new(filename), EasyListener.new(self) if @filename
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def copy
|
106
|
+
copied = self.class.new
|
107
|
+
copied.xml_dec = @xml_dec
|
108
|
+
copied.doc_type = @doc_type
|
109
|
+
copied.root = @root.copy
|
110
|
+
copied
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
<<-eos
|
115
|
+
#{@xml_dec}
|
116
|
+
#{@doc_type}
|
117
|
+
#{@root.children.first.to_s}
|
118
|
+
eos
|
119
|
+
end
|
120
|
+
|
121
|
+
def method_missing(name, *args, &block)
|
122
|
+
@root.send name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class Element
|
127
|
+
attr_accessor :parent, :children, :name, :body, :attributes
|
128
|
+
|
129
|
+
def initialize(name=nil)
|
130
|
+
@parent = nil
|
131
|
+
@name = name.to_sym if name
|
132
|
+
@body = ''
|
133
|
+
@attributes = HashWithIndifferentAccess.new
|
134
|
+
@children = []
|
135
|
+
end
|
136
|
+
|
137
|
+
def body=(content)
|
138
|
+
if @body =~ /^\s+$/m
|
139
|
+
@body = ''
|
140
|
+
else
|
141
|
+
@body = content
|
142
|
+
end
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def []=(key, val)
|
147
|
+
val = $1 if val =~ /^"(.*)"$/ or val =~ /^"(.*)"$/ #TODO
|
148
|
+
@attributes[key.to_s] = val
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
def [](key)
|
153
|
+
@attributes[key.to_s]
|
154
|
+
end
|
155
|
+
|
156
|
+
def <<(element, option=nil)
|
157
|
+
case option
|
158
|
+
when NilClass
|
159
|
+
@children << element
|
160
|
+
when Hash
|
161
|
+
if option[:after]
|
162
|
+
@children.insert @children.index(option[:after]) + 1, element
|
163
|
+
elsif option[:before]
|
164
|
+
@children.insert @children.index(option[:before]), element
|
165
|
+
end
|
166
|
+
when String, Symbol
|
167
|
+
case option.to_sym
|
168
|
+
when :top
|
169
|
+
@children.unshift element
|
170
|
+
when :bottom
|
171
|
+
@children << element
|
172
|
+
else
|
173
|
+
raise ArgumentError.new('invalid argument')
|
174
|
+
end
|
175
|
+
else
|
176
|
+
raise ArgumentError.new('invalid argument')
|
177
|
+
end
|
178
|
+
element.parent = self
|
179
|
+
end
|
180
|
+
|
181
|
+
def prune_children(num)
|
182
|
+
@children = @children[0...num]
|
183
|
+
end
|
184
|
+
|
185
|
+
def remove
|
186
|
+
@parent.children.delete self
|
187
|
+
end
|
188
|
+
|
189
|
+
def each(&block)
|
190
|
+
@children.each &block
|
191
|
+
end
|
192
|
+
|
193
|
+
def each_attribute(&block)
|
194
|
+
@attributes.each &block
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_s(indent=0)
|
198
|
+
"#{"\t" * indent}<#{@name}" +
|
199
|
+
unless @attributes.empty?
|
200
|
+
"\n#{@attributes.to_a.map{|k, v| "#{"\t" * (indent + 1)} #{k}=#{v.inspect}"}.join("\n")}"
|
201
|
+
else
|
202
|
+
''
|
203
|
+
end +
|
204
|
+
if @children.empty? and @body.empty?
|
205
|
+
" />\n"
|
206
|
+
elsif !@body.empty? and @children.empty?
|
207
|
+
">#{@body}\n#{"\t" * indent}</#{@name}>\n"
|
208
|
+
else
|
209
|
+
">\n#{@children.map{|c| c.to_s(indent + 1)}.join}#{"\t" * indent}</#{@name}>\n"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def copy
|
214
|
+
copied = self.class.new @name
|
215
|
+
copied.body = @body
|
216
|
+
copied.attributes = HashWithIndifferentAccess[@attributes]
|
217
|
+
@children.each do |c|
|
218
|
+
copied << c
|
219
|
+
end
|
220
|
+
copied
|
221
|
+
end
|
222
|
+
|
223
|
+
def method_missing(name, *args, &block)
|
224
|
+
if name == :each_with_index
|
225
|
+
[self].each_with_index &block
|
226
|
+
return
|
227
|
+
end
|
228
|
+
children = @children.select {|c| c.name == name.to_sym}
|
229
|
+
if children.empty?
|
230
|
+
name = name.to_s.sub(/^(.*)_$/, '\1').to_sym
|
231
|
+
children = @children.select {|c| c.name == name.to_sym}
|
232
|
+
end
|
233
|
+
unless block.nil?
|
234
|
+
children = children.select &block
|
235
|
+
end
|
236
|
+
if children.empty?
|
237
|
+
#puts "<#{name}> does not exist in <#{@name}>."
|
238
|
+
raise NameError.new(name)
|
239
|
+
elsif children.size == 1
|
240
|
+
children.first
|
241
|
+
else
|
242
|
+
children
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
data/test/test.xml
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'lib/xml_easy'
|
3
|
+
|
4
|
+
class XmlEasyTest < Test::Unit::TestCase
|
5
|
+
TEST_XML = "#{File.dirname(__FILE__)}/test.xml"
|
6
|
+
def setup
|
7
|
+
@xml = XmlEasy::Document.new TEST_XML
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_new_doc_with_string
|
11
|
+
xml = XmlEasy::Document.new <<-target
|
12
|
+
<?xml version="1.0"?>
|
13
|
+
<root attr="new with string">
|
14
|
+
<child1></child1>
|
15
|
+
<child2 attr1="val1"></child2>
|
16
|
+
</root>
|
17
|
+
target
|
18
|
+
|
19
|
+
assert_element <<-expected, xml
|
20
|
+
<?xml version='1.0'?>
|
21
|
+
|
22
|
+
<root
|
23
|
+
attr="new with string">
|
24
|
+
<child1 />
|
25
|
+
<child2
|
26
|
+
attr1="val1" />
|
27
|
+
</root>
|
28
|
+
expected
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_access_to_child
|
32
|
+
assert_equal "<child1 />\n", @xml.root_.child1.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_insert_after
|
36
|
+
c1 = @xml.root_.child1
|
37
|
+
c2 = @xml.root_.child2
|
38
|
+
|
39
|
+
c21 = c2.copy
|
40
|
+
c21[:attr1] = 'val11'
|
41
|
+
c1.parent.<< c21, :after => c2
|
42
|
+
assert_element <<-expected, @xml
|
43
|
+
<?xml version='1.0'?>
|
44
|
+
|
45
|
+
<root>
|
46
|
+
<child1 />
|
47
|
+
<child2
|
48
|
+
attr1="val1" />
|
49
|
+
<child2
|
50
|
+
attr1="val11" />
|
51
|
+
</root>
|
52
|
+
expected
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_insert_before
|
56
|
+
c1 = @xml.root_.child1
|
57
|
+
c2 = @xml.root_.child2
|
58
|
+
|
59
|
+
c22 = c2.copy
|
60
|
+
c22[:attr1] = 'val12'
|
61
|
+
c1.parent.<< c22, :before => c2
|
62
|
+
assert_element <<-expected, @xml
|
63
|
+
<?xml version='1.0'?>
|
64
|
+
|
65
|
+
<root>
|
66
|
+
<child1 />
|
67
|
+
<child2
|
68
|
+
attr1="val12" />
|
69
|
+
<child2
|
70
|
+
attr1="val1" />
|
71
|
+
</root>
|
72
|
+
expected
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_insert_top
|
76
|
+
c1 = @xml.root_.child1
|
77
|
+
c2 = @xml.root_.child2
|
78
|
+
|
79
|
+
c23 = c2.copy
|
80
|
+
c23[:attr1] = 'val13'
|
81
|
+
c1.parent.<< c23, :top
|
82
|
+
assert_element <<-expected, @xml
|
83
|
+
<?xml version='1.0'?>
|
84
|
+
|
85
|
+
<root>
|
86
|
+
<child2
|
87
|
+
attr1="val13" />
|
88
|
+
<child1 />
|
89
|
+
<child2
|
90
|
+
attr1="val1" />
|
91
|
+
</root>
|
92
|
+
expected
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_insert_bottom
|
96
|
+
c1 = @xml.root_.child1
|
97
|
+
c2 = @xml.root_.child2
|
98
|
+
|
99
|
+
c24 = c2.copy
|
100
|
+
c24[:attr1] = 'val14'
|
101
|
+
c1.parent.<< c24, :bottom
|
102
|
+
assert_element <<-expected, @xml
|
103
|
+
<?xml version='1.0'?>
|
104
|
+
|
105
|
+
<root>
|
106
|
+
<child1 />
|
107
|
+
<child2
|
108
|
+
attr1="val1" />
|
109
|
+
<child2
|
110
|
+
attr1="val14" />
|
111
|
+
</root>
|
112
|
+
expected
|
113
|
+
end
|
114
|
+
|
115
|
+
protected
|
116
|
+
|
117
|
+
def assert_element(expected, element)
|
118
|
+
assert_equal expected.strip, element.to_s.strip.gsub("\t", ' ')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: XmlEasy
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-12-08 00:00:00 +09:00
|
8
|
+
summary: handle xml easily
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: andyjpn@gmail.com
|
12
|
+
homepage: " by ANDO Yasushi"
|
13
|
+
rubyforge_project: xml_easy
|
14
|
+
description: "ruby-easy offers you a way to handle xml quite easily. == FEATURES/PROBLEMS: * handle xml == SYNOPSIS: xml_easy = XmlEasy::Document.new 'text.html'"
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- ANDO Yasushi
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- bin/xml_easy
|
37
|
+
- lib/xml_easy.rb
|
38
|
+
- test/test_xml_easy.rb
|
39
|
+
- test/test.xml
|
40
|
+
test_files:
|
41
|
+
- test/test_xml_easy.rb
|
42
|
+
rdoc_options:
|
43
|
+
- --main
|
44
|
+
- README.txt
|
45
|
+
extra_rdoc_files:
|
46
|
+
- History.txt
|
47
|
+
- Manifest.txt
|
48
|
+
- README.txt
|
49
|
+
executables:
|
50
|
+
- xml_easy
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
dependencies:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: hoe
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.3.0
|
64
|
+
version:
|