dozuki 0.0.1 → 0.0.3
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/CI +13 -0
- data/Gemfile.lock +21 -20
- data/{README.rdoc → README.markdown} +48 -37
- data/Rakefile +17 -0
- data/dozuki.gemspec +4 -3
- data/features/each_accessor.feature +22 -50
- data/features/exists_accessor.feature +3 -4
- data/features/float_accessor.feature +22 -28
- data/features/get_accessor.feature +17 -46
- data/features/int_accessor.feature +22 -28
- data/features/steps/collection_steps.rb +11 -0
- data/features/steps/error_steps.rb +11 -0
- data/features/steps/format_steps.rb +19 -0
- data/features/steps/xml_steps.rb +4 -53
- data/features/string_accessor.feature +16 -27
- data/lib/dozuki.rb +5 -1
- data/lib/dozuki/exceptions.rb +23 -0
- data/lib/dozuki/node.rb +53 -0
- data/lib/dozuki/node_collection.rb +25 -0
- data/lib/dozuki/parsers.rb +29 -0
- data/lib/dozuki/version.rb +1 -1
- data/lib/dozuki/xml.rb +3 -8
- data/spec/dozuki/node_collection_spec.rb +114 -0
- data/spec/dozuki/{xml/node_spec.rb → node_spec.rb} +40 -40
- data/spec/dozuki/{xml/parser_spec.rb → parsers_spec.rb} +37 -29
- data/spec/dozuki/xml_spec.rb +14 -11
- metadata +33 -33
- data/lib/dozuki/xml/exceptions.rb +0 -27
- data/lib/dozuki/xml/node.rb +0 -54
- data/lib/dozuki/xml/node_collection.rb +0 -27
- data/lib/dozuki/xml/parser.rb +0 -23
- data/spec/dozuki/xml/node_collection_spec.rb +0 -117
data/CI
ADDED
data/Gemfile.lock
CHANGED
@@ -7,34 +7,35 @@ PATH
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ZenTest (4.
|
10
|
+
ZenTest (4.5.0)
|
11
11
|
archive-tar-minitar (0.5.2)
|
12
12
|
autotest (4.4.6)
|
13
13
|
ZenTest (>= 4.4.1)
|
14
14
|
builder (3.0.0)
|
15
15
|
columnize (0.3.2)
|
16
|
-
cucumber (0.10.
|
16
|
+
cucumber (0.10.2)
|
17
17
|
builder (>= 2.1.2)
|
18
|
-
diff-lcs (
|
19
|
-
gherkin (
|
20
|
-
json (
|
21
|
-
term-ansicolor (
|
18
|
+
diff-lcs (>= 1.1.2)
|
19
|
+
gherkin (>= 2.3.5)
|
20
|
+
json (>= 1.4.6)
|
21
|
+
term-ansicolor (>= 1.0.5)
|
22
22
|
diff-lcs (1.1.2)
|
23
|
-
gherkin (2.3.
|
24
|
-
json (
|
25
|
-
json (1.
|
26
|
-
linecache19 (0.5.
|
23
|
+
gherkin (2.3.6)
|
24
|
+
json (>= 1.4.6)
|
25
|
+
json (1.5.1)
|
26
|
+
linecache19 (0.5.12)
|
27
27
|
ruby_core_source (>= 0.1.4)
|
28
28
|
nokogiri (1.4.4)
|
29
|
-
|
30
|
-
|
31
|
-
rspec-
|
32
|
-
rspec-
|
33
|
-
|
34
|
-
rspec-
|
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)
|
35
36
|
diff-lcs (~> 1.1.2)
|
36
|
-
rspec-mocks (2.
|
37
|
-
ruby-debug-base19 (0.11.
|
37
|
+
rspec-mocks (2.5.0)
|
38
|
+
ruby-debug-base19 (0.11.25)
|
38
39
|
columnize (>= 0.3.1)
|
39
40
|
linecache19 (>= 0.5.11)
|
40
41
|
ruby_core_source (>= 0.1.4)
|
@@ -42,7 +43,7 @@ GEM
|
|
42
43
|
columnize (>= 0.3.1)
|
43
44
|
linecache19 (>= 0.5.11)
|
44
45
|
ruby-debug-base19 (>= 0.11.19)
|
45
|
-
ruby_core_source (0.1.
|
46
|
+
ruby_core_source (0.1.5)
|
46
47
|
archive-tar-minitar (>= 0.5.2)
|
47
48
|
term-ansicolor (1.0.5)
|
48
49
|
|
@@ -53,6 +54,6 @@ DEPENDENCIES
|
|
53
54
|
autotest
|
54
55
|
cucumber
|
55
56
|
dozuki!
|
56
|
-
|
57
|
+
rake
|
57
58
|
rspec
|
58
59
|
ruby-debug19
|
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
# Dozuki
|
2
2
|
|
3
3
|
A Nokogiri wrapper that simplifies commonly occurring tasks.
|
4
4
|
|
5
|
-
|
5
|
+
[](http://travis-ci.org/JamesAlmond/dozuki)
|
6
|
+
|
7
|
+
## What does it do?
|
6
8
|
|
7
9
|
Dozuki removes the repetitive tasks from parsing XML documents with XPaths such as:
|
8
10
|
|
@@ -14,73 +16,82 @@ Dozuki removes the repetitive tasks from parsing XML documents with XPaths such
|
|
14
16
|
|
15
17
|
It's mainly sugar for reducing the amount of chaining on calls like:
|
16
18
|
|
17
|
-
|
19
|
+
doc.xpath('/my/xpath').first.to_i
|
20
|
+
|
21
|
+
to:
|
18
22
|
|
19
|
-
|
23
|
+
doc.int('/my/xpath')
|
20
24
|
|
21
|
-
|
25
|
+
## How do I use it?
|
22
26
|
|
23
|
-
|
27
|
+
The parse method supports all input parameters supported by [Nokogiri's
|
28
|
+
parse method](http://nokogiri.org/Nokogiri/XML/Document.html#parse):
|
29
|
+
|
30
|
+
# simple usage
|
31
|
+
doc = Dozuki::XML.parse(string_or_io)
|
32
|
+
|
33
|
+
# or the full parameter list
|
34
|
+
doc = Dozuki::XML.parse(string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML)
|
24
35
|
|
25
36
|
This documents supports the Dozuki extensions for:
|
26
37
|
|
27
|
-
|
38
|
+
### Extracting a single node
|
28
39
|
|
29
40
|
The get methods takes an xpath and returns the first node that matches the xpath:
|
30
41
|
|
31
|
-
|
42
|
+
doc.get('/my/xpath')
|
32
43
|
|
33
44
|
If the node can't be found then an exception is raised.
|
34
45
|
|
35
|
-
|
46
|
+
### Extracting a single node of a certain type
|
36
47
|
|
37
48
|
The following methods take the first node that matches the xpath and returns the formatted result:
|
38
49
|
|
39
|
-
|
40
|
-
|
41
|
-
|
50
|
+
doc.string('/my/xpath') # surrounding whitespace stripped
|
51
|
+
doc.float('/my/xpath')
|
52
|
+
doc.int('/my/xpath')
|
42
53
|
|
43
54
|
These functions are to replace calls using plain Nokogiri such as:
|
44
55
|
|
45
56
|
doc.xpath('/my/xpath').first.to_i
|
46
57
|
|
47
|
-
|
58
|
+
### Checking whether an element exists
|
48
59
|
|
49
|
-
|
60
|
+
doc.exists?('/my/xpath')
|
50
61
|
|
51
|
-
|
62
|
+
### Iterating through nodes
|
52
63
|
|
53
64
|
Dozuki also provides a slightly more succinct way to 'each' an xpath:
|
54
65
|
|
55
|
-
|
56
|
-
|
57
|
-
|
66
|
+
doc.each('/my/xpath') do |node|
|
67
|
+
# do something
|
68
|
+
end
|
58
69
|
|
59
|
-
|
70
|
+
## Iterating through node text and parsing
|
60
71
|
|
61
72
|
There are also simple ways to extract formatted text of a series of nodes with an each
|
62
73
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
doc.each('/my/xpath').as_string do |node|
|
75
|
+
# string with surrounding whitespace stripped
|
76
|
+
end
|
77
|
+
|
78
|
+
doc.each('/my/xpath').as_int do |node|
|
79
|
+
# int
|
80
|
+
end
|
81
|
+
|
82
|
+
doc.each('/my/xpath').as_float do |node|
|
83
|
+
# float
|
84
|
+
end
|
74
85
|
|
75
|
-
|
86
|
+
## Playing nicely with Nokogiri
|
76
87
|
|
77
88
|
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.
|
78
89
|
|
79
|
-
|
90
|
+
## More documentation
|
80
91
|
|
81
|
-
More features are described in the..
|
92
|
+
More features are described in the.. [features](https://github.com/jamesalmond/dozuki/tree/master/features)
|
82
93
|
|
83
|
-
|
94
|
+
## Contributing to Dozuki
|
84
95
|
|
85
96
|
* Fork the project.
|
86
97
|
* Add tests that cover the new feature or bug fix.
|
@@ -88,13 +99,13 @@ More features are described in the.. {features}[https://github.com/jamesalmond/d
|
|
88
99
|
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
|
89
100
|
* Send me a pull request. Bonus points for topic branches.
|
90
101
|
|
91
|
-
|
102
|
+
## LICENSE:
|
92
103
|
|
93
104
|
(The MIT License)
|
94
105
|
|
95
106
|
Copyright (c) 2010:
|
96
107
|
|
97
|
-
*
|
108
|
+
* [James Almond](http://jamesalmond.com)
|
98
109
|
|
99
110
|
Permission is hereby granted, free of charge, to any person obtaining
|
100
111
|
a copy of this software and associated documentation files (the
|
@@ -113,4 +124,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
113
124
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
114
125
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
115
126
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
116
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
127
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,2 +1,19 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require 'cucumber'
|
7
|
+
require 'cucumber/rake/task'
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
desc "Run specs"
|
12
|
+
RSpec::Core::RakeTask.new :spec
|
13
|
+
|
14
|
+
desc "Run integrations"
|
15
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
16
|
+
t.cucumber_opts = "features --format pretty"
|
17
|
+
end
|
18
|
+
task :default => [:spec, :features]
|
19
|
+
|
data/dozuki.gemspec
CHANGED
@@ -18,12 +18,13 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {spec,features}/* .autotest`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
-
|
21
|
+
|
22
22
|
s.add_dependency("nokogiri")
|
23
|
-
|
23
|
+
|
24
24
|
s.add_development_dependency("rspec")
|
25
25
|
s.add_development_dependency("cucumber")
|
26
26
|
s.add_development_dependency("ruby-debug19")
|
27
27
|
s.add_development_dependency("autotest")
|
28
|
-
|
28
|
+
s.add_development_dependency("rake")
|
29
|
+
|
29
30
|
end
|
@@ -2,71 +2,43 @@ Feature: Iterating through nodes
|
|
2
2
|
In order to provide simpler way of accessing groups of nods
|
3
3
|
As a traverser
|
4
4
|
I want to access nodes using the each method and an xpath
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have parsed the XML:
|
8
8
|
"""
|
9
9
|
<root>
|
10
10
|
<name>St. George's Arms</name>
|
11
|
-
<average_price>20.32</average_price>
|
12
|
-
<number_of_beers>2</number_of_beers>
|
13
11
|
<rooms>
|
14
12
|
<room>SINGLE</room>
|
15
13
|
<room>Double</room>
|
16
14
|
</rooms>
|
15
|
+
<room_numbers>
|
16
|
+
<number>5</number>
|
17
|
+
<number>7</number>
|
18
|
+
</room_numbers>
|
19
|
+
<prices>
|
20
|
+
<price>53.50</price>
|
21
|
+
<price>799.78</price>
|
22
|
+
</prices>
|
17
23
|
</root>
|
18
24
|
"""
|
19
|
-
|
25
|
+
|
26
|
+
Scenario: using each to traverse a document
|
27
|
+
When I call "each('/root/rooms/room')" on the document and collect the results
|
20
28
|
Then the results should contain a node with the text "SINGLE"
|
21
29
|
And the results should contain a node with the text "Double"
|
22
|
-
|
30
|
+
|
23
31
|
Scenario: using each to traverse a document and getting the string elements
|
24
|
-
When I
|
25
|
-
"""
|
26
|
-
<root>
|
27
|
-
<name>St. George's Arms</name>
|
28
|
-
<average_price>20.32</average_price>
|
29
|
-
<number_of_beers>2</number_of_beers>
|
30
|
-
<rooms>
|
31
|
-
<room>SINGLE</room>
|
32
|
-
<room>Double</room>
|
33
|
-
</rooms>
|
34
|
-
</root>
|
35
|
-
"""
|
36
|
-
And I call "each('/root/rooms/room').as_string" on the document and collect the results
|
32
|
+
When I call "each('/root/rooms/room').as_string" on the document and collect the results
|
37
33
|
Then the results should contain "SINGLE"
|
38
34
|
And the results should contain "Double"
|
39
|
-
|
35
|
+
|
40
36
|
Scenario: using each to traverse a document and getting the integer elements
|
41
|
-
When I
|
42
|
-
"""
|
43
|
-
<root>
|
44
|
-
<name>St. George's Arms</name>
|
45
|
-
<average_price>20.32</average_price>
|
46
|
-
<number_of_beers>2</number_of_beers>
|
47
|
-
<rooms>
|
48
|
-
<room>5</room>
|
49
|
-
<room>7</room>
|
50
|
-
</rooms>
|
51
|
-
</root>
|
52
|
-
"""
|
53
|
-
And I call "each('/root/rooms/room').as_int" on the document and collect the results
|
37
|
+
When I call "each('/root/room_numbers/number').as_int" on the document and collect the results
|
54
38
|
Then the results should contain 5
|
55
|
-
And the results should contain
|
56
|
-
|
39
|
+
And the results should contain 7
|
40
|
+
|
57
41
|
Scenario: using each to traverse a document and getting the float elements
|
58
|
-
When I
|
59
|
-
"""
|
60
|
-
<root>
|
61
|
-
<name>St. George's Arms</name>
|
62
|
-
<average_price>20.32</average_price>
|
63
|
-
<number_of_beers>2</number_of_beers>
|
64
|
-
<rooms>
|
65
|
-
<room>53.50</room>
|
66
|
-
<room>799.78</room>
|
67
|
-
</rooms>
|
68
|
-
</root>
|
69
|
-
"""
|
70
|
-
And I call "each('/root/rooms/room').as_float" on the document and collect the results
|
42
|
+
When I call "each('/root/prices/price').as_float" on the document and collect the results
|
71
43
|
Then the results should contain 53.50
|
72
|
-
And the results should contain 799.78
|
44
|
+
And the results should contain 799.78
|
@@ -2,8 +2,7 @@ Feature: Exists accessor
|
|
2
2
|
In order to easily determine whether a node exists in a document
|
3
3
|
As a traverser
|
4
4
|
I want to check whether a node exists
|
5
|
-
|
6
|
-
|
5
|
+
|
7
6
|
Scenario: the node exists
|
8
7
|
When I parse the XML:
|
9
8
|
"""
|
@@ -15,7 +14,7 @@ Feature: Exists accessor
|
|
15
14
|
"""
|
16
15
|
And I call "exists?('/root/number_of_beers')" on the document
|
17
16
|
Then the result should be true
|
18
|
-
|
17
|
+
|
19
18
|
Scenario: the node doesn't exist
|
20
19
|
When I parse the XML:
|
21
20
|
"""
|
@@ -26,4 +25,4 @@ Feature: Exists accessor
|
|
26
25
|
</root>
|
27
26
|
"""
|
28
27
|
And I call "exists?('/root/food')" on the document
|
29
|
-
Then the result should be false
|
28
|
+
Then the result should be false
|
@@ -2,43 +2,37 @@ Feature: Getting floats from the document
|
|
2
2
|
In order to provide simpler way of getting floats from a node
|
3
3
|
As a traverser
|
4
4
|
I want to access nodes using the float method and an xpath
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have parsed the XML:
|
8
8
|
"""
|
9
9
|
<root>
|
10
10
|
<name>St. George's Arms</name>
|
11
11
|
<average_price>20.32</average_price>
|
12
|
+
<highest_price>
|
13
|
+
30.33
|
14
|
+
</highest_price>
|
12
15
|
<number_of_beers>2</number_of_beers>
|
13
16
|
</root>
|
14
17
|
"""
|
15
|
-
|
18
|
+
|
19
|
+
Scenario: getting the float of a single node
|
20
|
+
When I call "float('/root/average_price')" on the document
|
16
21
|
Then the result should be 20.32
|
17
|
-
|
22
|
+
|
18
23
|
Scenario: getting the float of a single node with whitespace
|
19
|
-
When I
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"""
|
29
|
-
And I call "float('/root/average_price')" on the document
|
30
|
-
Then the result should be 20.32
|
31
|
-
|
24
|
+
When I call "float('/root/highest_price')" on the document
|
25
|
+
Then the result should be 30.33
|
26
|
+
|
27
|
+
Scenario: getting the int of a node that doesn't contain a float
|
28
|
+
When I call "float('/root/name')" on the document
|
29
|
+
Then it should raise an "InvalidFormat" error
|
30
|
+
And the error should have the value "St. George's Arms"
|
31
|
+
And the error should have the format "float"
|
32
|
+
|
32
33
|
Scenario: getting a non-existent node
|
33
|
-
When I
|
34
|
-
|
35
|
-
<root>
|
36
|
-
<name>St. George's Arms</name>
|
37
|
-
<average_price>20.32</average_price>
|
38
|
-
<number_of_beers>2</number_of_beers>
|
39
|
-
</root>
|
40
|
-
"""
|
41
|
-
Then calling "float('//something/missing')" on the document should raise a "NotFound" error
|
34
|
+
When I call "float('//something/missing')" on the document
|
35
|
+
Then it should raise a "NotFound" error
|
42
36
|
And the error should have the xpath "//something/missing"
|
43
37
|
And the error should have a stored node
|
44
|
-
|
38
|
+
|