nori 1.1.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nori might be problematic. Click here for more details.

@@ -1,17 +1,29 @@
1
- ## 1.1.5 (2013-03-03)
1
+ # 2.0 (2012-12-12)
2
2
 
3
- * Fix: [#37](https://github.com/savonrb/nori/issues/37) special characters
4
- problem on Ruby 1.9.3-p392.
3
+ Please make sure to read the updated README for how to use the new version.
5
4
 
6
- ## 1.1.4 (2013-01-10)
5
+ * Change: Nori now defaults to use the Nokogiri parser.
7
6
 
8
- * Fix for remote code execution bug. For more in-depth information, read about the
9
- recent [Rails hotfix](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/61bkgvnSGTQ).
10
- Please make sure to upgrade now!
7
+ * Refactoring: Changed the `Nori` module to a class. This might cause problems if you
8
+ included the `Nori` module somewhere in your application. This use case was removed
9
+ for overall simplicity.
10
+
11
+ * Refactoring: Changed the interface to remove any global state. The global configuration
12
+ is gone and replaced with simple options to be passed to `Nori.new`.
13
+
14
+ ``` ruby
15
+ parser = Nori.new(strip_namespaces: true)
16
+ parser.parse(xml)
17
+ ```
18
+
19
+ * Refactoring: Removed the `Nori::Parser` module methods. After refactoring the rest,
20
+ there was only a single method left for this module and that was moved to `Nori`.
21
+
22
+ * Fix: [#16](https://github.com/savonrb/nori/issues/16) strip XML passed to Nori.
11
23
 
12
24
  ## 1.1.3 (2012-07-12)
13
25
 
14
- * Fix: Merged [pull request 21](https://github.com/rubiii/nori/pull/21) to fix an
26
+ * Fix: Merged [pull request 21](https://github.com/savonrb/nori/pull/21) to fix an
15
27
  issue with date/time/datetime regexes not matching positive time zone offsets and
16
28
  datetime strings with seconds.
17
29
 
@@ -21,15 +33,15 @@
21
33
 
22
34
  ## 1.1.1 (2012-06-29) - yanked
23
35
 
24
- * Fix: Merged [pull request 17](https://github.com/rubiii/nori/pull/17) for improved
36
+ * Fix: Merged [pull request 17](https://github.com/savonrb/nori/pull/17) for improved
25
37
  xs:time/xs:date/xs:dateTime regular expression matchers.
26
38
 
27
39
  ## 1.1.0 (2012-02-17)
28
40
 
29
- * Improvement: Merged [pull request 9](https://github.com/rubiii/nori/pull/9) to
41
+ * Improvement: Merged [pull request 9](https://github.com/savonrb/nori/pull/9) to
30
42
  allow multiple configurations of Nori.
31
43
 
32
- * Fix: Merged [pull request 10](https://github.com/rubiii/nori/pull/10) to handle
44
+ * Fix: Merged [pull request 10](https://github.com/savonrb/nori/pull/10) to handle
33
45
  date/time parsing errors. Fixes a couple of similar error reports.
34
46
 
35
47
  ## 1.0.2 (2011-07-04)
@@ -88,7 +100,7 @@
88
100
 
89
101
  ## 0.2.1 (2011-05-15)
90
102
 
91
- * Fix: Changed XML attributes converted to Hash keys to be prefixed with an @-sign.
103
+ * Fix: Changed XML attributes converted to Hash keys to be prefixed with an @-sign.
92
104
  This avoids problems with attributes and child nodes having the same name.
93
105
 
94
106
  <multiRef id="id1">
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Nori [![Build Status](https://secure.travis-ci.org/rubiii/nori.png)](http://travis-ci.org/rubiii/nori)
1
+ Nori [![Build Status](https://secure.travis-ci.org/savonrb/nori.png)](http://travis-ci.org/savonrb/nori)
2
2
  ====
3
3
 
4
4
  Really simple XML parsing ripped from Crack which ripped it from Merb.
@@ -6,7 +6,8 @@ Nori was created to bypass the stale development of Crack, improve its XML parse
6
6
  and fix certain issues.
7
7
 
8
8
  ``` ruby
9
- Nori.parse("<tag>This is the contents</tag>")
9
+ parser = Nori.new
10
+ parser.parse("<tag>This is the contents</tag>")
10
11
  # => { 'tag' => 'This is the contents' }
11
12
  ```
12
13
 
@@ -14,7 +15,7 @@ Nori supports pluggable parsers and ships with both REXML and Nokogiri implement
14
15
  It defaults to REXML, but you can change it to use Nokogiri via:
15
16
 
16
17
  ``` ruby
17
- Nori.parser = :nokogiri
18
+ Nori.new(:parser => :nokogiri) # or :rexml
18
19
  ```
19
20
 
20
21
  Make sure Nokogiri is in your LOAD_PATH when parsing XML, because Nori tries to load it
@@ -33,7 +34,7 @@ Besides regular typecasting, Nori features somewhat "advanced" typecasting:
33
34
  You can disable this feature via:
34
35
 
35
36
  ``` ruby
36
- Nori.advanced_typecasting = false
37
+ Nori.new(:advanced_typecasting => false)
37
38
  ```
38
39
 
39
40
 
@@ -44,7 +45,7 @@ Nori can strip the namespaces from your XML tags. This feature might raise
44
45
  problems and is therefore disabled by default. Enable it via:
45
46
 
46
47
  ``` ruby
47
- Nori.strip_namespaces = true
48
+ Nori.new(:strip_namespaces => true)
48
49
  ```
49
50
 
50
51
 
@@ -55,10 +56,8 @@ Nori lets you specify a custom formula to convert XML tags to Hash keys.
55
56
  Let me give you an example:
56
57
 
57
58
  ``` ruby
58
- Nori.configure do |config|
59
- config.convert_tags_to { |tag| tag.snake_case.to_sym }
60
- end
59
+ parser = Nori.new(:convert_tags_to => lambda { |tag| tag.snake_case.to_sym })
61
60
 
62
61
  xml = '<userResponse><accountStatus>active</accountStatus></userResponse>'
63
- Nori.parse(xml) # => { :user_response => { :account_status => "active" }
62
+ parser.parse(xml) # => { :user_response => { :account_status => "active" }
64
63
  ```
@@ -5,7 +5,7 @@ require "benchmark"
5
5
 
6
6
  Benchmark.bm 30 do |x|
7
7
 
8
- num = 250
8
+ num = 500
9
9
  xml = File.read File.expand_path("../soap_response.xml", __FILE__)
10
10
 
11
11
  x.report "rexml parser" do
@@ -1,59 +1,45 @@
1
1
  require "nori/version"
2
2
  require "nori/core_ext"
3
- require "nori/parser"
4
3
  require "nori/xml_utility_node"
5
4
 
6
- module Nori
7
- extend self
5
+ class Nori
8
6
 
9
- # Translates the given +xml+ to a Hash. Accepts an optional +parser+ to use.
10
- def parse(xml, parser = nil)
11
- return {} if xml.blank?
12
- Parser.parse xml, parser, self
13
- end
7
+ PARSERS = { :rexml => "REXML", :nokogiri => "Nokogiri" }
14
8
 
15
- # Sets the +parser+ to use.
16
- def parser=(parser)
17
- Parser.use = parser
18
- end
9
+ def initialize(options = {})
10
+ defaults = {
11
+ :strip_namespaces => false,
12
+ :convert_tags_to => nil,
13
+ :advanced_typecasting => true,
14
+ :parser => :nokogiri
15
+ }
19
16
 
20
- # Yields +self+ for configuration.
21
- def configure
22
- yield self
17
+ validate_options! defaults.keys, options.keys
18
+ @options = defaults.merge(options)
23
19
  end
24
20
 
25
- # Sets whether to use advanced typecasting.
26
- attr_writer :advanced_typecasting
21
+ def parse(xml)
22
+ cleaned_xml = xml.strip
23
+ return {} if cleaned_xml.empty?
27
24
 
28
- # Returns whether to use advanced typecasting.
29
- # Defaults to +true+.
30
- def advanced_typecasting?
31
- @advanced_typecasting != false
25
+ parser = load_parser @options[:parser]
26
+ parser.parse(cleaned_xml, @options)
32
27
  end
33
28
 
34
- # Sets whether to strip namespaces.
35
- attr_writer :strip_namespaces
29
+ private
36
30
 
37
- # Returns whether to strip namespaces.
38
- # Defaults to +false+.
39
- def strip_namespaces?
40
- @strip_namespaces
31
+ def load_parser(parser)
32
+ require "nori/parser/#{parser}"
33
+ Parser.const_get PARSERS[parser]
41
34
  end
42
35
 
43
- # Expects a +block+ which receives a tag to convert.
44
- # Accepts +nil+ for a reset to the default behavior of not converting tags.
45
- def convert_tags_to(reset = nil, &block)
46
- @convert_tag = reset || block
47
- end
48
-
49
- # Transforms a given +tag+ using the specified conversion formula.
50
- def convert_tag(tag)
51
- @convert_tag.call(tag)
52
- end
36
+ def validate_options!(available_options, options)
37
+ spurious_options = options - available_options
53
38
 
54
- # Returns whether to convert tags.
55
- def convert_tags?
56
- @convert_tag
39
+ unless spurious_options.empty?
40
+ raise ArgumentError, "Spurious options: #{spurious_options.inspect}\n" \
41
+ "Available options are: #{available_options.inspect}"
42
+ end
57
43
  end
58
44
 
59
45
  end
@@ -1,6 +1,6 @@
1
1
  require "uri"
2
2
 
3
- module Nori
3
+ class Nori
4
4
  module CoreExt
5
5
  module Hash
6
6
 
@@ -1,4 +1,4 @@
1
- module Nori
1
+ class Nori
2
2
  module CoreExt
3
3
  module Object
4
4
 
@@ -1,4 +1,4 @@
1
- module Nori
1
+ class Nori
2
2
  module CoreExt
3
3
  module String
4
4
 
@@ -1,6 +1,6 @@
1
1
  require "nokogiri"
2
2
 
3
- module Nori
3
+ class Nori
4
4
  module Parser
5
5
 
6
6
  # = Nori::Parser::Nokogiri
@@ -9,14 +9,14 @@ module Nori
9
9
  module Nokogiri
10
10
 
11
11
  class Document < ::Nokogiri::XML::SAX::Document
12
- attr_accessor :nori
12
+ attr_accessor :options
13
13
 
14
14
  def stack
15
15
  @stack ||= []
16
16
  end
17
17
 
18
18
  def start_element(name, attrs = [])
19
- stack.push Nori::XMLUtilityNode.new(nori, name, Hash[*attrs.flatten])
19
+ stack.push Nori::XMLUtilityNode.new(options, name, Hash[*attrs.flatten])
20
20
  end
21
21
 
22
22
  def end_element(name)
@@ -34,11 +34,9 @@ module Nori
34
34
 
35
35
  end
36
36
 
37
- def self.parse(xml, nori)
38
- return {} if xml.strip.empty?
39
-
37
+ def self.parse(xml, options)
40
38
  document = Document.new
41
- document.nori = nori
39
+ document.options = options
42
40
  parser = ::Nokogiri::XML::SAX::Parser.new document
43
41
  parser.parse xml
44
42
  document.stack.length > 0 ? document.stack.pop.to_hash : {}
@@ -1,6 +1,6 @@
1
1
  require "rexml/parsers/baseparser"
2
2
 
3
- module Nori
3
+ class Nori
4
4
  module Parser
5
5
 
6
6
  # = Nori::Parser::REXML
@@ -8,7 +8,7 @@ module Nori
8
8
  # REXML pull parser.
9
9
  module REXML
10
10
 
11
- def self.parse(xml, nori)
11
+ def self.parse(xml, options)
12
12
  stack = []
13
13
  parser = ::REXML::Parsers::BaseParser.new(xml)
14
14
 
@@ -20,7 +20,7 @@ module Nori
20
20
  when :end_doctype, :start_doctype
21
21
  # do nothing
22
22
  when :start_element
23
- stack.push Nori::XMLUtilityNode.new(nori, event[1], event[2])
23
+ stack.push Nori::XMLUtilityNode.new(options, event[1], event[2])
24
24
  when :end_element
25
25
  if stack.size > 1
26
26
  temp = stack.pop
@@ -1,4 +1,4 @@
1
- module Nori
1
+ class Nori
2
2
  class StringIOFile < StringIO
3
3
 
4
4
  attr_accessor :original_filename, :content_type
@@ -1,4 +1,4 @@
1
- module Nori
1
+ class Nori
2
2
  class StringWithAttributes < String
3
3
 
4
4
  attr_accessor :attributes
@@ -1,5 +1,5 @@
1
- module Nori
1
+ class Nori
2
2
 
3
- VERSION = "1.1.5"
3
+ VERSION = "2.0.0"
4
4
 
5
5
  end
@@ -1,5 +1,4 @@
1
1
  require "rexml/text"
2
- require "rexml/document"
3
2
  require "date"
4
3
  require "time"
5
4
  require "yaml"
@@ -8,7 +7,7 @@ require "bigdecimal"
8
7
  require "nori/string_with_attributes"
9
8
  require "nori/string_io_file"
10
9
 
11
- module Nori
10
+ class Nori
12
11
 
13
12
  # This is a slighly modified version of the XMLUtilityNode from
14
13
  # http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
@@ -78,21 +77,23 @@ module Nori
78
77
  self.typecasts["decimal"] = lambda { |v| v.nil? ? nil : BigDecimal(v.to_s) }
79
78
  self.typecasts["double"] = lambda { |v| v.nil? ? nil : v.to_f }
80
79
  self.typecasts["float"] = lambda { |v| v.nil? ? nil : v.to_f }
80
+ self.typecasts["symbol"] = lambda { |v| v.nil? ? nil : v.to_sym }
81
81
  self.typecasts["string"] = lambda { |v| v.to_s }
82
+ self.typecasts["yaml"] = lambda { |v| v.nil? ? nil : YAML.load(v) }
82
83
  self.typecasts["base64Binary"] = lambda { |v| v.unpack('m').first }
83
84
 
84
85
  self.available_typecasts = self.typecasts.keys
85
86
 
86
- def initialize(nori, name, normalized_attributes = {})
87
+ def initialize(options, name, normalized_attributes = {})
87
88
  # unnormalize attribute values
88
89
  attributes = Hash[* normalized_attributes.map do |key, value|
89
90
  [ key, unnormalize_xml_entities(value) ]
90
91
  end.flatten]
91
92
 
92
- @nori = nori
93
- @name = name.tr("-", "_")
94
- @name = @name.split(":").last if @nori.strip_namespaces?
95
- @name = @nori.convert_tag(@name) if @nori.convert_tags?
93
+ @options = options
94
+ @name = name.tr("-", "_")
95
+ @name = @name.split(":").last if @options[:strip_namespaces]
96
+ @name = @options[:convert_tags_to].call(@name) if @options[:convert_tags_to].respond_to? :call
96
97
 
97
98
  # leave the type alone if we don't know what it is
98
99
  @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
@@ -119,7 +120,8 @@ module Nori
119
120
  end
120
121
 
121
122
  def prefixed_attribute_name(attribute)
122
- @nori.convert_tags? ? @nori.convert_tag(attribute) : attribute
123
+ return attribute unless @options[:convert_tags_to].respond_to? :call
124
+ @options[:convert_tags_to].call(attribute)
123
125
  end
124
126
 
125
127
  def add_node(node)
@@ -137,7 +139,7 @@ module Nori
137
139
 
138
140
  if @text
139
141
  t = typecast_value unnormalize_xml_entities(inner_html)
140
- t = advanced_typecasting(t) if t.is_a?(String) && @nori.advanced_typecasting?
142
+ t = advanced_typecasting(t) if t.is_a?(String) && @options[:advanced_typecasting]
141
143
 
142
144
  if t.is_a?(String)
143
145
  t = StringWithAttributes.new(t)
@@ -7,15 +7,15 @@ Gem::Specification.new do |s|
7
7
  s.version = Nori::VERSION
8
8
  s.authors = ["Daniel Harrington", "John Nunemaker", "Wynn Netherland"]
9
9
  s.email = "me@rubiii.com"
10
- s.homepage = "http://github.com/rubiii/nori"
10
+ s.homepage = "https://github.com/savonrb/nori"
11
11
  s.summary = "XML to Hash translator"
12
12
  s.description = s.summary
13
13
 
14
14
  s.rubyforge_project = "nori"
15
15
 
16
- s.add_development_dependency "rake", "~> 0.8.7"
16
+ s.add_development_dependency "rake", "~> 10.0"
17
17
  s.add_development_dependency "nokogiri", ">= 1.4.0"
18
- s.add_development_dependency "rspec", "~> 2.5.0"
18
+ s.add_development_dependency "rspec", "~> 2.12"
19
19
 
20
20
  s.files = `git ls-files`.split("\n")
21
21
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,108 @@
1
+ require "spec_helper"
2
+
3
+ describe Nori do
4
+
5
+ describe "PARSERS" do
6
+ it "should return a Hash of parser details" do
7
+ Nori::PARSERS.should == { :rexml => "REXML", :nokogiri => "Nokogiri" }
8
+ end
9
+ end
10
+
11
+ context ".new" do
12
+ it "defaults to not strip any namespace identifiers" do
13
+ xml = <<-XML
14
+ <history xmlns:ns10="http://ns10.example.com">
15
+ <ns10:case>a_case</ns10:case>
16
+ </history>
17
+ XML
18
+
19
+ nori.parse(xml)["history"]["ns10:case"].should == "a_case"
20
+ end
21
+
22
+ it "defaults to not change XML tags" do
23
+ xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
24
+ nori.parse(xml).should == { "userResponse" => { "@id" => "1", "accountStatus" => "active" } }
25
+ end
26
+
27
+ it "raises when passed unknown global options" do
28
+ expect { Nori.new(:invalid => true) }.
29
+ to raise_error(ArgumentError, /Spurious options: \[:invalid\]/)
30
+ end
31
+ end
32
+
33
+ context ".new with :strip_namespaces" do
34
+ it "strips the namespace identifiers when set to true" do
35
+ xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>'
36
+ nori(:strip_namespaces => true).parse(xml).should have_key("Envelope")
37
+ end
38
+
39
+ it "still converts namespaced entries to array elements" do
40
+ xml = <<-XML
41
+ <history
42
+ xmlns:ns10="http://ns10.example.com"
43
+ xmlns:ns11="http://ns10.example.com">
44
+ <ns10:case><ns10:name>a_name</ns10:name></ns10:case>
45
+ <ns11:case><ns11:name>another_name</ns11:name></ns11:case>
46
+ </history>
47
+ XML
48
+
49
+ expected = [{ "name" => "a_name" }, { "name" => "another_name" }]
50
+ nori(:strip_namespaces => true).parse(xml)["history"]["case"].should == expected
51
+ end
52
+ end
53
+
54
+ context ".new with :convert_tags_to" do
55
+ it "converts all tags by a given formula" do
56
+ xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
57
+
58
+ snakecase_symbols = lambda { |tag| tag.snakecase.to_sym }
59
+ nori = nori(:convert_tags_to => snakecase_symbols)
60
+
61
+ nori.parse(xml).should == { :user_response => { :@id => "1", :account_status => "active" } }
62
+ end
63
+ end
64
+
65
+ context "#parse" do
66
+ it "defaults to use advanced typecasting" do
67
+ hash = nori.parse("<value>true</value>")
68
+ hash["value"].should == true
69
+ end
70
+
71
+ it "defaults to use the Nokogiri parser" do
72
+ # parsers are loaded lazily by default
73
+ require "nori/parser/nokogiri"
74
+
75
+ Nori::Parser::Nokogiri.should_receive(:parse).once
76
+ nori.parse("<any>thing</any>")
77
+ end
78
+
79
+ it "strips the XML" do
80
+ xml = double("xml")
81
+ xml.should_receive(:strip).and_return("<any>thing</any>")
82
+
83
+ nori.parse(xml).should == { "any" => "thing" }
84
+ end
85
+ end
86
+
87
+ context "#parse without :advanced_typecasting" do
88
+ it "can be changed to not typecast too much" do
89
+ hash = nori(:advanced_typecasting => false).parse("<value>true</value>")
90
+ hash["value"].should == "true"
91
+ end
92
+ end
93
+
94
+ context "#parse with :parser" do
95
+ it "can be configured to use the REXML parser" do
96
+ # parsers are loaded lazily by default
97
+ require "nori/parser/rexml"
98
+
99
+ Nori::Parser::REXML.should_receive(:parse).once
100
+ nori(:parser => :rexml).parse("<any>thing</any>")
101
+ end
102
+ end
103
+
104
+ def nori(options = {})
105
+ Nori.new(options)
106
+ end
107
+
108
+ end
@@ -2,16 +2,11 @@ require "spec_helper"
2
2
 
3
3
  describe Nori do
4
4
 
5
- Nori::Parser::PARSERS.each do |parser, class_name|
5
+ Nori::PARSERS.each do |parser, class_name|
6
6
  context "using the :#{parser} parser" do
7
7
 
8
8
  let(:parser) { parser }
9
9
 
10
- it "should work with unnormalized characters" do
11
- xml = '<root>&amp;</root>'
12
- parse(xml).should == { 'root' => "&" }
13
- end
14
-
15
10
  it "should transform a simple tag with content" do
16
11
  xml = "<tag>This is the contents</tag>"
17
12
  parse(xml).should == { 'tag' => 'This is the contents' }
@@ -92,40 +87,33 @@ describe Nori do
92
87
  end
93
88
 
94
89
  context "without advanced typecasting" do
95
- around do |example|
96
- Nori.advanced_typecasting = false
97
- example.run
98
- Nori.advanced_typecasting = true
99
- end
100
-
101
90
  it "should not transform 'true'" do
102
- parse("<value>true</value>")["value"].should == "true"
91
+ hash = parse("<value>true</value>", :advanced_typecasting => false)
92
+ hash["value"].should == "true"
103
93
  end
104
94
 
105
95
  it "should not transform 'false'" do
106
- parse("<value>false</value>")["value"].should == "false"
96
+ hash = parse("<value>false</value>", :advanced_typecasting => false)
97
+ hash["value"].should == "false"
107
98
  end
108
99
 
109
100
  it "should not transform Strings matching the xs:time format" do
110
- parse("<value>09:33:55Z</value>")["value"].should == "09:33:55Z"
101
+ hash = parse("<value>09:33:55Z</value>", :advanced_typecasting => false)
102
+ hash["value"].should == "09:33:55Z"
111
103
  end
112
104
 
113
105
  it "should not transform Strings matching the xs:date format" do
114
- parse("<value>1955-04-18-05:00</value>")["value"].should == "1955-04-18-05:00"
106
+ hash = parse("<value>1955-04-18-05:00</value>", :advanced_typecasting => false)
107
+ hash["value"].should == "1955-04-18-05:00"
115
108
  end
116
109
 
117
110
  it "should not transform Strings matching the xs:dateTime format" do
118
- parse("<value>1955-04-18T11:22:33-05:00</value>")["value"].should == "1955-04-18T11:22:33-05:00"
111
+ hash = parse("<value>1955-04-18T11:22:33-05:00</value>", :advanced_typecasting => false)
112
+ hash["value"].should == "1955-04-18T11:22:33-05:00"
119
113
  end
120
114
  end
121
115
 
122
116
  context "with advanced typecasting" do
123
- around do |example|
124
- Nori.advanced_typecasting = true
125
- example.run
126
- Nori.advanced_typecasting = false
127
- end
128
-
129
117
  it "should transform 'true' to TrueClass" do
130
118
  parse("<value>true</value>")["value"].should == true
131
119
  end
@@ -294,46 +282,6 @@ describe Nori do
294
282
  parse(xml)['tag_1'].keys.should include('@attr_1')
295
283
  end
296
284
 
297
- context "with strip_namespaces set to true" do
298
- around do |example|
299
- Nori.strip_namespaces = true
300
- example.run
301
- Nori.strip_namespaces = false
302
- end
303
-
304
- it "should strip the namespace from every tag" do
305
- xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>'
306
- parse(xml).should have_key("Envelope")
307
- end
308
-
309
- it "converts namespaced entries to array elements" do
310
- xml = <<-XML
311
- <history
312
- xmlns:ns10="http://ns10.example.com"
313
- xmlns:ns11="http://ns10.example.com">
314
- <ns10:case><ns10:name>a_name</ns10:name></ns10:case>
315
- <ns11:case><ns11:name>another_name</ns11:name></ns11:case>
316
- </history>
317
- XML
318
-
319
- expected_case = [{ "name" => "a_name" }, { "name" => "another_name" }]
320
- parse(xml)["history"]["case"].should == expected_case
321
- end
322
- end
323
-
324
- context "with convert_tags_to set to a custom formula" do
325
- around do |example|
326
- Nori.convert_tags_to { |tag| tag.snakecase.to_sym }
327
- example.run
328
- Nori.convert_tags_to(nil)
329
- end
330
-
331
- it "transforms the tags to snakecase Symbols" do
332
- xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
333
- parse(xml).should == { :user_response => { :@id => "1", :account_status => "active" } }
334
- end
335
- end
336
-
337
285
  it "should render nested content correctly" do
338
286
  xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
339
287
  parse(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
@@ -400,8 +348,7 @@ describe Nori do
400
348
  'approved' => nil,
401
349
  'written_on' => nil,
402
350
  'viewed_at' => nil,
403
- # don't execute arbitary YAML code
404
- 'content' => { "@type" => "yaml" },
351
+ 'content' => nil,
405
352
  'parent_id' => nil,
406
353
  'nil_true' => nil,
407
354
  'namespaced' => nil
@@ -420,7 +367,7 @@ describe Nori do
420
367
  <replies-close-in type="integer">2592000000</replies-close-in>
421
368
  <written-on type="date">2003-07-16</written-on>
422
369
  <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
423
- <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true</content>
370
+ <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
424
371
  <author-email-address>david@loudthinking.com</author-email-address>
425
372
  <parent-id></parent-id>
426
373
  <ad-revenue type="decimal">1.5</ad-revenue>
@@ -441,13 +388,12 @@ describe Nori do
441
388
  # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
442
389
  # The line in ActiveSupport is
443
390
  # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
444
- 'content' => "--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true",
391
+ 'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
445
392
  'author_email_address' => "david@loudthinking.com",
446
393
  'parent_id' => nil,
447
394
  'ad_revenue' => BigDecimal("1.50"),
448
395
  'optimum_viewing_angle' => 135.0,
449
- # don't create symbols from arbitary remote code
450
- 'resident' => "yes"
396
+ 'resident' => :yes
451
397
  }
452
398
 
453
399
  parse(topic_xml)["topic"].each do |k,v|
@@ -667,29 +613,11 @@ describe Nori do
667
613
  end
668
614
 
669
615
  end
670
-
671
- describe "using different nori" do
672
- let(:parser) { parser }
673
- let(:different_nori) do
674
- module DifferentNori
675
- extend Nori
676
- end
677
- DifferentNori.configure do |config|
678
- config.convert_tags_to { |tag| tag.upcase }
679
- end
680
- DifferentNori
681
- end
682
-
683
- it "should transform with different nori" do
684
- xml = "<SomeThing>xml</SomeThing>"
685
- parse(xml).should == { "SomeThing" => "xml" }
686
- different_nori.parse(xml, parser).should == { "SOMETHING" => "xml" }
687
- end
688
- end
689
616
  end
690
617
 
691
- def parse(xml)
692
- Nori.parse xml, parser
618
+ def parse(xml, options = {})
619
+ defaults = { :parser => parser }
620
+ Nori.new(defaults.merge(options)).parse(xml)
693
621
  end
694
622
 
695
623
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nori
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-03-03 00:00:00.000000000 Z
14
+ date: 2012-12-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rake
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: 0.8.7
23
+ version: '10.0'
24
24
  type: :development
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  requirements:
29
29
  - - ~>
30
30
  - !ruby/object:Gem::Version
31
- version: 0.8.7
31
+ version: '10.0'
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: nokogiri
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -52,7 +52,7 @@ dependencies:
52
52
  requirements:
53
53
  - - ~>
54
54
  - !ruby/object:Gem::Version
55
- version: 2.5.0
55
+ version: '2.12'
56
56
  type: :development
57
57
  prerelease: false
58
58
  version_requirements: !ruby/object:Gem::Requirement
@@ -60,7 +60,7 @@ dependencies:
60
60
  requirements:
61
61
  - - ~>
62
62
  - !ruby/object:Gem::Version
63
- version: 2.5.0
63
+ version: '2.12'
64
64
  description: XML to Hash translator
65
65
  email: me@rubiii.com
66
66
  executables: []
@@ -82,7 +82,6 @@ files:
82
82
  - lib/nori/core_ext/hash.rb
83
83
  - lib/nori/core_ext/object.rb
84
84
  - lib/nori/core_ext/string.rb
85
- - lib/nori/parser.rb
86
85
  - lib/nori/parser/nokogiri.rb
87
86
  - lib/nori/parser/rexml.rb
88
87
  - lib/nori/string_io_file.rb
@@ -90,13 +89,13 @@ files:
90
89
  - lib/nori/version.rb
91
90
  - lib/nori/xml_utility_node.rb
92
91
  - nori.gemspec
92
+ - spec/nori/api_spec.rb
93
93
  - spec/nori/core_ext/hash_spec.rb
94
94
  - spec/nori/core_ext/object_spec.rb
95
95
  - spec/nori/core_ext/string_spec.rb
96
96
  - spec/nori/nori_spec.rb
97
- - spec/nori/parser_spec.rb
98
97
  - spec/spec_helper.rb
99
- homepage: http://github.com/rubiii/nori
98
+ homepage: https://github.com/savonrb/nori
100
99
  licenses: []
101
100
  post_install_message:
102
101
  rdoc_options: []
@@ -110,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
109
  version: '0'
111
110
  segments:
112
111
  - 0
113
- hash: -2754004364554343980
112
+ hash: 378604752016210991
114
113
  required_rubygems_version: !ruby/object:Gem::Requirement
115
114
  none: false
116
115
  requirements:
@@ -119,17 +118,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
118
  version: '0'
120
119
  segments:
121
120
  - 0
122
- hash: -2754004364554343980
121
+ hash: 378604752016210991
123
122
  requirements: []
124
123
  rubyforge_project: nori
125
- rubygems_version: 1.8.24
124
+ rubygems_version: 1.8.23
126
125
  signing_key:
127
126
  specification_version: 3
128
127
  summary: XML to Hash translator
129
128
  test_files:
129
+ - spec/nori/api_spec.rb
130
130
  - spec/nori/core_ext/hash_spec.rb
131
131
  - spec/nori/core_ext/object_spec.rb
132
132
  - spec/nori/core_ext/string_spec.rb
133
133
  - spec/nori/nori_spec.rb
134
- - spec/nori/parser_spec.rb
135
134
  - spec/spec_helper.rb
@@ -1,51 +0,0 @@
1
- module Nori
2
-
3
- # = Nori::Parser
4
- #
5
- # Manages the parser classes. Currently supports:
6
- #
7
- # * REXML
8
- # * Nokogiri
9
- module Parser
10
-
11
- # The default parser.
12
- DEFAULT = :rexml
13
-
14
- # List of available parsers.
15
- PARSERS = { :rexml => "REXML", :nokogiri => "Nokogiri" }
16
-
17
- # Returns the parser to use. Defaults to <tt>Nori::Parser::REXML</tt>.
18
- def self.use
19
- @use ||= DEFAULT
20
- end
21
-
22
- # Sets the +parser+ to use. Raises an +ArgumentError+ unless the +parser+ exists.
23
- def self.use=(parser)
24
- validate_parser! parser
25
- @use = parser
26
- end
27
-
28
- # Returns the parsed +xml+ using the parser to use. Raises an +ArgumentError+
29
- # unless the optional or default +parser+ exists.
30
- def self.parse(xml, parser = nil, nori = Nori)
31
- load_parser(parser).parse(xml, nori)
32
- end
33
-
34
- private
35
-
36
- # Raises an +ArgumentError+ unless the +parser+ exists.
37
- def self.validate_parser!(parser)
38
- raise ArgumentError, "Invalid Nori parser: #{parser}" unless PARSERS[parser]
39
- end
40
-
41
- # Requires and returns the +parser+ to use.
42
- def self.load_parser(parser)
43
- parser ||= use
44
- validate_parser! parser
45
-
46
- require "nori/parser/#{parser}"
47
- const_get PARSERS[parser]
48
- end
49
-
50
- end
51
- end
@@ -1,54 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Nori::Parser do
4
- let(:parser) { Nori::Parser }
5
-
6
- describe "::PARSERS" do
7
- it "should return a Hash of parser details" do
8
- Nori::Parser::PARSERS.should == { :rexml => "REXML", :nokogiri => "Nokogiri" }
9
- end
10
- end
11
-
12
- describe ".use" do
13
- it "should default to REXML" do
14
- parser.use.should == Nori::Parser::DEFAULT
15
- end
16
-
17
- it "should accept a parser to use" do
18
- parser.use = :nokogiri
19
- parser.use.should == :nokogiri
20
-
21
- # reset to default
22
- parser.use = Nori::Parser::DEFAULT
23
- parser.use.should == Nori::Parser::DEFAULT
24
- end
25
-
26
- it "should raise an ArgumentError in case of an invalid parser" do
27
- lambda { parser.use = :unknown }.should raise_error(ArgumentError)
28
- end
29
- end
30
-
31
- describe ".parse" do
32
- it "should load the parser to use and parse the given xml" do
33
- parser.parse("<some>xml</some>").should == { "some" => "xml" }
34
- end
35
- end
36
-
37
- describe ".parse with different nori" do
38
- let(:other_nori) do
39
- module OtherNori
40
- extend Nori
41
- end
42
- OtherNori.configure do |config|
43
- config.convert_tags_to { |tag| tag.upcase }
44
- end
45
- OtherNori
46
- end
47
-
48
- it "should load the parser to use and parse the given xml" do
49
- parser.parse("<SomeThing>xml</SomeThing>").should == { "SomeThing" => "xml" }
50
- parser.parse("<SomeThing>xml</SomeThing>", nil, other_nori).should == { "SOMETHING" => "xml" }
51
- end
52
- end
53
-
54
- end