traverse 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,3 +1,38 @@
1
1
  # Traverse
2
2
 
3
- Traverse is a simple tool that makes it easy to traverse XML.
3
+ Traverse is a simple tool that makes it easy to traverse XML.
4
+
5
+ Let's say you're messing around with Twitter's
6
+ [public timeline](http://api.twitter.com/statuses/public_timeline.xml).
7
+ Traverse let's you do things like this:
8
+
9
+ ```ruby
10
+ timeline = Traverse::Document.new(open "http://api.twitter.com/statuses/public_timeline.xml")
11
+
12
+ timeline.statuses.each do |status|
13
+ puts "#{status.user.name} says: #{status.text}"
14
+ end
15
+ ```
16
+
17
+ For a slightly more complicated example, take a look at a
18
+ [boxscore](http://gd2.mlb.com/components/game/mlb/year_2011/month_03/day_31/gid_2011_03_31_detmlb_nyamlb_1/boxscore.xml)
19
+ pulled from Major League Baseball's public API.
20
+
21
+ ```ruby
22
+ url = "http://gd2.mlb.com/components/game/mlb/year_2011/month_03/day_31/gid_2011_03_31_detmlb_nyamlb_1/boxscore.xml"
23
+ boxscore = Traverse::Document.new(open url)
24
+
25
+ # let's start traversing!
26
+
27
+ boxscore.game_id # => '2011/03/31/detmlb-nyamlb-1'
28
+
29
+ boxscore.battings[0].batters[1].name_display_first_last # => 'Derek Jeter'
30
+
31
+ boxscore.battings[0].batters.select do |batter|
32
+ batter.rbi.to_i > 0
33
+ end.count # => 4
34
+
35
+ boxscore.pitchings.find do |pitching|
36
+ pitching.team_flag == 'away'
37
+ end.out # => '24'
38
+ ```
@@ -1,3 +1,3 @@
1
1
  module Traverse
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/traverse.rb CHANGED
@@ -53,24 +53,26 @@ module Traverse
53
53
  @document.get_attribute attr
54
54
  end
55
55
 
56
+ def attributes
57
+ name_value_pairs = @document.attributes.map do |name, attribute|
58
+ [name, attribute.value]
59
+ end
60
+ Hash[ name_value_pairs ]
61
+ end
62
+
63
+ def children
64
+ real_children.map { |child| Document.new child }
65
+ end
66
+
56
67
  private
57
68
  def method_missing m, *args, &block
58
69
  self[m] or super
59
70
  end
60
71
 
61
- def attributes
62
- if @document.is_a? Nokogiri::XML::Document
63
- []
64
- else
65
- @document.attributes
66
- end
67
- end
68
-
69
72
  def text_node?
70
- num_children = @document.children.count
71
- return false unless num_children == 1
72
-
73
- @document.children.first.is_a? Nokogiri::XML::Text
73
+ @document.children.all? do |child|
74
+ child.is_a? Nokogiri::XML::Text
75
+ end
74
76
  end
75
77
 
76
78
  def text_only_node? node
@@ -103,7 +105,7 @@ module Traverse
103
105
  real_children.select do |child|
104
106
  child.children.all? do |baby|
105
107
  if baby.class == Nokogiri::XML::Text
106
- true
108
+ true # ignore text children
107
109
  else
108
110
  baby.name == child.name.singularize
109
111
  end
@@ -111,12 +113,28 @@ module Traverse
111
113
  end
112
114
  end
113
115
 
116
+ def find_first_non_comment_node xml_string
117
+ Nokogiri::XML(xml_string).children.find do |child|
118
+ !child.comment?
119
+ end
120
+ end
121
+
114
122
  def setup_underlying_document document
115
123
  if document.is_a? String
116
124
  begin
117
- @document = Nokogiri::XML(document)
125
+ @document = find_first_non_comment_node document
118
126
  rescue
119
- return nil
127
+ nil
128
+ end
129
+ elsif document.respond_to? :read # is it file-like...
130
+ begin
131
+ @document = find_first_non_comment_node document.read
132
+ rescue
133
+ nil
134
+ end
135
+ elsif document.is_a? Nokogiri::XML::Document
136
+ @document = document.children.find do |child|
137
+ !child.comment?
120
138
  end
121
139
  else
122
140
  @document = document
@@ -127,18 +145,4 @@ module Traverse
127
145
  "<Traversable... >"
128
146
  end
129
147
  end
130
-
131
- module Proxy
132
- private
133
- def proxy *args
134
- if args.empty?
135
- @proxy
136
- else
137
- @proxy = args.first
138
- def method_missing m
139
- @proxy.send m
140
- end
141
- end
142
- end
143
- end
144
148
  end
data/spec/spec.rb CHANGED
@@ -56,51 +56,69 @@ xml = %{
56
56
 
57
57
  describe Traverse::Document do
58
58
  before do
59
- @doc = Traverse::Document.new xml
59
+ @book = Traverse::Document.new xml
60
60
  end
61
61
 
62
62
  it "helps you access attributes" do
63
- @doc.book.title.must_equal "Vineland"
63
+ @book.title.must_equal "Vineland"
64
64
  end
65
65
 
66
66
  it "also helps you access attributes shadowed by children" do
67
- @doc.book.author.wont_equal "Thomas Pynchon"
68
- @doc.book['author'].must_equal "Thomas Pynchon"
69
- @doc.book.author.name.must_equal "Thomas Pynchon"
67
+ @book.author.wont_equal "Thomas Pynchon"
68
+ @book['author'].must_equal "Thomas Pynchon"
69
+ @book.author.name.must_equal "Thomas Pynchon"
70
70
  end
71
71
 
72
- it "helps you get at child nodes" do
73
- @doc.book.review.reviewer.must_equal "Salman Rushdie"
74
- @doc.book.epigraph.author.must_equal "Johnny Copeland"
72
+ describe "support for enumerable" do
73
+
74
+ it "gives you access to the current node's attributes" do
75
+ @book.attributes.any? do |name, value|
76
+ value == "Vineland"
77
+ end.must_equal true
78
+ end
79
+
80
+ it "gives you access to the current node's children as traversable documents" do
81
+ assert @book.children.any? do |child|
82
+ child.attributes.any? do |name, value|
83
+ name == "reviewer" and value == "Salman Rushdie"
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ it "helps you traverse to child nodes" do
91
+ @book.review.reviewer.must_equal "Salman Rushdie"
92
+ @book.epigraph.author.must_equal "Johnny Copeland"
75
93
  end
76
94
 
77
95
  it "knows when a node contains only text" do
78
- assert @doc.book.epigraph.send(:text_node?)
96
+ assert @book.epigraph.send(:text_node?)
79
97
  end
80
98
 
81
99
  it "handles annoying text nodes transparently" do
82
- @doc.book.epigraph.text.must_match(/Every dog has his day/)
83
- @doc.book.review.text.must_match(/that rarest of birds/)
100
+ @book.epigraph.text.must_match(/Every dog has his day/)
101
+ @book.review.text.must_match(/that rarest of birds/)
84
102
  end
85
103
 
86
104
  it "nevertheless handles attributes named 'text'" do
87
- @doc.book.text['text'].must_match(/seriously/)
88
- @doc.book.text.text.must_match(/rilly/)
105
+ @book.text['text'].must_match(/seriously/)
106
+ @book.text.text.must_match(/rilly/)
89
107
  end
90
108
 
91
109
  it "knows when a node has only text and no attributes" do
92
- @doc.book.pagecount.must_equal "385"
110
+ @book.pagecount.must_equal "385"
93
111
  end
94
112
 
95
113
  it "knows to collect children with the same name" do
96
- @doc.book.author.books.count.must_equal 8
97
- assert @doc.book.author.books.all? do |book|
114
+ @book.author.books.count.must_equal 8
115
+ assert @book.author.books.all? do |book|
98
116
  book.is_a? String
99
117
  end
100
118
  end
101
119
 
102
120
  it "knows to collect children of a pluralized parent" do
103
- @doc.book.quotations.count.must_equal 2
104
- @doc.book.quotations.last.text.must_match(/more like an idiot savant/)
121
+ @book.quotations.count.must_equal 2
122
+ @book.quotations.last.text.must_match(/more like an idiot savant/)
105
123
  end
106
124
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traverse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-03 00:00:00.000000000 -04:00
12
+ date: 2011-08-05 00:00:00.000000000 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
17
- requirement: &2157314300 !ruby/object:Gem::Requirement
17
+ requirement: &2152908620 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,7 +22,7 @@ dependencies:
22
22
  version: '1.5'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2157314300
25
+ version_requirements: *2152908620
26
26
  description: Easily traverse an XML document.
27
27
  email:
28
28
  - alan.m.odonnell@gmail.com