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.
@@ -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: