kind_dom 0.9.6 → 0.9.7

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/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