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.
@@ -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
- Scenario: getting a single node
7
- When I parse the XML:
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
- And I call "get('//rooms')" on the document
20
- Then the result should be a "Dozuki::XML::Node"
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 parse the XML:
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 parse the XML:
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
- Scenario: getting the int of a single node
7
- When I parse the XML:
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
- And I call "int('/root/number_of_beers')" on the document
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 parse the XML:
20
- """
21
- <root>
22
- <name>St. George's Arms</name>
23
- <average_price>20.32</average_price>
24
- <number_of_beers>
25
- 2
26
- </number_of_beers>
27
- </root>
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 parse the XML:
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
@@ -1,32 +1,15 @@
1
- When /^I parse the XML:$/ do |string|
1
+ When /^I (?:have )?parsed? the XML:$/ do |string|
2
2
  @doc = Dozuki::XML.parse(string)
3
3
  end
4
4
 
5
- Then /^the float result should be (\d+\.\d+)$/ do |float|
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
- fail "Expected error #{error}, nothing raised"
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
- Scenario: getting the string of a single node
7
- When I parse the XML:
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_cost>20.32</average_cost>
11
+ <address>
12
+ 1 Main Street
13
+ </address>
12
14
  <number_of_beers>2</number_of_beers>
13
15
  </root>
14
16
  """
15
- And I call "string('/root/name')" on the document
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 parse the XML:
20
- """
21
- <root>
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 parse the XML:
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
@@ -1,3 +1,7 @@
1
1
  require 'nokogiri'
2
2
 
3
- require 'dozuki/xml'
3
+ require 'dozuki/exceptions'
4
+ require 'dozuki/parsers'
5
+ require 'dozuki/node'
6
+ require 'dozuki/node_collection'
7
+ require 'dozuki/xml'
@@ -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
@@ -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