dozuki 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
- .rvmrc
4
+ .rvmrc
5
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ script: "rake"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
data/README.markdown CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  A Nokogiri wrapper that simplifies commonly occurring tasks.
4
4
 
5
- [![Build Status](http://travis-ci.org/JamesAlmond/dozuki.png)](http://travis-ci.org/JamesAlmond/dozuki)
5
+ [![Build Status](http://travis-ci.org/jamesalmond/dozuki.png)](http://travis-ci.org/JamesAlmond/dozuki)
6
6
 
7
7
  ## What does it do?
8
8
 
9
9
  Dozuki removes the repetitive tasks from parsing XML documents with XPaths such as:
10
10
 
11
- * extracting ints, floats, strings etc.
11
+ * extracting ints, floats, strings, dates etc.
12
12
  * accessing single nodes
13
13
  * checking whether an element exists
14
14
  * iterating through nodes
@@ -52,17 +52,18 @@ The get methods takes an xpath and returns the first node that matches the xpath
52
52
 
53
53
  If the node can't be found then an exception is raised.
54
54
 
55
- ### Extracting a single node of a certain type
55
+ ### Extracting and parsing the value of a single node
56
56
 
57
57
  The following methods take the first node that matches the xpath and returns the formatted result:
58
58
 
59
59
  doc.string('/my/xpath') # surrounding whitespace stripped
60
60
  doc.float('/my/xpath')
61
61
  doc.int('/my/xpath')
62
+ doc.date('/my/xpath') # format yyyy-mm-dd
62
63
 
63
64
  These functions are to replace calls using plain Nokogiri such as:
64
65
 
65
- doc.xpath('/my/xpath').first.to_i
66
+ doc.xpath('/my/xpath').first.to_i
66
67
 
67
68
  ### Checking whether an element exists
68
69
 
@@ -84,14 +85,18 @@ There are also simple ways to extract formatted text of a series of nodes with a
84
85
  # string with surrounding whitespace stripped
85
86
  end
86
87
 
87
- doc.each('/my/xpath').as_int do |node|
88
+ doc.each('/my/xpath').as_int do |int|
88
89
  # int
89
90
  end
90
91
 
91
- doc.each('/my/xpath').as_float do |node|
92
+ doc.each('/my/xpath').as_float do |float|
92
93
  # float
93
94
  end
94
95
 
96
+ doc.each('/my/xpath').as_date do |date|
97
+ # date
98
+ end
99
+
95
100
  ## Playing nicely with Nokogiri
96
101
 
97
102
  Dozuki will proxy any calls not recognised onto the underlying Nokogiri structure, including responds_to?, allowing you to treat it like any other Nokogiri document.
data/dozuki.gemspec CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.add_development_dependency("rspec")
25
25
  s.add_development_dependency("cucumber")
26
- s.add_development_dependency("ruby-debug19")
27
26
  s.add_development_dependency("autotest")
28
27
  s.add_development_dependency("rake")
29
28
 
@@ -0,0 +1,36 @@
1
+ Feature: Getting dates from the document
2
+ In order to provide simpler way of getting dates from a node
3
+ As a traverser
4
+ I want to access nodes using the date method and an xpath
5
+
6
+ Background:
7
+ Given I have parsed the XML:
8
+ """
9
+ <root>
10
+ <name>St. George's Arms</name>
11
+ <previous_event>2011-12-02</previous_event>
12
+ <next_event>
13
+ 2013-01-12
14
+ </next_event>
15
+ </root>
16
+ """
17
+
18
+ Scenario: getting the date of a single node
19
+ When I call "date('/root/previous_event')" on the document
20
+ Then the result should be 02/12/2011
21
+
22
+ Scenario: getting the of a single node with whitespace
23
+ When I call "date('/root/next_event')" on the document
24
+ Then the result should be 12/01/2013
25
+
26
+ Scenario: getting the int of a node that doesn't contain a date
27
+ When I call "date('/root/name')" on the document
28
+ Then it should raise an "InvalidFormat" error
29
+ And the error should have the value "St. George's Arms"
30
+ And the error should have the format "date"
31
+
32
+ Scenario: getting the date of a non-existent node
33
+ When I call "date('//something/missing')" on the document
34
+ Then it should raise a "NotFound" error
35
+ And the error should have the xpath "//something/missing"
36
+ And the error should have a stored node
@@ -20,6 +20,10 @@ Feature: Iterating through nodes
20
20
  <price>53.50</price>
21
21
  <price>799.78</price>
22
22
  </prices>
23
+ <available>
24
+ <day>2011-05-03</day>
25
+ <day>2013-07-09</day>
26
+ </available>
23
27
  </root>
24
28
  """
25
29
 
@@ -42,3 +46,8 @@ Feature: Iterating through nodes
42
46
  When I call "each('/root/prices/price').as_float" on the document and collect the results
43
47
  Then the results should contain 53.50
44
48
  And the results should contain 799.78
49
+
50
+ Scenario: using each to traverse a document and getting the date elements
51
+ When I call "each('/root/available/day').as_date" on the document and collect the results
52
+ Then the results should contain 03/05/2011
53
+ And the results should contain 09/07/2013
@@ -9,3 +9,8 @@ end
9
9
  Then /^the results should contain (\d+\.\d+)$/ do |float|
10
10
  @results.should include(float.to_f)
11
11
  end
12
+
13
+ Then /^the results should contain (\d+)\/(\d+)\/(\d+)$/ do |day, month, year|
14
+ @results.should include(Date.civil(year.to_i, month.to_i, day.to_i))
15
+ end
16
+
@@ -17,3 +17,8 @@ end
17
17
  Then /^the result should be false$/ do
18
18
  @result.should == false
19
19
  end
20
+
21
+ Then /^the result should be (\d+)\/(\d+)\/(\d+)$/ do |day, month, year|
22
+ @result.should == Date.civil(year.to_i, month.to_i, day.to_i)
23
+ end
24
+
data/lib/dozuki/node.rb CHANGED
@@ -31,6 +31,9 @@ module Dozuki
31
31
  def float(xpath)
32
32
  Parsers::Float.parse(get_first_node(xpath))
33
33
  end
34
+ def date(xpath)
35
+ Parsers::Date.parse(get_first_node(xpath))
36
+ end
34
37
 
35
38
  def get(xpath)
36
39
  node = Node.new(get_first_node(xpath))
@@ -21,5 +21,8 @@ module Dozuki
21
21
  def as_float(&blk)
22
22
  collection.each{|item| blk.call(Parsers::Float.parse(item))}
23
23
  end
24
+ def as_date(&blk)
25
+ collection.each{|item| blk.call(Parsers::Date.parse(item))}
26
+ end
24
27
  end
25
28
  end
@@ -25,5 +25,15 @@ module Dozuki
25
25
  end
26
26
  end
27
27
  end
28
+ module Date
29
+ def self.parse(node)
30
+ string = String.parse(node)
31
+ begin
32
+ ::Date.parse(string)
33
+ rescue ArgumentError
34
+ raise InvalidFormat.new(:node => node, :value => string, :format => "date")
35
+ end
36
+ end
37
+ end
28
38
  end
29
39
  end
@@ -1,3 +1,3 @@
1
1
  module Dozuki
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -2,111 +2,107 @@ require 'spec_helper'
2
2
  module Dozuki
3
3
  describe NodeCollection do
4
4
 
5
+ let(:collection_item){mock "collection item"}
6
+ let(:collection){ [collection_item] }
7
+ let(:node_collection){NodeCollection.new(collection)}
8
+
9
+
5
10
  describe "as_node" do
6
- let(:collection){mock "some_nodes"}
7
- let(:blk){Proc.new{}}
8
- let(:node_collection){NodeCollection.new(collection)}
9
- let(:collection_item){mock "collection item"}
10
- let(:new_node){mock "new node"}
11
+ let(:a_dozuki_node){mock "a dozuki node"}
12
+
11
13
  before(:each) do
12
- collection.stub(:each).and_yield(collection_item)
13
- Node.stub(:new).and_return(new_node)
14
+ Node.stub(:new).and_return(a_dozuki_node)
14
15
  end
15
16
 
16
- subject{node_collection.as_node(&blk)}
17
+ subject{ node_collection.as_node{|arg| @block_called_with = arg} }
17
18
 
18
- it "should iterate through the collection" do
19
- collection.should_receive(:each)
20
- subject
21
- end
22
19
  it "should create a new Node with the yielded collection item" do
23
- Node.should_receive(:new).with(collection_item)
20
+ Node.should_receive(:new).with(collection_item).and_return(a_dozuki_node)
24
21
  subject
25
22
  end
26
- it "should call the bloc with the node" do
27
- blk.should_receive(:call).with(new_node)
23
+ it "should call the block with the wrapped dozuki node" do
28
24
  subject
25
+ @block_called_with.should == a_dozuki_node
29
26
  end
30
27
  end
31
28
 
32
29
  describe "as_string" do
33
- let(:collection){mock "some_nodes"}
34
- let(:blk){Proc.new{}}
35
- let(:node_collection){NodeCollection.new(collection)}
36
- let(:collection_item){mock "collection item"}
37
- let(:string){"string"}
30
+
31
+ let(:a_string){"string"}
32
+
38
33
  before(:each) do
39
- collection.stub(:each).and_yield(collection_item)
40
- Dozuki::Parsers::String.stub(:parse).and_return(string)
34
+ Dozuki::Parsers::String.stub(:parse).and_return(a_string)
41
35
  end
42
36
 
43
- subject{node_collection.as_string(&blk)}
37
+ subject{ node_collection.as_string{|arg| @block_called_with = arg} }
44
38
 
45
- it "should iterate through the collection" do
46
- collection.should_receive(:each)
39
+ it "should parse the node to a string" do
40
+ Dozuki::Parsers::String.should_receive(:parse).with(collection_item).and_return(a_string)
47
41
  subject
48
42
  end
49
- it "should create a new Node with the yielded collection item" do
50
- Dozuki::Parsers::String.should_receive(:parse).with(collection_item)
51
- subject
52
- end
53
- it "should call the bloc with the node" do
54
- blk.should_receive(:call).with("string")
43
+ it "should call the block with the string" do
55
44
  subject
45
+ @block_called_with.should == a_string
56
46
  end
57
47
  end
58
48
 
59
49
  describe "as_int" do
60
- let(:collection){mock "some_nodes"}
61
- let(:blk){Proc.new{}}
62
- let(:node_collection){NodeCollection.new(collection)}
63
- let(:collection_item){mock "collection item"}
64
- let(:int){39}
50
+
51
+ let(:an_int){39}
52
+
65
53
  before(:each) do
66
- collection.stub(:each).and_yield(collection_item)
67
- Dozuki::Parsers::Integer.stub(:parse).and_return(int)
54
+ Dozuki::Parsers::Integer.stub(:parse).and_return(an_int)
68
55
  end
69
56
 
70
- subject{node_collection.as_int(&blk)}
57
+ subject{ node_collection.as_int{|arg| @block_called_with = arg} }
71
58
 
72
- it "should iterate through the collection" do
73
- collection.should_receive(:each)
59
+ it "should parse the node to an integer" do
60
+ Dozuki::Parsers::Integer.should_receive(:parse).with(collection_item).and_return(an_int)
74
61
  subject
75
62
  end
76
- it "should create a new Node with the yielded collection item" do
77
- Dozuki::Parsers::Integer.should_receive(:parse).with(collection_item)
78
- subject
79
- end
80
- it "should call the bloc with the node" do
81
- blk.should_receive(:call).with(int)
63
+ it "should call the block with the integer" do
82
64
  subject
65
+ @block_called_with.should == an_int
83
66
  end
84
67
  end
85
68
 
86
69
  describe "as_float" do
87
- let(:collection){mock "some_nodes"}
88
- let(:blk){Proc.new{}}
89
- let(:node_collection){NodeCollection.new(collection)}
90
- let(:collection_item){mock "collection item"}
91
- let(:float){39.50}
70
+
71
+ let(:a_float){39.50}
72
+
92
73
  before(:each) do
93
- collection.stub(:each).and_yield(collection_item)
94
- Dozuki::Parsers::Float.stub(:parse).and_return(float)
74
+ Dozuki::Parsers::Float.stub(:parse).and_return(a_float)
95
75
  end
96
76
 
97
- subject{node_collection.as_float(&blk)}
77
+ subject{node_collection.as_float{|arg| @block_called_with = arg}}
98
78
 
99
- it "should iterate through the collection" do
100
- collection.should_receive(:each)
79
+ it "should parse the node to a float" do
80
+ Dozuki::Parsers::Float.should_receive(:parse).with(collection_item).and_return(a_float)
101
81
  subject
102
82
  end
103
- it "should create a new Node with the yielded collection item" do
104
- Dozuki::Parsers::Float.should_receive(:parse).with(collection_item)
83
+ it "should call the block with the float" do
84
+ subject
85
+ @block_called_with.should == a_float
86
+ end
87
+ end
88
+
89
+ describe "as_date" do
90
+
91
+ let(:a_date){Date.civil(2012, 2,5)}
92
+
93
+ before(:each) do
94
+ Dozuki::Parsers::Date.stub(:parse).and_return(a_date)
95
+ end
96
+
97
+ subject{node_collection.as_date{|arg| @block_called_with = arg}}
98
+
99
+ it "should parse the node to a date" do
100
+ Dozuki::Parsers::Date.should_receive(:parse).with(collection_item).and_return(a_date)
105
101
  subject
106
102
  end
107
- it "should call the bloc with the node" do
108
- blk.should_receive(:call).with(float)
103
+ it "should call the block with the date" do
109
104
  subject
105
+ @block_called_with.should == a_date
110
106
  end
111
107
  end
112
108
 
@@ -191,6 +191,41 @@ module Dozuki
191
191
  end
192
192
  end
193
193
 
194
+ describe "date" do
195
+ let(:nokogiri_node) { mock("nokogiri_node") }
196
+ let(:xpath) { "/some/xpath" }
197
+ let(:node) { Node.new(nokogiri_node) }
198
+ let(:date_node) { mock "some node" }
199
+ let(:parsed) { mock "parsed result" }
200
+
201
+ before(:each) do
202
+ nokogiri_node.stub(:xpath).and_return([date_node])
203
+ Dozuki::Parsers::Date.stub(:parse).and_return(parsed)
204
+ end
205
+
206
+ subject{ node.date(xpath) }
207
+
208
+ it "should get the collection from the nokogiri node" do
209
+ nokogiri_node.should_receive(:xpath).with(xpath)
210
+ subject
211
+ end
212
+ it "should parse the node to a date" do
213
+ Dozuki::Parsers::Date.should_receive(:parse).with(date_node)
214
+ subject
215
+ end
216
+ it "should return the parsed result" do
217
+ subject.should == parsed
218
+ end
219
+ context "where the xpath returns no node" do
220
+ before(:each) do
221
+ nokogiri_node.should_receive(:xpath).and_return([])
222
+ end
223
+ it "should raise a NotFound error" do
224
+ expect{subject}.to raise_error(Dozuki::NotFound)
225
+ end
226
+ end
227
+ end
228
+
194
229
  describe "get" do
195
230
  let(:nokogiri_node) { mock("nokogiri_node") }
196
231
  let(:xpath) { "/some/xpath" }
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module Dozuki
4
4
  module Parsers
5
-
5
+
6
6
  describe String do
7
7
  describe ".parse" do
8
8
  let(:string){"something"}
@@ -80,7 +80,26 @@ module Dozuki
80
80
  end
81
81
  end
82
82
  end
83
+ end
84
+
85
+ describe Date do
86
+ describe ".parse" do
87
+ let(:string){"2011-01-03"}
88
+ let(:node) { mock :node , :text => string }
89
+
90
+ subject { Date.parse(node) }
83
91
 
92
+ it{ should == ::Date.civil(2011, 1, 3)}
93
+
94
+ context "where the string has whitespace" do
95
+ let(:string){"\n\n2011-04-01"}
96
+ it{ should == ::Date.civil(2011, 4, 1)}
97
+ end
98
+ context "where the string is not a valid date" do
99
+ let(:string){ "dhjklfsjkfjkds" }
100
+ specify { expect{ subject}.to raise_error(InvalidFormat)}
101
+ end
102
+ end
84
103
  end
85
104
  end
86
105
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: dozuki
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.0
5
+ version: 0.3.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - James Almond
@@ -10,12 +10,11 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-21 00:00:00 +01:00
13
+ date: 2011-05-09 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: nokogiri
18
- prerelease: false
19
18
  requirement: &id001 !ruby/object:Gem::Requirement
20
19
  none: false
21
20
  requirements:
@@ -23,10 +22,10 @@ dependencies:
23
22
  - !ruby/object:Gem::Version
24
23
  version: "0"
25
24
  type: :runtime
25
+ prerelease: false
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
- prerelease: false
30
29
  requirement: &id002 !ruby/object:Gem::Requirement
31
30
  none: false
32
31
  requirements:
@@ -34,10 +33,10 @@ dependencies:
34
33
  - !ruby/object:Gem::Version
35
34
  version: "0"
36
35
  type: :development
36
+ prerelease: false
37
37
  version_requirements: *id002
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: cucumber
40
- prerelease: false
41
40
  requirement: &id003 !ruby/object:Gem::Requirement
42
41
  none: false
43
42
  requirements:
@@ -45,10 +44,10 @@ dependencies:
45
44
  - !ruby/object:Gem::Version
46
45
  version: "0"
47
46
  type: :development
47
+ prerelease: false
48
48
  version_requirements: *id003
49
49
  - !ruby/object:Gem::Dependency
50
- name: ruby-debug19
51
- prerelease: false
50
+ name: autotest
52
51
  requirement: &id004 !ruby/object:Gem::Requirement
53
52
  none: false
54
53
  requirements:
@@ -56,10 +55,10 @@ dependencies:
56
55
  - !ruby/object:Gem::Version
57
56
  version: "0"
58
57
  type: :development
58
+ prerelease: false
59
59
  version_requirements: *id004
60
60
  - !ruby/object:Gem::Dependency
61
- name: autotest
62
- prerelease: false
61
+ name: rake
63
62
  requirement: &id005 !ruby/object:Gem::Requirement
64
63
  none: false
65
64
  requirements:
@@ -67,18 +66,8 @@ dependencies:
67
66
  - !ruby/object:Gem::Version
68
67
  version: "0"
69
68
  type: :development
70
- version_requirements: *id005
71
- - !ruby/object:Gem::Dependency
72
- name: rake
73
69
  prerelease: false
74
- requirement: &id006 !ruby/object:Gem::Requirement
75
- none: false
76
- requirements:
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- version: "0"
80
- type: :development
81
- version_requirements: *id006
70
+ version_requirements: *id005
82
71
  description: A simple way of extracting various elements from an Nokogiri document using XPaths
83
72
  email:
84
73
  - james@jamesalmond.com
@@ -92,12 +81,13 @@ files:
92
81
  - .autotest
93
82
  - .gitignore
94
83
  - .rspec
84
+ - .travis.yml
95
85
  - CI
96
86
  - Gemfile
97
- - Gemfile.lock
98
87
  - README.markdown
99
88
  - Rakefile
100
89
  - dozuki.gemspec
90
+ - features/date_accessor.feature
101
91
  - features/each_accessor.feature
102
92
  - features/exists_accessor.feature
103
93
  - features/float_accessor.feature
@@ -138,12 +128,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
128
  requirements:
139
129
  - - ">="
140
130
  - !ruby/object:Gem::Version
131
+ hash: -990796854414031921
132
+ segments:
133
+ - 0
141
134
  version: "0"
142
135
  required_rubygems_version: !ruby/object:Gem::Requirement
143
136
  none: false
144
137
  requirements:
145
138
  - - ">="
146
139
  - !ruby/object:Gem::Version
140
+ hash: -990796854414031921
141
+ segments:
142
+ - 0
147
143
  version: "0"
148
144
  requirements: []
149
145
 
@@ -154,6 +150,7 @@ specification_version: 3
154
150
  summary: A syntactic sugar wrapper for Nokogiri
155
151
  test_files:
156
152
  - .autotest
153
+ - features/date_accessor.feature
157
154
  - features/each_accessor.feature
158
155
  - features/exists_accessor.feature
159
156
  - features/float_accessor.feature
data/Gemfile.lock DELETED
@@ -1,59 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- dozuki (0.1.0)
5
- nokogiri
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- ZenTest (4.5.0)
11
- archive-tar-minitar (0.5.2)
12
- autotest (4.4.6)
13
- ZenTest (>= 4.4.1)
14
- builder (3.0.0)
15
- columnize (0.3.2)
16
- cucumber (0.10.2)
17
- builder (>= 2.1.2)
18
- diff-lcs (>= 1.1.2)
19
- gherkin (>= 2.3.5)
20
- json (>= 1.4.6)
21
- term-ansicolor (>= 1.0.5)
22
- diff-lcs (1.1.2)
23
- gherkin (2.3.6)
24
- json (>= 1.4.6)
25
- json (1.5.1)
26
- linecache19 (0.5.12)
27
- ruby_core_source (>= 0.1.4)
28
- nokogiri (1.4.4)
29
- rake (0.8.7)
30
- rspec (2.5.0)
31
- rspec-core (~> 2.5.0)
32
- rspec-expectations (~> 2.5.0)
33
- rspec-mocks (~> 2.5.0)
34
- rspec-core (2.5.1)
35
- rspec-expectations (2.5.0)
36
- diff-lcs (~> 1.1.2)
37
- rspec-mocks (2.5.0)
38
- ruby-debug-base19 (0.11.25)
39
- columnize (>= 0.3.1)
40
- linecache19 (>= 0.5.11)
41
- ruby_core_source (>= 0.1.4)
42
- ruby-debug19 (0.11.6)
43
- columnize (>= 0.3.1)
44
- linecache19 (>= 0.5.11)
45
- ruby-debug-base19 (>= 0.11.19)
46
- ruby_core_source (0.1.5)
47
- archive-tar-minitar (>= 0.5.2)
48
- term-ansicolor (1.0.5)
49
-
50
- PLATFORMS
51
- ruby
52
-
53
- DEPENDENCIES
54
- autotest
55
- cucumber
56
- dozuki!
57
- rake
58
- rspec
59
- ruby-debug19