diff --git a/CHANGELOG.md b/CHANGELOG.md index e5af3fba..2fa81a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ * Added Savon::SOAP::XML#env_namespace to configure the SOAP envelope namespace. It defaults to :env but can also be set to an empty String for SOAP envelope tags without a namespace. +* Replaced quite a lot of core extensions by moving the Hash to XML translation into a new gem called + Gyoku (http://rubygems.org/gems/gyoku). + ## 0.8.0.beta.4 (2010-11-20) * Fix for issue #107 (Soap Fault regex not working for API I connect). diff --git a/lib/savon/core_ext/array.rb b/lib/savon/core_ext/array.rb deleted file mode 100644 index 6397394d..00000000 --- a/lib/savon/core_ext/array.rb +++ /dev/null @@ -1,45 +0,0 @@ -require "builder" - -require "savon/core_ext/object" -require "savon/core_ext/string" -require "savon/core_ext/hash" -require "savon/core_ext/datetime" - -module Savon - module CoreExt - module Array - - # Translates the Array into SOAP compatible XML. See: Hash.to_soap_xml. - def to_soap_xml(key, escape_xml = true, attributes = {}) - xml = Builder::XmlMarkup.new - - each_with_index do |item, index| - attrs = tag_attributes attributes, index - case item - when ::Hash then xml.tag!(key, attrs) { xml << item.to_soap_xml } - when NilClass then xml.tag!(key, "xsi:nil" => "true") - else xml.tag!(key, attrs) { xml << (escape_xml ? item.to_soap_value : item.to_soap_value!) } - end - end - - xml.target! - end - - private - - # Takes a Hash of +attributes+ and the +index+ for which to return attributes - # for duplicate tags. - def tag_attributes(attributes, index) - return {} if attributes.empty? - - attributes.inject({}) do |hash, (key, value)| - value = value[index] if value.kind_of? ::Array - hash.merge key => value - end - end - - end - end -end - -Array.send :include, Savon::CoreExt::Array diff --git a/lib/savon/core_ext/datetime.rb b/lib/savon/core_ext/datetime.rb deleted file mode 100644 index 8744227f..00000000 --- a/lib/savon/core_ext/datetime.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "date" -require "savon/soap" - -module Savon - module CoreExt - module DateTime - - # Returns the DateTime as an xs:dateTime formatted String. - def to_soap_value - strftime Savon::SOAP::DateTimeFormat - end - - alias_method :to_soap_value!, :to_soap_value - - end - end -end - -DateTime.send :include, Savon::CoreExt::DateTime diff --git a/lib/savon/core_ext/hash.rb b/lib/savon/core_ext/hash.rb index fb02c2a7..bec27c8e 100644 --- a/lib/savon/core_ext/hash.rb +++ b/lib/savon/core_ext/hash.rb @@ -3,9 +3,6 @@ require "savon" require "savon/core_ext/object" require "savon/core_ext/string" -require "savon/core_ext/symbol" -require "savon/core_ext/array" -require "savon/core_ext/datetime" module Savon module CoreExt @@ -19,74 +16,6 @@ def find_soap_body body_key ? envelope[body_key].map_soap_response : {} end - # Translates the Hash into SOAP request compatible XML. - # - # { :find_user => { :id => 123, "wsdl:Key" => "api" } }.to_soap_xml - # # => "123api" - # - # ==== Mapping - # - # * Hash keys specified as Symbols are converted to lowerCamelCase Strings - # * Hash keys specified as Strings are not converted and may contain namespaces - # * DateTime values are converted to xs:dateTime Strings - # * Objects responding to to_datetime (except Strings) are converted to xs:dateTime Strings - # * TrueClass and FalseClass objects are converted to "true" and "false" Strings - # * All other objects are expected to be converted to Strings using to_s - # - # An example: - # - # { :magic_request => { - # :perform_move => true, - # "perform_at" => DateTime.new(2010, 11, 22, 11, 22, 33) - # } - # }.to_soap_xml - # - # - # true - # 2012-06-11T10:42:21 - # - # - # ==== Escaped XML values - # - # By default, special characters in XML String values are escaped. - # - # ==== Fixed order of XML tags - # - # In case your service requires the tags to be in a specific order (parameterOrder), you have two - # options. The first is to specify your body as an XML string. The second is to specify the order - # through an additional array stored under the +:order!+ key. - # - # { :name => "Eve", :id => 123, :order! => [:id, :name] }.to_soap_xml - # # => "123Eve" - # - # ==== XML attributes - # - # If you need attributes, you could either go with an XML string or add another hash under the - # +:attributes!+ key. - # - # { :person => "Eve", :attributes! => { :person => { :id => 666 } } }.to_soap_xml - # # => 'Eve' - def to_soap_xml - xml = Builder::XmlMarkup.new - attributes = delete(:attributes!) || {} - - order.each do |key| - attrs = attributes[key] || {} - value = self[key] - escape_xml = key.to_s[-1, 1] != "!" - key = key.to_soap_key - - case value - when ::Array then xml << value.to_soap_xml(key, escape_xml, attrs) - when ::Hash then xml.tag!(key, attrs) { xml << value.to_soap_xml } - when NilClass then xml.tag!(key, "xsi:nil" => "true") - else xml.tag!(key, attrs) { xml << (escape_xml ? value.to_soap_value : value.to_soap_value!) } - end - end - - xml.target! - end - # Maps keys and values of a Hash created from SOAP response XML to more convenient Ruby Objects. def map_soap_response inject({}) do |hash, (key, value)| @@ -112,22 +41,6 @@ def map_soap_response end end - private - - # Deletes and returns an Array of keys stored under the :order! key. Defaults to return the actual - # keys of this Hash if no :order! key could be found. Raises an ArgumentError in case the :order! - # Array does not match the Hash keys. - def order - order = delete :order! - order = keys unless order.kind_of? ::Array - - missing, spurious = keys - order, order - keys - raise ArgumentError, "Missing elements in :order! #{missing.inspect}" unless missing.empty? - raise ArgumentError, "Spurious elements in :order! #{spurious.inspect}" unless spurious.empty? - - order - end - end end end diff --git a/lib/savon/core_ext/object.rb b/lib/savon/core_ext/object.rb index 48e792d9..d8b60ddd 100644 --- a/lib/savon/core_ext/object.rb +++ b/lib/savon/core_ext/object.rb @@ -1,5 +1,3 @@ -require "savon/core_ext/datetime" - module Savon module CoreExt module Object @@ -9,14 +7,6 @@ def blank? respond_to?(:empty?) ? empty? : !self end unless defined? blank? - # Returns the Object as a SOAP request compliant value. - def to_soap_value - return to_s unless respond_to? :to_datetime - to_datetime.to_soap_value - end - - alias_method :to_soap_value!, :to_soap_value - end end end diff --git a/lib/savon/core_ext/string.rb b/lib/savon/core_ext/string.rb index b8b5b9c8..2fbe0775 100644 --- a/lib/savon/core_ext/string.rb +++ b/lib/savon/core_ext/string.rb @@ -1,5 +1,3 @@ -require "cgi" - require "savon/soap" module Savon @@ -52,21 +50,6 @@ def map_soap_response self end - # Returns the Object as a SOAP request compliant key. - def to_soap_key - self[-1, 1] == "!" ? chop : self - end - - # Returns the String as a SOAP value. Escapes special characters for XML. - def to_soap_value - CGI.escapeHTML self - end - - # Convert the String into a SOAP value without escaping special characters. - def to_soap_value! - self - end - end end end diff --git a/lib/savon/core_ext/symbol.rb b/lib/savon/core_ext/symbol.rb deleted file mode 100644 index 72692b24..00000000 --- a/lib/savon/core_ext/symbol.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "savon/core_ext/string" - -module Savon - module CoreExt - module Symbol - - # Returns the Symbol as a lowerCamelCase String. - def to_soap_key - to_s.to_soap_key.lower_camelcase - end - - end - end -end - -Symbol.send :include, Savon::CoreExt::Symbol diff --git a/lib/savon/soap/xml.rb b/lib/savon/soap/xml.rb index 81247a7b..8b0c3f83 100644 --- a/lib/savon/soap/xml.rb +++ b/lib/savon/soap/xml.rb @@ -87,7 +87,7 @@ def namespace_identifier # Accessor for the Savon::WSSE object. attr_accessor :wsse - # Accessor for the SOAP +body+. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml + # Accessor for the SOAP +body+. Expected to be a Hash that can be translated to XML via Gyoku.xml # or any other Object responding to to_s. attr_accessor :body @@ -132,7 +132,7 @@ def complete_namespaces # Returns the SOAP header as an XML String. def header_for_xml - @header_for_xml ||= header.to_soap_xml + wsse_header + @header_for_xml ||= Gyoku.xml(header) + wsse_header end # Returns the WSSE header or an empty String in case WSSE was not set. @@ -142,7 +142,7 @@ def wsse_header # Returns the SOAP body as an XML String. def body_to_xml - body.respond_to?(:to_soap_xml) ? body.to_soap_xml : body.to_s + body.kind_of?(Hash) ? Gyoku.xml(body) : body.to_s end end diff --git a/savon.gemspec b/savon.gemspec index 82a42513..21315cfe 100644 --- a/savon.gemspec +++ b/savon.gemspec @@ -17,6 +17,7 @@ Gem::Specification.new do |s| s.add_dependency "builder", "~> 2.1.2" s.add_dependency "crack", "~> 0.1.8" s.add_dependency "httpi", ">= 0.7.1" + s.add_dependency "gyoku", ">= 0.1.0" s.add_development_dependency "rspec", "~> 2.0.0" s.add_development_dependency "mocha", "~> 0.9.7" diff --git a/spec/savon/core_ext/array_spec.rb b/spec/savon/core_ext/array_spec.rb deleted file mode 100644 index c5255f1c..00000000 --- a/spec/savon/core_ext/array_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require "spec_helper" - -describe Array do - - describe "to_soap_xml" do - it "should return the XML for an Array of Hashes" do - array = [{ :name => "adam" }, { :name => "eve" }] - result = "adameve" - - array.to_soap_xml("user").should == result - end - - it "should return the XML for an Array of different Objects" do - array = [:symbol, "string", 123] - result = "symbolstring123" - - array.to_soap_xml("value").should == result - end - - it "should default to escape special characters" do - array = ["", "adam & eve"] - result = "<tag />adam & eve" - - array.to_soap_xml("value").should == result - end - - it "should not escape special characters when told to" do - array = ["", "adam & eve"] - result = "adam & eve" - - array.to_soap_xml("value", false).should == result - end - - it "should add attributes to a given tag" do - array = ["adam", "eve"] - result = 'adameve' - - array.to_soap_xml("value", :escape_xml, :active => true).should == result - end - - it "should add attributes to duplicate tags" do - array = ["adam", "eve"] - result = 'adameve' - - array.to_soap_xml("value", :escape_xml, :id => [1, 2]).should == result - end - end - -end \ No newline at end of file diff --git a/spec/savon/core_ext/datetime_spec.rb b/spec/savon/core_ext/datetime_spec.rb deleted file mode 100644 index f8c3d830..00000000 --- a/spec/savon/core_ext/datetime_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require "spec_helper" - -describe DateTime do - before do - @datetime = DateTime.new 2012, 03, 22, 16, 22, 33 - @datetime_string = "2012-03-22T16:22:33+00:00" - end - - describe "to_soap_value" do - it "should return an xs:dateTime compliant String" do - @datetime.to_soap_value.should == @datetime_string - end - end - - describe "to_soap_value!" do - it "should act like :to_soap_value" do - @datetime.to_soap_value.should == @datetime_string - end - end - -end diff --git a/spec/savon/core_ext/hash_spec.rb b/spec/savon/core_ext/hash_spec.rb index d120bc6a..5dd9e49b 100644 --- a/spec/savon/core_ext/hash_spec.rb +++ b/spec/savon/core_ext/hash_spec.rb @@ -14,132 +14,6 @@ end end - describe "to_soap_xml" do - describe "should return SOAP request compatible XML" do - it "for a simple Hash" do - hash, result = { :some => "user" }, "user" - hash.to_soap_xml.should == result - end - - it "for a nested Hash" do - hash, result = { :some => { :new => "user" } }, "user" - hash.to_soap_xml.should == result - end - - it "for a Hash with multiple keys" do - hash = { :all => "users", :before => "whatever" } - hash.to_soap_xml.should include("users", "whatever") - end - - it "for a Hash containing an Array" do - hash, result = { :some => ["user", "gorilla"] }, "usergorilla" - hash.to_soap_xml.should == result - end - - it "for a Hash containing an Array of Hashes" do - hash = { :some => [{ :new => "user" }, { :old => "gorilla" }] } - result = "usergorilla" - - hash.to_soap_xml.should == result - end - end - - it "should convert Hash key Symbols to lowerCamelCase" do - hash, result = { :find_or_create => "user" }, "user" - hash.to_soap_xml.should == result - end - - it "should not convert Hash key Strings" do - hash, result = { "find_or_create" => "user" }, "user" - hash.to_soap_xml.should == result - end - - it "should convert DateTime objects to xs:dateTime compliant Strings" do - hash = { :before => DateTime.new(2012, 03, 22, 16, 22, 33) } - result = "2012-03-22T16:22:33+00:00" - - hash.to_soap_xml.should == result - end - - it "should convert Objects responding to to_datetime to xs:dateTime compliant Strings" do - singleton = Object.new - def singleton.to_datetime - DateTime.new(2012, 03, 22, 16, 22, 33) - end - - hash, result = { :before => singleton }, "2012-03-22T16:22:33+00:00" - hash.to_soap_xml.should == result - end - - it "should call to_s on Strings even if they respond to to_datetime" do - object = "gorilla" - object.expects(:to_datetime).never - - hash, result = { :name => object }, "gorilla" - hash.to_soap_xml.should == result - end - - it "should properly serialize nil values" do - { :some => nil }.to_soap_xml.should == '' - end - - it "should call to_s on any other Object" do - [666, true, false].each do |object| - { :some => object }.to_soap_xml.should == "#{object}" - end - end - - it "should default to escape special characters" do - result = { :some => { :nested => "" }, :tag => "" }.to_soap_xml - result.should include("<tag />") - result.should include("<tag />") - end - - it "should not escape special characters for keys marked with an exclamation mark" do - result = { :some => { :nested! => "" }, :tag! => "" }.to_soap_xml - result.should include("") - result.should include("") - end - - it "should preserve the order of Hash keys and values specified through :order!" do - hash = { :find_user => { :name => "Lucy", :id => 666, :order! => [:id, :name] } } - result = "666Lucy" - hash.to_soap_xml.should == result - - hash = { :find_user => { :mname => "in the", :lname => "Sky", :fname => "Lucy", :order! => [:fname, :mname, :lname] } } - result = "Lucyin theSky" - hash.to_soap_xml.should == result - end - - it "should raise an error if the :order! Array does not match the Hash keys" do - hash = { :name => "Lucy", :id => 666, :order! => [:name] } - lambda { hash.to_soap_xml }.should raise_error(ArgumentError) - - hash = { :by_name => { :name => "Lucy", :lname => "Sky", :order! => [:mname, :name] } } - lambda { hash.to_soap_xml }.should raise_error(ArgumentError) - end - - it "should add attributes to Hash keys specified through :attributes!" do - hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666 } } } } - result = 'Lucy' - hash.to_soap_xml.should == result - - hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666, :city => "Hamburg" } } } } - soap_xml = hash.to_soap_xml - soap_xml.should include('id="666"', 'city="Hamburg"') - end - - it "should add attributes to duplicate Hash keys specified through :attributes!" do - hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :id => [1, 3] } } } } - result = 'LucyAnna' - hash.to_soap_xml.should == result - - hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :active => "true" } } } } - result = 'LucyAnna' - hash.to_soap_xml.should == result - end - end - describe "map_soap_response" do it "should convert Hash key Strings to snake_case Symbols" do soap_response = { "userResponse" => { "accountStatus" => "active" } } diff --git a/spec/savon/core_ext/object_spec.rb b/spec/savon/core_ext/object_spec.rb index 6a3d7e42..d982a9fb 100644 --- a/spec/savon/core_ext/object_spec.rb +++ b/spec/savon/core_ext/object_spec.rb @@ -16,19 +16,4 @@ end end - describe "to_soap_value" do - it "returns an xs:dateTime compliant String for Objects responding to to_datetime" do - singleton = Object.new - def singleton.to_datetime - DateTime.new(2012, 03, 22, 16, 22, 33) - end - - singleton.to_soap_value.should == "2012-03-22T16:22:33+00:00" - end - - it "calls to_s unless the Object responds to to_datetime" do - "value".to_soap_value.should == "value".to_s - end - end - end diff --git a/spec/savon/core_ext/string_spec.rb b/spec/savon/core_ext/string_spec.rb index d00388e0..0958838a 100644 --- a/spec/savon/core_ext/string_spec.rb +++ b/spec/savon/core_ext/string_spec.rb @@ -62,26 +62,4 @@ end end - describe "to_soap_key" do - it "removes exclamation marks from the end of the String" do - "value".to_soap_key.should == "value" - "value!".to_soap_key.should == "value" - end - end - - describe "to_soap_value" do - it "should return the String value and escape special characters" do - "string".to_soap_value.should == "string" - "".to_soap_value.should == "<tag>" - "at&t".to_soap_value.should == "at&t" - '"quotes"'.to_soap_value.should == ""quotes"" - end - end - - describe "to_soap_value!" do - it "should just return the String value without escaping special characters" do - "".to_soap_value!.should == "" - end - end - end diff --git a/spec/savon/core_ext/symbol_spec.rb b/spec/savon/core_ext/symbol_spec.rb deleted file mode 100644 index 4d1859ec..00000000 --- a/spec/savon/core_ext/symbol_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require "spec_helper" - -describe Symbol do - - describe "to_soap_key" do - it "converts the Symbol from snake_case to a lowerCamelCase String" do - :lower_camel_case.to_soap_key.should == "lowerCamelCase" - :lower_camel_case!.to_soap_key.should == "lowerCamelCase" - end - end - -end