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
@@ -2,9 +2,9 @@ Feature: Getting nodes from the document
|
|
2
2
|
In order to provide a unified interface to the xml objects
|
3
3
|
As a traverser
|
4
4
|
I want to access nodes using the get 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>
|
@@ -16,53 +16,24 @@ Feature: Getting nodes from the document
|
|
16
16
|
</rooms>
|
17
17
|
</root>
|
18
18
|
"""
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
Scenario: getting a single node
|
21
|
+
When I call "get('//rooms')" on the document
|
22
|
+
Then the result should be a "Dozuki::Node"
|
21
23
|
And the result should have 2 elements
|
22
24
|
|
23
|
-
Scenario: getting a single node but accessing it with a block
|
24
|
-
When I parse the XML:
|
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 "get('//rooms')" on the document with a block
|
37
|
-
Then the block parameter should be a "Dozuki::XML::Node"
|
38
|
-
And the parameter should have 2 elements
|
39
|
-
|
40
25
|
Scenario: getting the first of multiple nodes
|
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>SINGLE</room>
|
49
|
-
<room>Double</room>
|
50
|
-
</rooms>
|
51
|
-
</root>
|
52
|
-
"""
|
53
|
-
And I call "get('//rooms/room')" on the document
|
26
|
+
When I call "get('//rooms/room')" on the document
|
54
27
|
Then the result should have the text "SINGLE"
|
55
|
-
|
28
|
+
|
29
|
+
Scenario: getting a single node but accessing it with a block
|
30
|
+
When I call "get('//rooms')" on the document with a block
|
31
|
+
Then the block parameter should be a "Dozuki::Node"
|
32
|
+
And the parameter should have 2 elements
|
33
|
+
|
56
34
|
Scenario: getting a non-existent node
|
57
|
-
When I
|
58
|
-
|
59
|
-
<root>
|
60
|
-
<name>St. George's Arms</name>
|
61
|
-
<average_price>20.32</average_price>
|
62
|
-
<number_of_beers>2</number_of_beers>
|
63
|
-
</root>
|
64
|
-
"""
|
65
|
-
Then calling "get('//something/missing')" on the document should raise a "NotFound" error
|
35
|
+
When I call "get('//something/missing')" on the document
|
36
|
+
Then it should raise a "NotFound" error
|
66
37
|
And the error should have the xpath "//something/missing"
|
67
38
|
And the error should have a stored node
|
68
|
-
And the error should have a stored node
|
39
|
+
And the error should have a stored node
|
@@ -2,42 +2,36 @@ Feature: Getting integers from the document
|
|
2
2
|
In order to provide simpler way of getting integers from a node
|
3
3
|
As a traverser
|
4
4
|
I want to access nodes using the int 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
12
|
<number_of_beers>2</number_of_beers>
|
13
|
+
<number_of_ciders>
|
14
|
+
5
|
15
|
+
</number_of_ciders>
|
13
16
|
</root>
|
14
17
|
"""
|
15
|
-
|
18
|
+
|
19
|
+
Scenario: getting the int of a single node
|
20
|
+
When I call "int('/root/number_of_beers')" on the document
|
16
21
|
Then the result should be 2
|
17
|
-
|
22
|
+
|
18
23
|
Scenario: getting the int of a single node with whitespace
|
19
|
-
When I
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"""
|
29
|
-
And I call "int('/root/number_of_beers')" on the document
|
30
|
-
Then the result should be 2
|
31
|
-
|
24
|
+
When I call "int('/root/number_of_ciders')" on the document
|
25
|
+
Then the result should be 5
|
26
|
+
|
27
|
+
Scenario: getting the int of a node that doesn't contain an int
|
28
|
+
When I call "int('/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 "integer"
|
32
|
+
|
32
33
|
Scenario: getting the int of 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 "int('//something/missing')" on the document should raise a "NotFound" error
|
34
|
+
When I call "int('//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
|
-
And the error should have a stored node
|
37
|
+
And the error should have a stored node
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Then /^the results should contain "([^"]*)"$/ do |string|
|
2
|
+
@results.should include(string)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the results should contain (\d+)$/ do |int|
|
6
|
+
@results.should include(int.to_i)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the results should contain (\d+\.\d+)$/ do |float|
|
10
|
+
@results.should include(float.to_f)
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Then /^it should raise an? "([^"]*)" error$/ do |error|
|
2
|
+
@error.should be_a Dozuki.const_get(error)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the error should have the (\w+) "([^"]*)"$/ do |method, value|
|
6
|
+
@error.send(method).should == value
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the error should have a stored node$/ do
|
10
|
+
@error.node.should_not be_nil
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Then /^the result should be "([^"]*)"$/ do |string|
|
2
|
+
@result.should == string
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the result should be (\d+)$/ do |int|
|
6
|
+
@result.should == int.to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the result should be (\d+\.\d+)$/ do |float|
|
10
|
+
@result.should == float.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^the result should be true$/ do
|
14
|
+
@result.should == true
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the result should be false$/ do
|
18
|
+
@result.should == false
|
19
|
+
end
|
data/features/steps/xml_steps.rb
CHANGED
@@ -1,32 +1,15 @@
|
|
1
|
-
When /^I
|
1
|
+
When /^I (?:have )?parsed? the XML:$/ do |string|
|
2
2
|
@doc = Dozuki::XML.parse(string)
|
3
3
|
end
|
4
4
|
|
5
|
-
|
6
|
-
@result.should == float.to_f
|
7
|
-
end
|
8
|
-
|
9
|
-
Then /^the error should have the xpath "([^"]*)"$/ do |xpath|
|
10
|
-
@error.xpath.should == xpath
|
11
|
-
end
|
12
|
-
|
13
|
-
Then /^the error should have a stored node$/ do
|
14
|
-
@error.node.should_not be_nil
|
15
|
-
end
|
16
|
-
|
17
|
-
Then /^calling "([^"]*)" on the document should raise a "([^"]*)" error$/ do |code, error|
|
5
|
+
When /^I call "([^"]*)" on the document$/ do |code|
|
18
6
|
begin
|
19
|
-
@doc.instance_eval(code)
|
20
|
-
|
21
|
-
rescue Dozuki::XML.const_get(error) => e
|
7
|
+
@result = @doc.instance_eval(code)
|
8
|
+
rescue StandardError => e
|
22
9
|
@error = e
|
23
10
|
end
|
24
11
|
end
|
25
12
|
|
26
|
-
When /^I call "([^"]*)" on the document$/ do |code|
|
27
|
-
@result = @doc.instance_eval(code)
|
28
|
-
end
|
29
|
-
|
30
13
|
When /^I call "([^"]*)" on the document with a block$/ do |code|
|
31
14
|
@result = @doc.instance_eval(code + "{|res| res}") # returns the variable passed to the block
|
32
15
|
end
|
@@ -35,30 +18,10 @@ When /^I call "([^"]*)" on the document and collect the results$/ do |code|
|
|
35
18
|
@results = @doc.instance_eval("results = [];" + code + "{|res| results << res}; results;")
|
36
19
|
end
|
37
20
|
|
38
|
-
Then /^the result should be (\d+)$/ do |int|
|
39
|
-
@result.should == int.to_i
|
40
|
-
end
|
41
|
-
|
42
|
-
Then /^the result should be "([^"]*)"$/ do |string|
|
43
|
-
@result.should == string
|
44
|
-
end
|
45
|
-
|
46
|
-
Then /^the result should be (\d+\.\d+)$/ do |float|
|
47
|
-
@result.should == float.to_f
|
48
|
-
end
|
49
|
-
|
50
21
|
Then /^the (?:result|block parameter) should be a "([^"]*)"$/ do |type|
|
51
22
|
@result.class.to_s.should == type
|
52
23
|
end
|
53
24
|
|
54
|
-
Then /^the result should be true$/ do
|
55
|
-
@result.should be_true
|
56
|
-
end
|
57
|
-
|
58
|
-
Then /^the result should be false$/ do
|
59
|
-
@result.should be_false
|
60
|
-
end
|
61
|
-
|
62
25
|
Then /^the (?:result|parameter) should have (\d+) elements$/ do |count|
|
63
26
|
@result.children.select{|e| e.is_a?(Nokogiri::XML::Element)}.count.should == count.to_i
|
64
27
|
end
|
@@ -70,15 +33,3 @@ end
|
|
70
33
|
Then /^the results should contain a node with the text "([^"]*)"$/ do |text|
|
71
34
|
@results.any?{|n| n.text == text}.should be_true
|
72
35
|
end
|
73
|
-
|
74
|
-
Then /^the results should contain "([^"]*)"$/ do |string|
|
75
|
-
@results.should include(string)
|
76
|
-
end
|
77
|
-
|
78
|
-
Then /^the results should contain (\d+)$/ do |int|
|
79
|
-
@results.should include(int.to_i)
|
80
|
-
end
|
81
|
-
|
82
|
-
Then /^the results should contain (\d+\.\d+)$/ do |float|
|
83
|
-
@results.should include(float.to_f)
|
84
|
-
end
|
@@ -2,40 +2,29 @@ Feature: Getting strings from the document
|
|
2
2
|
In order to provide simpler way of getting strings from a node
|
3
3
|
As a traverser
|
4
4
|
I want to access nodes using the string 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
|
+
<address>
|
12
|
+
1 Main Street
|
13
|
+
</address>
|
12
14
|
<number_of_beers>2</number_of_beers>
|
13
15
|
</root>
|
14
16
|
"""
|
15
|
-
|
17
|
+
|
18
|
+
Scenario: getting the string of a single node
|
19
|
+
When I call "string('/root/name')" on the document
|
16
20
|
Then the result should be "St. George's Arms"
|
17
|
-
|
21
|
+
|
18
22
|
Scenario: getting the text of a single node with whitespace
|
19
|
-
When I
|
20
|
-
|
21
|
-
|
22
|
-
<name>St. George's Arms</name>
|
23
|
-
<average_cost>20.32</average_cost>
|
24
|
-
<number_of_beers>2</number_of_beers>
|
25
|
-
</root>
|
26
|
-
"""
|
27
|
-
And I call "string('/root/name')" on the document
|
28
|
-
Then the result should be "St. George's Arms"
|
29
|
-
|
23
|
+
When I call "string('/root/address')" on the document
|
24
|
+
Then the result should be "1 Main Street"
|
25
|
+
|
30
26
|
Scenario: getting a non-existent node
|
31
|
-
When I
|
32
|
-
|
33
|
-
<root>
|
34
|
-
<name>St. George's Arms</name>
|
35
|
-
<average_cost>20.32</average_cost>
|
36
|
-
<number_of_beers>2</number_of_beers>
|
37
|
-
</root>
|
38
|
-
"""
|
39
|
-
Then calling "string('//something/missing')" on the document should raise a "NotFound" error
|
27
|
+
When I call "string('//something/missing')" on the document
|
28
|
+
Then it should raise a "NotFound" error
|
40
29
|
And the error should have the xpath "//something/missing"
|
41
|
-
And the error should have a stored node
|
30
|
+
And the error should have a stored node
|
data/lib/dozuki.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dozuki
|
2
|
+
class NotFound < StandardError
|
3
|
+
attr_accessor :xpath, :node
|
4
|
+
|
5
|
+
def initialize(msg=nil, opts={})
|
6
|
+
super msg
|
7
|
+
self.xpath = opts[:xpath]
|
8
|
+
self.node = opts[:node]
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class InvalidFormat < StandardError
|
14
|
+
attr_accessor :format, :node, :value
|
15
|
+
|
16
|
+
def initialize(opts={})
|
17
|
+
super "Cannot parse '#{opts[:value]}' to #{opts[:format]}"
|
18
|
+
self.format = opts[:format]
|
19
|
+
self.node = opts[:node]
|
20
|
+
self.value = opts[:value]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/dozuki/node.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'dozuki/parsers'
|
2
|
+
module Dozuki
|
3
|
+
class Node
|
4
|
+
attr_accessor :nokogiri_node
|
5
|
+
|
6
|
+
def initialize(nokogiri_node)
|
7
|
+
self.nokogiri_node = nokogiri_node
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method, *args, &blk)
|
11
|
+
nokogiri_node.send(method, *args, &blk)
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_to?(method)
|
15
|
+
nokogiri_node.respond_to?(method) ? true : super
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(xpath, &blk)
|
19
|
+
collection = NodeCollection.new(nokogiri_node.xpath(xpath))
|
20
|
+
block_given? ? collection.as_node(&blk) : collection
|
21
|
+
end
|
22
|
+
|
23
|
+
def string(xpath)
|
24
|
+
Parsers::String.parse(get_first_node(xpath))
|
25
|
+
end
|
26
|
+
|
27
|
+
def int(xpath)
|
28
|
+
Parsers::Integer.parse(get_first_node(xpath))
|
29
|
+
end
|
30
|
+
|
31
|
+
def float(xpath)
|
32
|
+
Parsers::Float.parse(get_first_node(xpath))
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(xpath)
|
36
|
+
node = Node.new(get_first_node(xpath))
|
37
|
+
yield node if block_given?
|
38
|
+
node
|
39
|
+
end
|
40
|
+
|
41
|
+
def exists?(xpath)
|
42
|
+
!nokogiri_node.xpath(xpath).empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def get_first_node(xpath)
|
47
|
+
node = nokogiri_node.xpath(xpath)
|
48
|
+
raise NotFound.new("Node not found", :xpath => xpath, :node => nokogiri_node) if node.empty?
|
49
|
+
node.first
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Dozuki
|
2
|
+
class NodeCollection
|
3
|
+
attr_accessor :collection
|
4
|
+
|
5
|
+
def initialize(collection)
|
6
|
+
self.collection = collection
|
7
|
+
end
|
8
|
+
|
9
|
+
def as_node(&blk)
|
10
|
+
collection.each{|item| blk.call(Node.new(item))}
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_string(&blk)
|
14
|
+
collection.each{|item| blk.call(Parsers::String.parse(item))}
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_int(&blk)
|
18
|
+
collection.each{|item| blk.call(Parsers::Integer.parse(item))}
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_float(&blk)
|
22
|
+
collection.each{|item| blk.call(Parsers::Float.parse(item))}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|