XmlEasy 0.0.1 → 0.0.2
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/Manifest.txt +3 -1
- data/examples/xhtml_scrape.rb +14 -0
- data/lib/xml_easy.rb +98 -48
- data/test/test_xml_easy.rb +99 -15
- data/test/xml/complex.xml +1 -0
- data/test/{test.xml → xml/simple.xml} +0 -0
- metadata +5 -3
data/Manifest.txt
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'lib/xml_easy'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
xhtml_spec =
|
5
|
+
open 'http://www.w3.org/TR/xhtml1/' do |f|
|
6
|
+
XmlEasy::Document.new f.read
|
7
|
+
end
|
8
|
+
|
9
|
+
puts <<-EOS
|
10
|
+
Title:
|
11
|
+
#{xhtml_spec.html.head.title.body}
|
12
|
+
Subtitles:
|
13
|
+
#{xhtml_spec.html.body_.h2.map{|h2| " #{h2.body.strip}"}.join("\n")}
|
14
|
+
EOS
|
data/lib/xml_easy.rb
CHANGED
@@ -5,33 +5,27 @@ require 'rexml/parsers/baseparser'
|
|
5
5
|
require 'rexml/streamlistener'
|
6
6
|
|
7
7
|
class Array
|
8
|
-
def
|
9
|
-
|
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
|
8
|
+
def to_element_list
|
9
|
+
inject(XmlEasy::ElementList.new) {|s, e| s << e}
|
18
10
|
end
|
19
11
|
end
|
20
12
|
|
21
13
|
class HashWithIndifferentAccess < Hash
|
22
|
-
def
|
23
|
-
|
14
|
+
def [](key)
|
15
|
+
super(key.to_s) || super(key.to_sym)
|
24
16
|
end
|
25
|
-
alias get_with_indifferent_access []
|
26
17
|
|
27
|
-
def
|
28
|
-
|
18
|
+
def []=(key, val)
|
19
|
+
super(key.to_s, val)
|
29
20
|
end
|
30
|
-
alias set_with_indifferent_access []=
|
31
21
|
end
|
32
22
|
|
33
23
|
class XmlEasy
|
34
|
-
VERSION = '0.0.
|
24
|
+
VERSION = '0.0.2'
|
25
|
+
|
26
|
+
def self.read(filename=nil)
|
27
|
+
Document.new filename
|
28
|
+
end
|
35
29
|
|
36
30
|
class EasyListener
|
37
31
|
include REXML::StreamListener
|
@@ -41,12 +35,14 @@ class XmlEasy
|
|
41
35
|
end
|
42
36
|
|
43
37
|
def attlistdecl(elm_name, attrs, content); end
|
44
|
-
def cdata(content); end
|
45
38
|
def comment(content); end
|
46
39
|
def elementdecl(content); end
|
47
40
|
def entity(content); end
|
48
41
|
def instruction(name, inst); end
|
49
42
|
def notationdecl(content); end
|
43
|
+
def doctype(name, pub_sys, long_name, uri); end
|
44
|
+
def entitydecl(content); end
|
45
|
+
def doctype_end; end
|
50
46
|
|
51
47
|
def xmldecl(version, encoding, standalone)
|
52
48
|
@doc.xml_dec = "<?xml#{
|
@@ -58,17 +54,6 @@ class XmlEasy
|
|
58
54
|
}?>"
|
59
55
|
end
|
60
56
|
|
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
57
|
def tag_start(name, attrs)
|
73
58
|
element = Element.new name
|
74
59
|
attrs.each{|k, v| element[k] = v.gsub('"', '"').inspect}
|
@@ -87,6 +72,10 @@ class XmlEasy
|
|
87
72
|
def text(content)
|
88
73
|
@current.body = content
|
89
74
|
end
|
75
|
+
|
76
|
+
def cdata(content)
|
77
|
+
#TODO
|
78
|
+
end
|
90
79
|
end
|
91
80
|
|
92
81
|
class Document
|
@@ -94,12 +83,27 @@ class XmlEasy
|
|
94
83
|
|
95
84
|
def initialize(filename=nil)
|
96
85
|
@root = Element.new
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
86
|
+
io =
|
87
|
+
case filename
|
88
|
+
when nil; nil
|
89
|
+
when IO; filename
|
90
|
+
when /^<.*$/; StringIO.new filename
|
91
|
+
else; File.new filename
|
92
|
+
end
|
93
|
+
REXML::Document.parse_stream io, EasyListener.new(self) if io
|
94
|
+
end
|
95
|
+
|
96
|
+
def <<(root)
|
97
|
+
raise RuntimeError.new('root element must be uniq.') unless @root.children.empty?
|
98
|
+
root = "<#{root} />" if root.is_a? Symbol
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit(&block)
|
102
|
+
@root.visit &block
|
103
|
+
end
|
104
|
+
|
105
|
+
def collect(&block)
|
106
|
+
@root.collect &block
|
103
107
|
end
|
104
108
|
|
105
109
|
def copy
|
@@ -131,7 +135,7 @@ class XmlEasy
|
|
131
135
|
@name = name.to_sym if name
|
132
136
|
@body = ''
|
133
137
|
@attributes = HashWithIndifferentAccess.new
|
134
|
-
@children =
|
138
|
+
@children = ElementList.new
|
135
139
|
end
|
136
140
|
|
137
141
|
def body=(content)
|
@@ -178,10 +182,6 @@ class XmlEasy
|
|
178
182
|
element.parent = self
|
179
183
|
end
|
180
184
|
|
181
|
-
def prune_children(num)
|
182
|
-
@children = @children[0...num]
|
183
|
-
end
|
184
|
-
|
185
185
|
def remove
|
186
186
|
@parent.children.delete self
|
187
187
|
end
|
@@ -194,6 +194,23 @@ class XmlEasy
|
|
194
194
|
@attributes.each &block
|
195
195
|
end
|
196
196
|
|
197
|
+
def size
|
198
|
+
1
|
199
|
+
end
|
200
|
+
|
201
|
+
def each_with_index(&block)
|
202
|
+
[self].each_with_index &block
|
203
|
+
end
|
204
|
+
|
205
|
+
def visit(&block)
|
206
|
+
block.call self
|
207
|
+
@children.each{|c| c.visit &block}
|
208
|
+
end
|
209
|
+
|
210
|
+
def collect(&block)
|
211
|
+
@children.inject(block.call(self) ? [self] : []){|s, c| s << c.collect(&block)}.flatten
|
212
|
+
end
|
213
|
+
|
197
214
|
def to_s(indent=0)
|
198
215
|
"#{"\t" * indent}<#{@name}" +
|
199
216
|
unless @attributes.empty?
|
@@ -215,16 +232,12 @@ class XmlEasy
|
|
215
232
|
copied.body = @body
|
216
233
|
copied.attributes = HashWithIndifferentAccess[@attributes]
|
217
234
|
@children.each do |c|
|
218
|
-
copied << c
|
235
|
+
copied << c.copy
|
219
236
|
end
|
220
237
|
copied
|
221
238
|
end
|
222
239
|
|
223
240
|
def method_missing(name, *args, &block)
|
224
|
-
if name == :each_with_index
|
225
|
-
[self].each_with_index &block
|
226
|
-
return
|
227
|
-
end
|
228
241
|
children = @children.select {|c| c.name == name.to_sym}
|
229
242
|
if children.empty?
|
230
243
|
name = name.to_s.sub(/^(.*)_$/, '\1').to_sym
|
@@ -234,13 +247,50 @@ class XmlEasy
|
|
234
247
|
children = children.select &block
|
235
248
|
end
|
236
249
|
if children.empty?
|
237
|
-
|
238
|
-
|
250
|
+
msg = "<#{name}> does not exist in <#{@name}>."
|
251
|
+
puts msg if $xmleasy_debug or $debug
|
252
|
+
raise NameError.new(msg)
|
253
|
+
nil
|
239
254
|
elsif children.size == 1
|
240
255
|
children.first
|
241
256
|
else
|
242
|
-
children
|
257
|
+
children.to_element_list
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
class NilElement < Element
|
263
|
+
def size
|
264
|
+
0
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class ElementList < Array
|
269
|
+
def method_missing(name, *args, &block)
|
270
|
+
elms = []
|
271
|
+
errs = []
|
272
|
+
off_debug do
|
273
|
+
elms = map do |e|
|
274
|
+
begin; e.send(name, *args, &block)
|
275
|
+
rescue => e; errs << e and [] end
|
276
|
+
end.flatten
|
277
|
+
end
|
278
|
+
#puts errs.map{|e| e.message}.join("\n") if $xmleasy_debug or $debug
|
279
|
+
|
280
|
+
unless block.nil?
|
281
|
+
elms = elms.select &block
|
243
282
|
end
|
283
|
+
elms.to_element_list
|
284
|
+
end
|
285
|
+
|
286
|
+
protected
|
287
|
+
|
288
|
+
def off_debug(&block)
|
289
|
+
org = $xmleasy_debug
|
290
|
+
$xmleasy_debug = false
|
291
|
+
block.call
|
292
|
+
ensure
|
293
|
+
$xmleasy_debug = org
|
244
294
|
end
|
245
295
|
end
|
246
296
|
end
|
data/test/test_xml_easy.rb
CHANGED
@@ -2,9 +2,23 @@ require 'test/unit'
|
|
2
2
|
require 'lib/xml_easy'
|
3
3
|
|
4
4
|
class XmlEasyTest < Test::Unit::TestCase
|
5
|
-
|
5
|
+
XML_DIR = "#{File.dirname(__FILE__)}/xml"
|
6
|
+
|
6
7
|
def setup
|
7
|
-
@
|
8
|
+
@xml_simple = XmlEasy::Document.new "#{XML_DIR}/simple.xml"
|
9
|
+
@xml_complex = XmlEasy::Document.new "#{XML_DIR}/complex.xml"
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_copy
|
13
|
+
xml2 = @xml_simple.copy
|
14
|
+
|
15
|
+
@xml_simple.root_[:id] = 'original'
|
16
|
+
assert_equal 'original', @xml_simple.root_[:id]
|
17
|
+
assert_not_equal 'original', xml2.root_[:id]
|
18
|
+
|
19
|
+
@xml_simple.root_.child1[:id] = 'original'
|
20
|
+
assert_equal 'original', @xml_simple.root_.child1[:id]
|
21
|
+
assert_not_equal 'original', xml2.root_.child1[:id]
|
8
22
|
end
|
9
23
|
|
10
24
|
def test_new_doc_with_string
|
@@ -29,17 +43,17 @@ class XmlEasyTest < Test::Unit::TestCase
|
|
29
43
|
end
|
30
44
|
|
31
45
|
def test_access_to_child
|
32
|
-
assert_equal "<child1 />\n", @
|
46
|
+
assert_equal "<child1 />\n", @xml_simple.root_.child1.to_s
|
33
47
|
end
|
34
48
|
|
35
49
|
def test_insert_after
|
36
|
-
c1 = @
|
37
|
-
c2 = @
|
50
|
+
c1 = @xml_simple.root_.child1
|
51
|
+
c2 = @xml_simple.root_.child2
|
38
52
|
|
39
53
|
c21 = c2.copy
|
40
54
|
c21[:attr1] = 'val11'
|
41
55
|
c1.parent.<< c21, :after => c2
|
42
|
-
assert_element <<-expected, @
|
56
|
+
assert_element <<-expected, @xml_simple
|
43
57
|
<?xml version='1.0'?>
|
44
58
|
|
45
59
|
<root>
|
@@ -53,13 +67,13 @@ class XmlEasyTest < Test::Unit::TestCase
|
|
53
67
|
end
|
54
68
|
|
55
69
|
def test_insert_before
|
56
|
-
c1 = @
|
57
|
-
c2 = @
|
70
|
+
c1 = @xml_simple.root_.child1
|
71
|
+
c2 = @xml_simple.root_.child2
|
58
72
|
|
59
73
|
c22 = c2.copy
|
60
74
|
c22[:attr1] = 'val12'
|
61
75
|
c1.parent.<< c22, :before => c2
|
62
|
-
assert_element <<-expected, @
|
76
|
+
assert_element <<-expected, @xml_simple
|
63
77
|
<?xml version='1.0'?>
|
64
78
|
|
65
79
|
<root>
|
@@ -73,13 +87,13 @@ class XmlEasyTest < Test::Unit::TestCase
|
|
73
87
|
end
|
74
88
|
|
75
89
|
def test_insert_top
|
76
|
-
c1 = @
|
77
|
-
c2 = @
|
90
|
+
c1 = @xml_simple.root_.child1
|
91
|
+
c2 = @xml_simple.root_.child2
|
78
92
|
|
79
93
|
c23 = c2.copy
|
80
94
|
c23[:attr1] = 'val13'
|
81
95
|
c1.parent.<< c23, :top
|
82
|
-
assert_element <<-expected, @
|
96
|
+
assert_element <<-expected, @xml_simple
|
83
97
|
<?xml version='1.0'?>
|
84
98
|
|
85
99
|
<root>
|
@@ -93,13 +107,13 @@ class XmlEasyTest < Test::Unit::TestCase
|
|
93
107
|
end
|
94
108
|
|
95
109
|
def test_insert_bottom
|
96
|
-
c1 = @
|
97
|
-
c2 = @
|
110
|
+
c1 = @xml_simple.root_.child1
|
111
|
+
c2 = @xml_simple.root_.child2
|
98
112
|
|
99
113
|
c24 = c2.copy
|
100
114
|
c24[:attr1] = 'val14'
|
101
115
|
c1.parent.<< c24, :bottom
|
102
|
-
assert_element <<-expected, @
|
116
|
+
assert_element <<-expected, @xml_simple
|
103
117
|
<?xml version='1.0'?>
|
104
118
|
|
105
119
|
<root>
|
@@ -112,6 +126,76 @@ class XmlEasyTest < Test::Unit::TestCase
|
|
112
126
|
expected
|
113
127
|
end
|
114
128
|
|
129
|
+
def test_condition
|
130
|
+
xml = XmlEasy::Document.new <<-end_of_xml
|
131
|
+
<?xml version="1.0"?>
|
132
|
+
<root>
|
133
|
+
<child id="id"></child>
|
134
|
+
<child>
|
135
|
+
<grandchild></grandchild>
|
136
|
+
</child>
|
137
|
+
</root>
|
138
|
+
end_of_xml
|
139
|
+
|
140
|
+
$xmleasy_debug = true
|
141
|
+
assert_equal 2, xml.root_.child.size
|
142
|
+
assert_equal 1, xml.root_.child{|c| c[:id] == 'id'}.size
|
143
|
+
assert_equal 1, xml.root_.child_.grandchild{|c| c[:id].nil?}.size
|
144
|
+
assert_equal 0, xml.root_.child_.grandchild{|c| !c[:id].nil?}.size
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_array_to_element_list
|
148
|
+
ary = [XmlEasy::Element.new('name')]
|
149
|
+
assert_equal XmlEasy::ElementList, ary.to_element_list.class
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_construct
|
153
|
+
xml = XmlEasy::Document.new
|
154
|
+
xml << :root
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_visit
|
158
|
+
xml = XmlEasy.read <<-end_of_xml
|
159
|
+
<?xml version="1.0"?>
|
160
|
+
<root>
|
161
|
+
<child id="child"></child>
|
162
|
+
<child>
|
163
|
+
<grandchild id="grandchild"></grandchild>
|
164
|
+
</child>
|
165
|
+
</root>
|
166
|
+
end_of_xml
|
167
|
+
|
168
|
+
res = []
|
169
|
+
xml.visit do |e|
|
170
|
+
res << e.name unless e[:id].nil?
|
171
|
+
end
|
172
|
+
assert_equal [:child, :grandchild], res
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_collect
|
176
|
+
xml = XmlEasy.read <<-end_of_xml
|
177
|
+
<?xml version="1.0"?>
|
178
|
+
<root>
|
179
|
+
<child id="child"></child>
|
180
|
+
<child>
|
181
|
+
<grandchild id="grandchild"></grandchild>
|
182
|
+
</child>
|
183
|
+
</root>
|
184
|
+
end_of_xml
|
185
|
+
|
186
|
+
assert_equal [:child, :grandchild], xml.collect{|e| not e[:id].nil?}.map{|e| e.name}
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_hash_with_ia
|
190
|
+
hash = HashWithIndifferentAccess.new
|
191
|
+
hash['key1'] = :value1
|
192
|
+
hash[:key2] = :value2
|
193
|
+
assert_equal :value1, hash['key1']
|
194
|
+
assert_equal :value1, hash[:key1]
|
195
|
+
assert_equal :value2, hash['key2']
|
196
|
+
assert_equal :value2, hash[:key2]
|
197
|
+
end
|
198
|
+
|
115
199
|
protected
|
116
200
|
|
117
201
|
def assert_element(expected, element)
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0"?>
|
File without changes
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: XmlEasy
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-12-
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2007-12-09 00:00:00 +09:00
|
8
8
|
summary: handle xml easily
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -36,7 +36,9 @@ files:
|
|
36
36
|
- bin/xml_easy
|
37
37
|
- lib/xml_easy.rb
|
38
38
|
- test/test_xml_easy.rb
|
39
|
-
- test/
|
39
|
+
- test/xml/simple.xml
|
40
|
+
- test/xml/complex.xml
|
41
|
+
- examples/xhtml_scrape.rb
|
40
42
|
test_files:
|
41
43
|
- test/test_xml_easy.rb
|
42
44
|
rdoc_options:
|