kind_dom 0.9.3 → 0.9.4
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/lib/kind_dom/base.rb +126 -0
- data/lib/kind_dom/object_proxy.rb +31 -0
- data/lib/kind_dom.rb +7 -123
- data/test/kind_dom_test.rb +1 -1
- metadata +4 -10
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'kind_dom/object_proxy'
|
2
|
+
require 'xml/libxml'
|
3
|
+
|
4
|
+
module KindDom
|
5
|
+
|
6
|
+
# KindDom provides graceful access to the the DOM of an XML document using
|
7
|
+
# three methods of Kindness:
|
8
|
+
# - <tt>#collection_of</tt> to select a collection of nodes
|
9
|
+
# - <tt>#first_of</tt> to select one node
|
10
|
+
# - <tt>#content_for</tt> to get node content.
|
11
|
+
#
|
12
|
+
# The original libxml behavior of the parsed document is preserved.
|
13
|
+
#
|
14
|
+
# As a contrived example, in the controller:
|
15
|
+
# @results = KindDom.new(xml_data)
|
16
|
+
#
|
17
|
+
# In the view:
|
18
|
+
#
|
19
|
+
# <% @results.first_of('//item') do |item| -%>
|
20
|
+
# <p>(This block is only executed if `item` is found.)</p>
|
21
|
+
#
|
22
|
+
# <% item.content_for('title') do |title| -%>
|
23
|
+
# <h2><%=h title %></h2>
|
24
|
+
# <p>(This block is only executed if `title` has content.)</p>
|
25
|
+
# <% end -%>
|
26
|
+
#
|
27
|
+
# <% item.content_for('description') do |description| -%>
|
28
|
+
# <p><%=h description %></p>
|
29
|
+
# <p>(This block is only executed if `description` has content.)</p>
|
30
|
+
# <% end -%>
|
31
|
+
# <% end -%>
|
32
|
+
#
|
33
|
+
class Base < ObjectProxy
|
34
|
+
|
35
|
+
attr_accessor :dom
|
36
|
+
|
37
|
+
def initialize(raw_xml=nil)
|
38
|
+
unless raw_xml.blank?
|
39
|
+
parser = XML::Parser.new
|
40
|
+
parser.string = raw_xml
|
41
|
+
@dom = parser.parse
|
42
|
+
end
|
43
|
+
rescue XML::Parser::ParseError
|
44
|
+
ensure
|
45
|
+
@dom.extend Kindness
|
46
|
+
super(@dom) # Proxy all method calls to @dom.
|
47
|
+
end
|
48
|
+
|
49
|
+
module Kindness
|
50
|
+
# Retrieve the contents of the first node or attribute selected by the XPath.
|
51
|
+
#
|
52
|
+
# Optional second argument is the default value to return if the DOM find fails.
|
53
|
+
#
|
54
|
+
# When a block is provided, it will be called with the found content
|
55
|
+
# (or default) before returning.
|
56
|
+
#
|
57
|
+
def content_for(xpath, default=nil) # :yields: found_content
|
58
|
+
node = case self.class.to_s
|
59
|
+
when 'XML::Document' then
|
60
|
+
root.find_first(xpath)
|
61
|
+
else # 'XML::Node'
|
62
|
+
find_first(xpath)
|
63
|
+
end
|
64
|
+
content = case node.class.to_s
|
65
|
+
when 'XML::Attr' then
|
66
|
+
node.value
|
67
|
+
else
|
68
|
+
node.content
|
69
|
+
end
|
70
|
+
rescue NoMethodError
|
71
|
+
ensure
|
72
|
+
content = content.blank? ? default : content
|
73
|
+
if block_given? and !content.blank?
|
74
|
+
return yield(content)
|
75
|
+
else
|
76
|
+
return content
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Retrieve a collection (Array) of DOM nodes selected by the XPath.
|
81
|
+
#
|
82
|
+
# Each node is extended with Kindness to support #content_for, #collection_of & #first_of.
|
83
|
+
#
|
84
|
+
# When a block is provided, it will be called with the found collection
|
85
|
+
# (or default) before returning.
|
86
|
+
#
|
87
|
+
def collection_of(xpath, default=nil) # :yields: found_collection
|
88
|
+
c = find(xpath).collect {|n| n.extend Kindness }
|
89
|
+
rescue NoMethodError
|
90
|
+
ensure
|
91
|
+
c = c.blank?||c.size<1 ? default : c
|
92
|
+
if block_given? and !c.nil?
|
93
|
+
return yield(c)
|
94
|
+
else
|
95
|
+
return c
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Retrieve the first DOM node selected by the XPath.
|
100
|
+
#
|
101
|
+
# The node is extended with Kindness to support #content_for, #collection_of & #first_of.
|
102
|
+
#
|
103
|
+
# When a block is provided, it will be called with the found node
|
104
|
+
# (or default) before returning.
|
105
|
+
#
|
106
|
+
def first_of(xpath, default=nil) # :yields: found_node
|
107
|
+
n = case self.class.to_s
|
108
|
+
when 'XML::Document' then
|
109
|
+
root.find_first(xpath)
|
110
|
+
else # 'XML::Node'
|
111
|
+
find_first(xpath)
|
112
|
+
end
|
113
|
+
n.extend Kindness
|
114
|
+
rescue NoMethodError
|
115
|
+
ensure
|
116
|
+
n = n.blank? ? default : n
|
117
|
+
if block_given? and !n.blank?
|
118
|
+
return yield(n)
|
119
|
+
else
|
120
|
+
return n
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
data/lib/kind_dom.rb
CHANGED
@@ -1,125 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'xml/libxml'
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# - <tt>#collection_of</tt> to select a collection of nodes
|
9
|
-
# - <tt>#first_of</tt> to select one node
|
10
|
-
# - <tt>#content_for</tt> to get node content.
|
11
|
-
#
|
12
|
-
# The original libxml behavior of the parsed document is preserved.
|
13
|
-
#
|
14
|
-
# As a contrived example, in the controller:
|
15
|
-
# @results = KindDom.new(xml_data)
|
16
|
-
#
|
17
|
-
# In the view:
|
18
|
-
#
|
19
|
-
# <% @results.first_of('//item') do |item| -%>
|
20
|
-
# <p>(This block is only executed if `item` is found.)</p>
|
21
|
-
#
|
22
|
-
# <% item.content_for('title') do |title| -%>
|
23
|
-
# <h2><%=h title %></h2>
|
24
|
-
# <p>(This block is only executed if `title` has content.)</p>
|
25
|
-
# <% end -%>
|
26
|
-
#
|
27
|
-
# <% item.content_for('description') do |description| -%>
|
28
|
-
# <p><%=h description %></p>
|
29
|
-
# <p>(This block is only executed if `description` has content.)</p>
|
30
|
-
# <% end -%>
|
31
|
-
# <% end -%>
|
32
|
-
#
|
33
|
-
class KindDom < ObjectProxy
|
34
|
-
|
35
|
-
attr_accessor :dom
|
36
|
-
|
37
|
-
def initialize(raw_xml=nil)
|
38
|
-
unless raw_xml.nil?
|
39
|
-
parser = XML::Parser.new
|
40
|
-
parser.string = raw_xml
|
41
|
-
@dom = parser.parse
|
42
|
-
end
|
43
|
-
rescue XML::Parser::ParseError
|
44
|
-
ensure
|
45
|
-
@dom.extend Kindness
|
46
|
-
super(@dom) # Proxy all method calls to @dom.
|
47
|
-
end
|
48
|
-
|
49
|
-
module Kindness
|
50
|
-
# Retrieve the contents of the first node or attribute selected by the XPath.
|
51
|
-
#
|
52
|
-
# Optional second argument is the default value to return if the DOM find fails.
|
53
|
-
#
|
54
|
-
# When a block is provided, it will be called with the found content
|
55
|
-
# (or default) before returning.
|
56
|
-
#
|
57
|
-
def content_for(xpath, default=nil) # :yields: found_content
|
58
|
-
node = case self.class.to_s
|
59
|
-
when 'XML::Document' then
|
60
|
-
root.find_first(xpath)
|
61
|
-
else # 'XML::Node'
|
62
|
-
find_first(xpath)
|
63
|
-
end
|
64
|
-
content = case node.class.to_s
|
65
|
-
when 'XML::Attr' then
|
66
|
-
node.value
|
67
|
-
else
|
68
|
-
node.content
|
69
|
-
end
|
70
|
-
rescue NoMethodError
|
71
|
-
ensure
|
72
|
-
content = content.blank? ? default : content
|
73
|
-
if block_given? and !content.blank?
|
74
|
-
return yield(content)
|
75
|
-
else
|
76
|
-
return content
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Retrieve a collection (Array) of DOM nodes selected by the XPath.
|
81
|
-
#
|
82
|
-
# Each node is extended with Kindness to support #content_for, #collection_of & #first_of.
|
83
|
-
#
|
84
|
-
# When a block is provided, it will be called with the found collection
|
85
|
-
# (or default) before returning.
|
86
|
-
#
|
87
|
-
def collection_of(xpath, default=nil) # :yields: found_collection
|
88
|
-
c = find(xpath).collect {|n| n.extend Kindness }
|
89
|
-
rescue NoMethodError
|
90
|
-
ensure
|
91
|
-
c = c.blank?||c.size<1 ? default : c
|
92
|
-
if block_given? and !c.nil?
|
93
|
-
return yield(c)
|
94
|
-
else
|
95
|
-
return c
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Retrieve the first DOM node selected by the XPath.
|
100
|
-
#
|
101
|
-
# The node is extended with Kindness to support #content_for, #collection_of & #first_of.
|
102
|
-
#
|
103
|
-
# When a block is provided, it will be called with the found node
|
104
|
-
# (or default) before returning.
|
105
|
-
#
|
106
|
-
def first_of(xpath, default=nil) # :yields: found_node
|
107
|
-
n = case self.class.to_s
|
108
|
-
when 'XML::Document' then
|
109
|
-
root.find_first(xpath)
|
110
|
-
else # 'XML::Node'
|
111
|
-
find_first(xpath)
|
112
|
-
end
|
113
|
-
n.extend Kindness
|
114
|
-
rescue NoMethodError
|
115
|
-
ensure
|
116
|
-
n = n.blank? ? default : n
|
117
|
-
if block_given? and !n.blank?
|
118
|
-
return yield(n)
|
119
|
-
else
|
120
|
-
return n
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
4
|
+
unless defined? ActiveSupport
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active_support'
|
125
7
|
end
|
8
|
+
|
9
|
+
require 'kind_dom/base'
|
data/test/kind_dom_test.rb
CHANGED
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../lib/kind_dom'
|
|
3
3
|
|
4
4
|
class KindDomTest < Test::Unit::TestCase
|
5
5
|
|
6
|
-
@@test_dom = KindDom.new(File.read(File.dirname(__FILE__) + '/flickr_rss.xml'))
|
6
|
+
@@test_dom = KindDom::Base.new(File.read(File.dirname(__FILE__) + '/flickr_rss.xml'))
|
7
7
|
|
8
8
|
def test_dom_initialization
|
9
9
|
assert_kind_of XML::Document, @@test_dom
|
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.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mars Hall
|
@@ -12,15 +12,6 @@ cert_chain: []
|
|
12
12
|
date: 2008-04-07 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: object_proxy
|
17
|
-
version_requirement:
|
18
|
-
version_requirements: !ruby/object:Gem::Requirement
|
19
|
-
requirements:
|
20
|
-
- - ">="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 1.0.2
|
23
|
-
version:
|
24
15
|
- !ruby/object:Gem::Dependency
|
25
16
|
name: activesupport
|
26
17
|
version_requirement:
|
@@ -48,6 +39,9 @@ extensions: []
|
|
48
39
|
extra_rdoc_files:
|
49
40
|
- README
|
50
41
|
files:
|
42
|
+
- lib/kind_dom
|
43
|
+
- lib/kind_dom/base.rb
|
44
|
+
- lib/kind_dom/object_proxy.rb
|
51
45
|
- lib/kind_dom.rb
|
52
46
|
- test/flickr_rss.xml
|
53
47
|
- test/kind_dom_test.rb
|