XmlEasy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,4 +5,6 @@ Rakefile
5
5
  bin/xml_easy
6
6
  lib/xml_easy.rb
7
7
  test/test_xml_easy.rb
8
- test/test.xml
8
+ test/xml/simple.xml
9
+ test/xml/complex.xml
10
+ examples/xhtml_scrape.rb
@@ -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
@@ -5,33 +5,27 @@ require 'rexml/parsers/baseparser'
5
5
  require 'rexml/streamlistener'
6
6
 
7
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
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 get_with_indifferent_access(key)
23
- self[key.to_s] || self[key.to_sym]
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 set_with_indifferent_access(key, val)
28
- self[key.to_s] = val
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.1'
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 '&#38;#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('"', '&quot;').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
- @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
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
- #puts "<#{name}> does not exist in <#{@name}>."
238
- raise NameError.new(name)
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
@@ -2,9 +2,23 @@ require 'test/unit'
2
2
  require 'lib/xml_easy'
3
3
 
4
4
  class XmlEasyTest < Test::Unit::TestCase
5
- TEST_XML = "#{File.dirname(__FILE__)}/test.xml"
5
+ XML_DIR = "#{File.dirname(__FILE__)}/xml"
6
+
6
7
  def setup
7
- @xml = XmlEasy::Document.new TEST_XML
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", @xml.root_.child1.to_s
46
+ assert_equal "<child1 />\n", @xml_simple.root_.child1.to_s
33
47
  end
34
48
 
35
49
  def test_insert_after
36
- c1 = @xml.root_.child1
37
- c2 = @xml.root_.child2
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, @xml
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 = @xml.root_.child1
57
- c2 = @xml.root_.child2
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, @xml
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 = @xml.root_.child1
77
- c2 = @xml.root_.child2
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, @xml
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 = @xml.root_.child1
97
- c2 = @xml.root_.child2
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, @xml
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.1
7
- date: 2007-12-08 00:00:00 +09:00
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/test.xml
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: