kind_dom 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- KindDom provides graceful access to the the DOM of an XML document, while preserving the original libxml behavior of the parsed document.
1
+ KindDom: gracefully use XML content with node-type neutrality, content closure & default values.
2
2
 
3
3
  See the source code comments, rdoc or ri for usage details; the tests also demonstrate use cases.
4
4
 
data/lib/kind_dom/base.rb CHANGED
@@ -1,15 +1,14 @@
1
- require 'kind_dom/object_proxy'
2
1
  require 'xml/libxml'
3
2
 
4
3
  module KindDom
5
4
 
6
5
  # KindDom provides graceful access to the the DOM of an XML document using
7
- # three methods of Kindness:
6
+ # three methods of kindness:
8
7
  # - <tt>#collection_of</tt> to select a collection of nodes
9
8
  # - <tt>#first_of</tt> to select one node
10
9
  # - <tt>#content_for</tt> to get node content.
11
10
  #
12
- # The original libxml behavior of the parsed document is preserved.
11
+ # The original libxml behavior of the XML document is preserved through the #dom accessor.
13
12
  #
14
13
  # As a contrived example, in the controller:
15
14
  # @results = KindDom::Base.new(xml_data)
@@ -31,105 +30,102 @@ module KindDom
31
30
  # <% end -%>
32
31
  # <% end -%>
33
32
  #
34
- class Base < ObjectProxy
33
+ class Base
35
34
 
36
- attr_accessor :dom
35
+ attr_reader :dom
37
36
 
38
37
  # A new KindDom object may be created from raw XML [string] data, or
39
- # an already instantiated XML::Node or XML::Document.
38
+ # an already instantiated KindDom::Base, XML::Node or XML::Document.
40
39
  #
41
40
  def initialize(xml_in=nil)
42
- unless xml_in.blank?
41
+ unless xml_in.nil?
43
42
  case
43
+ when xml_in.kind_of?(KindDom::Base)
44
+ @dom = xml_in.dom
45
+ when xml_in.kind_of?(XML::Document), xml_in.kind_of?(XML::Node)
46
+ @dom = xml_in
44
47
  when xml_in.kind_of?(String)
45
48
  parser = XML::Parser.new
46
49
  parser.string = xml_in
47
50
  @dom = parser.parse
48
- when xml_in.kind_of?(XML::Document), xml_in.kind_of?(XML::Node)
49
- @dom = xml_in
50
51
  end
51
52
  end
52
53
  rescue XML::Parser::ParseError
54
+ end
55
+
56
+ # Retrieve the contents of the first node or attribute selected by the XPath;
57
+ # defaults to the current node, "."
58
+ #
59
+ # Optional second argument is the default value to return if the DOM find fails.
60
+ #
61
+ # When a block is provided, it will be called with the found content
62
+ # (or default) before returning.
63
+ #
64
+ def content_for(xpath='.', default=nil) # :yields: found_content
65
+ node = case @dom.class.to_s
66
+ when 'XML::Document' then
67
+ @dom.root.find_first(xpath)
68
+ else # 'XML::Node'
69
+ @dom.find_first(xpath)
70
+ end
71
+ content = case node.class.to_s
72
+ when 'XML::Attr' then
73
+ node.value
74
+ else
75
+ node.content
76
+ end
77
+ rescue NoMethodError
53
78
  ensure
54
- @dom.extend Kindness
55
- super(@dom) # Proxy all method calls to @dom.
79
+ content = content.blank? ? default : content
80
+ if block_given? and !content.blank?
81
+ return yield(content)
82
+ else
83
+ return content
84
+ end
56
85
  end
57
86
 
58
- module Kindness
59
- # Retrieve the contents of the first node or attribute selected by the XPath.
60
- #
61
- # Optional second argument is the default value to return if the DOM find fails.
62
- #
63
- # When a block is provided, it will be called with the found content
64
- # (or default) before returning.
65
- #
66
- def content_for(xpath, default=nil) # :yields: found_content
67
- node = case self.class.to_s
68
- when 'XML::Document' then
69
- root.find_first(xpath)
70
- else # 'XML::Node'
71
- find_first(xpath)
72
- end
73
- content = case node.class.to_s
74
- when 'XML::Attr' then
75
- node.value
76
- else
77
- node.content
78
- end
79
- rescue NoMethodError
80
- ensure
81
- content = content.blank? ? default : content
82
- if block_given? and !content.blank?
83
- return yield(content)
84
- else
85
- return content
86
- end
87
+ # Retrieve a collection (Array) of DOM nodes selected by the XPath.
88
+ #
89
+ # Each node is returned as KindDom to support #content_for, #collection_of & #first_of.
90
+ #
91
+ # When a block is provided, it will be called with the found collection
92
+ # (or default) before returning.
93
+ #
94
+ def collection_of(xpath, default=nil) # :yields: found_collection
95
+ c = @dom.find(xpath).collect {|n| self.class.new(n) }
96
+ rescue NoMethodError
97
+ ensure
98
+ c = c.blank?||c.size<1 ? default : c
99
+ if block_given? and !c.nil?
100
+ return yield(c)
101
+ else
102
+ return c
87
103
  end
88
-
89
- # Retrieve a collection (Array) of DOM nodes selected by the XPath.
90
- #
91
- # Each node is extended with Kindness to support #content_for, #collection_of & #first_of.
92
- #
93
- # When a block is provided, it will be called with the found collection
94
- # (or default) before returning.
95
- #
96
- def collection_of(xpath, default=nil) # :yields: found_collection
97
- c = find(xpath).collect {|n| n.extend Kindness }
98
- rescue NoMethodError
99
- ensure
100
- c = c.blank?||c.size<1 ? default : c
101
- if block_given? and !c.nil?
102
- return yield(c)
103
- else
104
- return c
105
- end
104
+ end
105
+
106
+ # Retrieve the first DOM node selected by the XPath.
107
+ #
108
+ # The node is returned as KindDom to support #content_for, #collection_of & #first_of.
109
+ #
110
+ # When a block is provided, it will be called with the found node
111
+ # (or default) before returning.
112
+ #
113
+ def first_of(xpath, default=nil) # :yields: found_node
114
+ n = case @dom.class.to_s
115
+ when 'XML::Document' then
116
+ @dom.root.find_first(xpath)
117
+ else # 'XML::Node'
118
+ @dom.find_first(xpath)
106
119
  end
107
-
108
- # Retrieve the first DOM node selected by the XPath.
109
- #
110
- # The node is extended with Kindness to support #content_for, #collection_of & #first_of.
111
- #
112
- # When a block is provided, it will be called with the found node
113
- # (or default) before returning.
114
- #
115
- def first_of(xpath, default=nil) # :yields: found_node
116
- n = case self.class.to_s
117
- when 'XML::Document' then
118
- root.find_first(xpath)
119
- else # 'XML::Node'
120
- find_first(xpath)
121
- end
122
- n.extend Kindness
123
- rescue NoMethodError
124
- ensure
125
- n = n.blank? ? default : n
126
- if block_given? and !n.blank?
127
- return yield(n)
128
- else
129
- return n
130
- end
120
+ rescue NoMethodError
121
+ ensure
122
+ n = n.blank? ? default : self.class.new(n)
123
+ if block_given? and !n.blank?
124
+ return yield(n)
125
+ else
126
+ return n
131
127
  end
132
128
  end
133
129
 
134
130
  end
135
- end
131
+ end
@@ -3,102 +3,105 @@ require File.dirname(__FILE__) + '/../lib/kind_dom'
3
3
 
4
4
  class KindDomTest < Test::Unit::TestCase
5
5
 
6
- @@test_dom = KindDom::Base.new(File.read(File.dirname(__FILE__) + '/flickr_rss.xml'))
6
+ @@test_doc = KindDom::Base.new(File.read(File.dirname(__FILE__) + '/flickr_rss.xml'))
7
7
 
8
8
  def test_initialization_from_raw_xml
9
- assert_kind_of XML::Document, @@test_dom
10
- assert @@test_dom.respond_to?(:content_for)
9
+ assert_kind_of KindDom::Base, @@test_doc
10
+ assert_kind_of XML::Document, @@test_doc.dom
11
+ assert @@test_doc.respond_to?(:content_for)
11
12
  end
12
13
 
13
14
  def test_initialization_from_xml_document
14
15
  parsed = parse_xml File.read(File.dirname(__FILE__) + '/flickr_rss.xml')
15
16
  k = KindDom::Base.new(parsed)
16
- assert_kind_of XML::Document, k
17
+ assert_kind_of KindDom::Base, k
18
+ assert_kind_of XML::Document, k.dom
17
19
  assert k.respond_to?(:content_for)
18
20
  end
19
21
 
20
22
  def test_initialization_from_xml_node
21
23
  parsed = parse_xml File.read(File.dirname(__FILE__) + '/flickr_rss.xml')
22
24
  k = KindDom::Base.new(parsed.root.find_first('channel/item'))
23
- assert_kind_of XML::Node, k
25
+ assert_kind_of KindDom::Base, k
26
+ assert_kind_of XML::Node, k.dom
24
27
  assert k.respond_to?(:content_for)
25
28
  end
26
29
 
27
30
 
28
31
  def test_content_for__success
29
- assert_equal 'Photos from marsi', @@test_dom.content_for('channel/title')
32
+ assert_equal 'Photos from marsi', @@test_doc.content_for('channel/title')
30
33
  end
31
34
 
32
35
  def test_content_for__success_with_closure
33
- assert_equal 'PHOTOS FROM MARSI', @@test_dom.content_for('channel/title') {|content| content.upcase}
36
+ assert_equal 'PHOTOS FROM MARSI', @@test_doc.content_for('channel/title') {|content| content.upcase}
34
37
  end
35
38
 
36
39
  def test_content_for__default
37
- assert_equal 'a flickr stream', @@test_dom.content_for('//blank_or_non_existent', 'a flickr stream')
40
+ assert_equal 'a flickr stream', @@test_doc.content_for('//blank_or_non_existent', 'a flickr stream')
38
41
  end
39
42
 
40
43
  def test_content_for__default_with_closure
41
- assert_equal 'A FLICKR STREAM', @@test_dom.content_for('//blank_or_non_existent', 'a flickr stream') {|content| content.upcase}
44
+ assert_equal 'A FLICKR STREAM', @@test_doc.content_for('//blank_or_non_existent', 'a flickr stream') {|content| content.upcase}
42
45
  end
43
46
 
44
47
  def test_content_for__failure
45
- assert_nil @@test_dom.content_for('//blank_or_non_existent')
48
+ assert_nil @@test_doc.content_for('//blank_or_non_existent')
46
49
  end
47
50
 
48
51
  def test_content_for__failure_with_closure
49
- assert_nil @@test_dom.content_for('//blank_or_non_existent') {|content| 'this closure is not called'}
52
+ assert_nil @@test_doc.content_for('//blank_or_non_existent') {|content| 'this closure is not called'}
50
53
  end
51
54
 
52
55
 
53
56
  def test_collection_of__success
54
- items = @@test_dom.collection_of('channel/item')
57
+ items = @@test_doc.collection_of('channel/item')
55
58
  assert_kind_of Enumerable, items
56
59
  assert 20 == items.size
57
60
  end
58
61
 
59
62
  def test_collection_of__success_with_closure
60
- items = @@test_dom.collection_of('channel/item') {|collection| collection[0..-2]}
63
+ items = @@test_doc.collection_of('channel/item') {|collection| collection[0..-2]}
61
64
  assert_kind_of Enumerable, items
62
65
  assert 19 == items.size
63
66
  end
64
67
 
65
68
  def test_collection_of__default
66
- items = @@test_dom.collection_of('//blank_or_non_existent', ['A lonely default item'])
69
+ items = @@test_doc.collection_of('//blank_or_non_existent', ['A lonely default item'])
67
70
  assert_kind_of Enumerable, items
68
71
  assert 1 == items.size
69
72
  end
70
73
 
71
74
  def test_collection_of__default_with_closure
72
- items = @@test_dom.collection_of('//blank_or_non_existent', Array.new) {|collection| collection << 'I was empty' }
75
+ items = @@test_doc.collection_of('//blank_or_non_existent', Array.new) {|collection| collection << 'I was empty' }
73
76
  assert_kind_of Enumerable, items
74
77
  assert 1 == items.size
75
78
  end
76
79
 
77
80
  def test_collection_of__failure
78
- assert_nil @@test_dom.collection_of('//blank_or_non_existent')
81
+ assert_nil @@test_doc.collection_of('//blank_or_non_existent')
79
82
  end
80
83
 
81
84
  def test_collection_of__failure_with_closure
82
- assert_nil @@test_dom.collection_of('//blank_or_non_existent') {|collection| 'this closure is not called'}
85
+ assert_nil @@test_doc.collection_of('//blank_or_non_existent') {|collection| 'this closure is not called'}
83
86
  end
84
87
 
85
88
 
86
89
  def test_first_of__success
87
- assert_kind_of XML::Node, @@test_dom.first_of('channel/item')
90
+ assert_kind_of KindDom::Base, @@test_doc.first_of('channel/item')
88
91
  end
89
92
 
90
93
  def test_first_of__success_with_closure
91
- n = @@test_dom.first_of('channel/item') {|node| node.parent}
92
- assert_kind_of XML::Node, n
93
- assert_equal 'channel', n.name
94
+ n = @@test_doc.first_of('channel/item') {|node| node.first_of('title') }
95
+ assert_kind_of KindDom::Base, n
96
+ assert_equal 'Alaya Cove', n.content_for
94
97
  end
95
98
 
96
99
  def test_first_of__failure
97
- assert_nil @@test_dom.first_of('//blank_or_non_existent')
100
+ assert_nil @@test_doc.first_of('//blank_or_non_existent')
98
101
  end
99
102
 
100
103
  def test_first_of__failure_with_closure
101
- assert_nil @@test_dom.first_of('//blank_or_non_existent') {|node| 'this closure is not called'}
104
+ assert_nil @@test_doc.first_of('//blank_or_non_existent') {|node| 'this closure is not called'}
102
105
  end
103
106
 
104
107
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind_dom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mars Hall
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-02 00:00:00 -07:00
12
+ date: 2008-07-05 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -41,7 +41,6 @@ extra_rdoc_files:
41
41
  files:
42
42
  - lib/kind_dom
43
43
  - lib/kind_dom/base.rb
44
- - lib/kind_dom/object_proxy.rb
45
44
  - lib/kind_dom.rb
46
45
  - test/flickr_rss.xml
47
46
  - test/kind_dom_test.rb
@@ -1,31 +0,0 @@
1
- module KindDom
2
-
3
- # The foundation of an object proxy is the BlankSlate.
4
- #
5
- # BlankSlate is included with Rails ActiveSupport
6
- # as active_support/vendor/builder-2.1.2/blankslate.rb
7
- #
8
- # From the Rails rdoc: "BlankSlate provides an abstract base class with
9
- # no predefined methods (except for __send__ and __id__). BlankSlate is useful
10
- # as a base class when writing classes that depend upon method_missing
11
- # (e.g. dynamic proxies)."
12
- #
13
- # ObjectProxy is a bare-bones proxy. Override #method_missing in your subclass
14
- # to handle method calls in the proxy.
15
- #
16
- class ObjectProxy < BlankSlate
17
-
18
- # Create a proxy to the provided object.
19
- def initialize(target)
20
- @target = target
21
- end
22
-
23
- # Override #method_missing in your subclass
24
- # to handle method calls in the proxy; call `super(sym, *args, &block)`
25
- # therein to get a response from the proxied object.
26
- def method_missing(sym, *args, &block)
27
- @target.__send__(sym, *args, &block)
28
- end
29
- end
30
-
31
- end