XmlEasy 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|