From 62fef7ffdb28c80190295e950781d08076009f38 Mon Sep 17 00:00:00 2001 From: Mikael Henriksson Date: Wed, 4 Sep 2013 19:55:38 +0200 Subject: [PATCH] Per SOAP Service configuration --- README.md | 16 +++- app/helpers/wash_out_helper.rb | 2 +- .../wash_with_soap/document/response.builder | 2 +- .../wash_with_soap/document/wsdl.builder | 4 +- app/views/wash_with_soap/rpc/response.builder | 2 +- app/views/wash_with_soap/rpc/wsdl.builder | 4 +- lib/wash_out.rb | 21 ++++- lib/wash_out/configurable.rb | 41 +++++++++ lib/wash_out/dispatcher.rb | 36 +++++--- lib/wash_out/engine.rb | 44 +-------- lib/wash_out/param.rb | 32 +++---- lib/wash_out/router.rb | 4 +- lib/wash_out/soap.rb | 12 ++- lib/wash_out/soap_config.rb | 91 +++++++++++++++++++ lib/wash_out/type.rb | 9 +- lib/wash_out/wsse.rb | 15 +-- spec/lib/wash_out/dispatcher_spec.rb | 12 +-- spec/lib/wash_out/param_spec.rb | 15 ++- spec/lib/wash_out/type_spec.rb | 2 + spec/lib/wash_out_spec.rb | 38 +++----- spec/spec_helper.rb | 17 ++-- wash_out.gemspec | 5 +- 22 files changed, 272 insertions(+), 152 deletions(-) create mode 100644 lib/wash_out/configurable.rb create mode 100644 lib/wash_out/soap_config.rb diff --git a/README.md b/README.md index b9f4d183..43f4ef15 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ In your Gemfile, add this line: gem 'wash_out' +## Upgrading from version < 0.8.5 + +Replace `include WashOut::Soap` with `soap_service` at the controller level. + ## Usage A SOAP endpoint in WashOut is simply a Rails controller which includes the module WashOut::SOAP. Each SOAP @@ -34,7 +38,7 @@ demonstrated. ```ruby # app/controllers/rumbas_controller.rb class RumbasController < ApplicationController - include WashOut::SOAP + soap_service namespace: 'urn:WashOut' # Simple case soap_action "integer_to_string", @@ -139,7 +143,8 @@ inside your interface declarations. ## Configuration -Use `config.wash_out...` inside your environment configuration to setup WashOut. +Use `config.wash_out...` inside your environment configuration to setup WashOut globally. +To override the values on a specific controller just add an override as part of the arguments to the `soap_service` method. Available properties are: @@ -149,7 +154,7 @@ Available properties are: * **namespace**: SOAP namespace to use. Default is `urn:WashOut`. * **snakecase**: *(DEPRECATED SINCE 0.4.0)* Determines if WashOut should modify parameters keys to snakecase. Default is `false`. * **snakecase_input**: Determines if WashOut should modify parameters keys to snakecase. Default is `false`. -* **camelize_wsdl**: Determines if WashOut should camelize types within WSDL and responses. Default is `false`. +* **camelize_wsdl**: Determines if WashOut should camelize types within WSDL and responses. Supports `true` for CamelCase and `:lower` for camelCase. Default is `false`. ### Camelization @@ -164,6 +169,9 @@ soap_action "foo" # this will be passed as is * Boris Staal, [@inossidabile](http://staal.io) +## Contributors +* Mikael Henriksson, [@mhenrixon](http://twitter.com/mhenrixon) + ## License -It is free software, and may be redistributed under the terms of MIT license. \ No newline at end of file +It is free software, and may be redistributed under the terms of MIT license. diff --git a/app/helpers/wash_out_helper.rb b/app/helpers/wash_out_helper.rb index 34fabcd0..f9c23163 100644 --- a/app/helpers/wash_out_helper.rb +++ b/app/helpers/wash_out_helper.rb @@ -1,7 +1,7 @@ module WashOutHelper def wsdl_data_options(param) - case WashOut::Engine.style + case controller.soap_config.wsdl_style when 'rpc' { :"xsi:type" => param.namespaced_type } when 'document' diff --git a/app/views/wash_with_soap/document/response.builder b/app/views/wash_with_soap/document/response.builder index 63cc2f1b..34748226 100644 --- a/app/views/wash_with_soap/document/response.builder +++ b/app/views/wash_with_soap/document/response.builder @@ -3,7 +3,7 @@ xml.tag! "soap:Envelope", "xmlns:soap" => 'http://schemas.xmlsoap.org/soap/envel "xmlns:xsd" => 'http://www.w3.org/2001/XMLSchema', "xmlns:tns" => @namespace do xml.tag! "soap:Body" do - key = "tns:#{@operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" + key = "tns:#{@operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" xml.tag! @action_spec[:response_tag] do wsdl_data xml, result diff --git a/app/views/wash_with_soap/document/wsdl.builder b/app/views/wash_with_soap/document/wsdl.builder index 6bf7b083..bd0df374 100644 --- a/app/views/wash_with_soap/document/wsdl.builder +++ b/app/views/wash_with_soap/document/wsdl.builder @@ -23,7 +23,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/', @map.keys.each do |operation| xml.operation :name => operation do xml.input :message => "tns:#{operation}" - xml.output :message => "tns:#{operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" + xml.output :message => "tns:#{operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" end end end @@ -59,7 +59,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/', xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type) end end - xml.message :name => "#{operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" do + xml.message :name => "#{operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" do formats[:out].each do |p| xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type) end diff --git a/app/views/wash_with_soap/rpc/response.builder b/app/views/wash_with_soap/rpc/response.builder index 5e88fd52..461a572d 100644 --- a/app/views/wash_with_soap/rpc/response.builder +++ b/app/views/wash_with_soap/rpc/response.builder @@ -4,7 +4,7 @@ xml.tag! "soap:Envelope", "xmlns:soap" => 'http://schemas.xmlsoap.org/soap/envel "xmlns:xsi" => 'http://www.w3.org/2001/XMLSchema-instance', "xmlns:tns" => @namespace do xml.tag! "soap:Body" do - key = "tns:#{@operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" + key = "tns:#{@operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" xml.tag! @action_spec[:response_tag] do wsdl_data xml, result diff --git a/app/views/wash_with_soap/rpc/wsdl.builder b/app/views/wash_with_soap/rpc/wsdl.builder index 29645414..a1cae99a 100644 --- a/app/views/wash_with_soap/rpc/wsdl.builder +++ b/app/views/wash_with_soap/rpc/wsdl.builder @@ -23,7 +23,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/', @map.keys.each do |operation| xml.operation :name => operation do xml.input :message => "tns:#{operation}" - xml.output :message => "tns:#{operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" + xml.output :message => "tns:#{operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" end end end @@ -59,7 +59,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/', xml.part wsdl_occurence(p, true, :name => p.name, :type => p.namespaced_type) end end - xml.message :name => "#{operation}#{WashOut::Engine.camelize_wsdl ? 'Response' : '_response'}" do + xml.message :name => "#{operation}#{controller.soap_config.camelize_wsdl ? 'Response' : '_response'}" do formats[:out].each do |p| xml.part wsdl_occurence(p, true, :name => p.name, :type => p.namespaced_type) end diff --git a/lib/wash_out.rb b/lib/wash_out.rb index 56f63797..8107922d 100644 --- a/lib/wash_out.rb +++ b/lib/wash_out.rb @@ -1,3 +1,6 @@ +require 'wash_out/configurable' +require 'wash_out/soap_config' +require 'wash_out/soap' require 'wash_out/engine' require 'wash_out/param' require 'wash_out/dispatcher' @@ -28,9 +31,17 @@ def wash_out(controller_name, options={}) _render_soap(what, options) end -module ActionView - class Base - cattr_accessor :washout_namespace - @@washout_namespace = false +ActionController::Base.class_eval do + + # Define a SOAP service. The function has no required +options+: + # but allow any of :parser, :namespace, :wsdl_style, :snakecase_input, + # :camelize_wsdl, :wsse_username, :wsse_password and :catch_xml_errors. + # + # Any of the the params provided allows for overriding the defaults + # (like supporting multiple namespaces instead of application wide such) + # + def self.soap_service(options={}) + include WashOut::SOAP + self.soap_config = options end -end +end \ No newline at end of file diff --git a/lib/wash_out/configurable.rb b/lib/wash_out/configurable.rb new file mode 100644 index 00000000..38beeead --- /dev/null +++ b/lib/wash_out/configurable.rb @@ -0,0 +1,41 @@ +module WashOut + module Configurable + extend ActiveSupport::Concern + + included do + cattr_reader :soap_config + class_variable_set :@@soap_config, WashOut::SoapConfig.new({}) + end + + module ClassMethods + + def soap_config=(obj) + + unless obj.is_a?(Hash) + raise "Value needs to be a Hash." + end + + if class_variable_defined?(:@@soap_config) + class_variable_get(:@@soap_config).configure obj + else + class_variable_set :@@soap_config, WashOut::SoapConfig.new(obj) + end + end + end + + def soap_config=(obj) + + unless obj.is_a?(Hash) + raise "Value needs to be a Hash." + end + + class_eval do + if class_variable_defined?(:@@soap_config) + class_variable_get(:@@soap_config).configure obj + else + class_variable_set :@@soap_config, WashOut::SoapConfig.new(obj) + end + end + end + end +end \ No newline at end of file diff --git a/lib/wash_out/dispatcher.rb b/lib/wash_out/dispatcher.rb index a60df46e..06749e30 100644 --- a/lib/wash_out/dispatcher.rb +++ b/lib/wash_out/dispatcher.rb @@ -12,14 +12,14 @@ class ProgrammerError < Exception; end # This filter parses the SOAP request and puts it into +params+ array. def _parse_soap_parameters - parser = Nori.new( - :parser => WashOut::Engine.parser, + + nori_parser = Nori.new( + :parser => soap_config.parser, :strip_namespaces => true, :advanced_typecasting => true, - :convert_tags_to => ( WashOut::Engine.snakecase_input ? lambda { |tag| tag.snakecase.to_sym } \ - : lambda { |tag| tag.to_sym } )) + :convert_tags_to => ( soap_config.snakecase_input ? lambda { |tag| tag.snakecase.to_sym } : lambda { |tag| tag.to_sym } )) - @_params = parser.parse(request.body.read) + @_params = nori_parser.parse(request.body.read) references = WashOut::Dispatcher.deep_select(@_params){|k,v| v.is_a?(Hash) && v.has_key?(:@id)} unless references.blank? @@ -29,6 +29,7 @@ def _parse_soap_parameters end def _authenticate_wsse + begin xml_security = @_params.values_at(:envelope, :Envelope).compact.first xml_security = xml_security.values_at(:header, :Header).compact.first @@ -38,12 +39,13 @@ def _authenticate_wsse username_token = nil end - WashOut::Wsse.authenticate username_token + WashOut::Wsse.authenticate soap_config, username_token request.env['WSSE_TOKEN'] = username_token.with_indifferent_access unless username_token.blank? end def _map_soap_parameters + soap_action = request.env['wash_out.soap_action'] action_spec = self.class.soap_actions[soap_action] @@ -55,7 +57,7 @@ def _map_soap_parameters strip_empty_nodes = lambda{|hash| hash.keys.each do |key| if hash[key].is_a? Hash - value = hash[key].delete_if{|key, value| key.to_s[0] == '@'} + value = hash[key].delete_if{|k, v| key.to_s[0] == '@'} if value.length > 0 hash[key] = strip_empty_nodes.call(value) @@ -85,17 +87,18 @@ def _load_params(spec, xml_data) # This action generates the WSDL for defined SOAP methods. def _generate_wsdl + @map = self.class.soap_actions - @namespace = WashOut::Engine.namespace + @namespace = soap_config.namespace @name = controller_path.gsub('/', '_') - render :template => "wash_with_soap/#{WashOut::Engine.style}/wsdl", :layout => false, + render :template => "wash_with_soap/#{soap_config.wsdl_style}/wsdl", :layout => false, :content_type => 'text/xml' end # Render a SOAP response. def _render_soap(result, options) - @namespace = WashOut::Engine.namespace + @namespace = soap_config.namespace @operation = soap_action = request.env['wash_out.soap_action'] @action_spec = self.class.soap_actions[soap_action] @@ -144,7 +147,7 @@ def _render_soap(result, options) return result_spec } - render :template => "wash_with_soap/#{WashOut::Engine.style}/response", + render :template => "wash_with_soap/#{soap_config.wsdl_style}/response", :layout => false, :locals => { :result => inject.call(result, @action_spec[:out]) }, :content_type => 'text/xml' @@ -164,7 +167,7 @@ def _render_soap_exception(error) # Rails do not support sequental rescue_from handling, that is, rescuing an # exception from a rescue_from handler. Hence this function is a public API. def render_soap_error(message) - render :template => "wash_with_soap/#{WashOut::Engine.style}/error", :status => 500, + render :template => "wash_with_soap/#{soap_config.wsdl_style}/error", :status => 500, :layout => false, :locals => { :error_message => message }, :content_type => 'text/xml' @@ -173,9 +176,12 @@ def render_soap_error(message) def self.included(controller) controller.send :rescue_from, SOAPError, :with => :_render_soap_exception controller.send :helper, :wash_out - controller.send :before_filter, :_parse_soap_parameters, :except => [ :_generate_wsdl, :_invalid_action ] - controller.send :before_filter, :_authenticate_wsse, :except => [ :_generate_wsdl, :_invalid_action ] - controller.send :before_filter, :_map_soap_parameters, :except => [ :_generate_wsdl, :_invalid_action ] + controller.send :before_filter, :_parse_soap_parameters, :except => [ + :_generate_wsdl, :_invalid_action ] + controller.send :before_filter, :_authenticate_wsse, :except => [ + :_generate_wsdl, :_invalid_action ] + controller.send :before_filter, :_map_soap_parameters, :except => [ + :_generate_wsdl, :_invalid_action ] controller.send :skip_before_filter, :verify_authenticity_token end diff --git a/lib/wash_out/engine.rb b/lib/wash_out/engine.rb index 79c5c4e4..72e42dc6 100644 --- a/lib/wash_out/engine.rb +++ b/lib/wash_out/engine.rb @@ -1,49 +1,9 @@ + module WashOut class Engine < ::Rails::Engine - class << self - attr_accessor :parser - attr_accessor :namespace - attr_accessor :style - attr_accessor :snakecase, :camelize_output - attr_accessor :snakecase_input, :camelize_wsdl - attr_accessor :wsse_username, :wsse_password - attr_accessor :catch_xml_errors - end - - self.parser = :rexml - - self.namespace = 'urn:WashOut' - self.style = 'rpc' - self.snakecase = nil - - self.snakecase_input = false - self.camelize_wsdl = false - - self.wsse_username = nil - self.wsse_password = nil - config.wash_out = ActiveSupport::OrderedOptions.new - initializer "wash_out.configuration" do |app| - app.config.wash_out.each do |key, value| - self.class.send "#{key}=", value - end - - app.config.wash_out.catch_xml_errors ||= false - - unless self.class.snakecase.nil? - raise "Usage of wash_out.snakecase is deprecated. You should use wash_out.snakecase_input and wash_out.camelize_wsdl" - end - - unless self.class.camelize_output.nil? - raise "Usage of wash_out.camelize_output is deprecated. You should use wash_out.camelize_wsdl option instead" - end - - unless ['rpc','document'].include? self.class.style - raise "Invalid binding. Style must be one of #{['rpc','document'].join(',')}" - end - - if self.class.catch_xml_errors + if app.config.wash_out[:catch_xml_errors] app.config.middleware.insert_after 'ActionDispatch::ShowExceptions', WashOut::Middleware end end diff --git a/lib/wash_out/param.rb b/lib/wash_out/param.rb index f47f9a7f..54dc3caf 100644 --- a/lib/wash_out/param.rb +++ b/lib/wash_out/param.rb @@ -10,17 +10,17 @@ class Param # Defines a WSDL parameter with name +name+ and type specifier +type+. # The type specifier format is described in #parse_def. - def initialize(name, type, multiplied = false) + def initialize(soap_config, name, type, multiplied = false) type ||= {} - + @soap_config = soap_config @name = name.to_s @raw_name = name.to_s @map = {} @multiplied = multiplied - if WashOut::Engine.camelize_wsdl.to_s == 'lower' + if soap_config.camelize_wsdl.to_s == 'lower' @name = @name.camelize(:lower) - elsif WashOut::Engine.camelize_wsdl + elsif soap_config.camelize_wsdl @name = @name.camelize end @@ -28,11 +28,11 @@ def initialize(name, type, multiplied = false) @type = type.to_s elsif type.is_a?(Class) @type = 'struct' - @map = self.class.parse_def(type.wash_out_param_map) + @map = self.class.parse_def(soap_config, type.wash_out_param_map) @source_class = type else @type = 'struct' - @map = self.class.parse_def(type) + @map = self.class.parse_def(soap_config, type) end end @@ -50,13 +50,13 @@ def load(data, key) data ||= {} if @multiplied data.map do |x| - map_struct x do |param, data, elem| - param.load(data, elem) + map_struct x do |param, dat, elem| + param.load(dat, elem) end end else - map_struct data do |param, data, elem| - param.load(data, elem) + map_struct data do |param, dat, elem| + param.load(dat, elem) end end else @@ -64,7 +64,7 @@ def load(data, key) when 'string'; :to_s when 'integer'; :to_i when 'double'; :to_f - when 'boolean'; lambda{|data| data === "0" ? false : !!data} + when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat} when 'date'; :to_date when 'datetime'; :to_datetime when 'time'; :to_time @@ -99,7 +99,7 @@ def classified? def basic_type return name unless classified? - return source_class.wash_out_param_name + return source_class.wash_out_param_name(@soap_config) end def xsd_type @@ -129,7 +129,7 @@ def namespaced_type # +:parameter_name+ is ignored. # # This function returns an array of WashOut::Param objects. - def self.parse_def(definition) + def self.parse_def(soap_config, definition) raise RuntimeError, "[] should not be used in your params. Use nil if you want to mark empty set." if definition == [] return [] if definition == nil @@ -146,9 +146,9 @@ def self.parse_def(definition) if opt.is_a? WashOut::Param opt elsif opt.is_a? Array - WashOut::Param.new(name, opt[0], true) + WashOut::Param.new(soap_config, name, opt[0], true) else - WashOut::Param.new(name, opt) + WashOut::Param.new(soap_config, name, opt) end end else @@ -157,7 +157,7 @@ def self.parse_def(definition) end def flat_copy - copy = self.class.new(@name, @type.to_sym, @multiplied) + copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied) copy.raw_name = raw_name copy end diff --git a/lib/wash_out/router.rb b/lib/wash_out/router.rb index b5a46e2f..3ad9fea7 100644 --- a/lib/wash_out/router.rb +++ b/lib/wash_out/router.rb @@ -13,8 +13,8 @@ def call(env) # RUBY18 1.8 does not have force_encoding. soap_action.force_encoding('UTF-8') if soap_action.respond_to? :force_encoding - if WashOut::Engine.namespace - namespace = Regexp.escape WashOut::Engine.namespace.to_s + if controller.soap_config.namespace + namespace = Regexp.escape controller.soap_config.namespace.to_s soap_action.gsub!(/^\"(#{namespace}(\/|#)?)?(.*)\"$/, '\3') else soap_action = soap_action[1...-1] diff --git a/lib/wash_out/soap.rb b/lib/wash_out/soap.rb index 8cf24bc2..2ee23ed0 100644 --- a/lib/wash_out/soap.rb +++ b/lib/wash_out/soap.rb @@ -15,21 +15,22 @@ module ClassMethods # which are not valid Ruby function names. def soap_action(action, options={}) if action.is_a?(Symbol) - if WashOut::Engine.camelize_wsdl.to_s == 'lower' + if soap_config.camelize_wsdl.to_s == 'lower' options[:to] ||= action.to_s action = action.to_s.camelize(:lower) - elsif WashOut::Engine.camelize_wsdl + elsif soap_config.camelize_wsdl options[:to] ||= action.to_s action = action.to_s.camelize end + end - default_response_tag = WashOut::Engine.camelize_wsdl ? 'Response' : '_response' + default_response_tag = soap_config.camelize_wsdl ? 'Response' : '_response' default_response_tag = "tns:#{action}#{default_response_tag}" self.soap_actions[action] = { - :in => WashOut::Param.parse_def(options[:args]), - :out => WashOut::Param.parse_def(options[:return]), + :in => WashOut::Param.parse_def(soap_config, options[:args]), + :out => WashOut::Param.parse_def(soap_config, options[:return]), :to => options[:to] || action, :response_tag => options[:response_tag] || default_response_tag } @@ -37,6 +38,7 @@ def soap_action(action, options={}) end included do + include WashOut::Configurable include WashOut::Dispatcher self.soap_actions = {} end diff --git a/lib/wash_out/soap_config.rb b/lib/wash_out/soap_config.rb new file mode 100644 index 00000000..f36d1918 --- /dev/null +++ b/lib/wash_out/soap_config.rb @@ -0,0 +1,91 @@ +module WashOut + require 'forwardable' + # Configuration options for {Client}, defaulting to values + # in {Default} + class SoapConfig + extend Forwardable + DEFAULT_CONFIG = { + parser: :rexml, + namespace: 'urn:WashOut', + wsdl_style: 'rpc', + snakecase_input: false, + camelize_wsdl: false, + catch_xml_errors: false, + wsse_username: nil, + wsse_password: nil, + } + + attr_reader :config + def_delegators :@config, :[], :[]=, :sort + + + # The keys allowed + def self.keys + @keys ||= config.keys + end + + def self.config + DEFAULT_CONFIG + end + + def self.soap_accessor(*syms) + syms.each do |sym| + + unless sym =~ /^[_A-Za-z]\w*$/ + raise NameError.new("invalid class attribute name: #{sym}") + end + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + unless defined? @#{sym} + @#{sym} = nil + end + + def #{sym} + @#{sym} + end + + def #{sym}=(obj) + @#{sym} = obj + end + EOS + end + end + + soap_accessor(*WashOut::SoapConfig.keys) + + def initialize(options = {}) + @config = {} + options.reverse_merge!(engine_config) if engine_config + options.reverse_merge!(DEFAULT_CONFIG) + configure options + end + + def default? + DEFAULT_CONFIG.sort == config.sort + end + + def configure(options = {}) + @config.merge! validate_config!(options) + + config.each do |key,value| + send("#{key}=", value) + end + end + + private + + def engine_config + @engine_config ||= WashOut::Engine.config.wash_out + end + + def validate_config!(options = {}) + rejected_keys = options.keys.reject do |key| + WashOut::SoapConfig.keys.include?(key) + end + + if rejected_keys.any? + raise "The following keys are not allows: #{rejected_keys}\n Did you intend for one of the following? #{WashOut::SoapConfig.keys}" + end + options + end + end +end diff --git a/lib/wash_out/type.rb b/lib/wash_out/type.rb index b2cce8b2..7aaea958 100644 --- a/lib/wash_out/type.rb +++ b/lib/wash_out/type.rb @@ -1,5 +1,6 @@ module WashOut class Type + def self.type_name(value) @param_type_name = value end @@ -13,15 +14,15 @@ def self.wash_out_param_map @param_map end - def self.wash_out_param_name + def self.wash_out_param_name(soap_config = nil) + soap_config ||= WashOut::SoapConfig.new({}) @param_type_name ||= name.underscore.gsub '/', '.' - if WashOut::Engine.camelize_wsdl.to_s == 'lower' + if soap_config.camelize_wsdl.to_s == 'lower' @param_type_name = @param_type_name.camelize(:lower) - elsif WashOut::Engine.camelize_wsdl + elsif soap_config.camelize_wsdl @param_type_name = @param_type_name.camelize end - @param_type_name end end diff --git a/lib/wash_out/wsse.rb b/lib/wash_out/wsse.rb index aa495969..3a94bfed 100644 --- a/lib/wash_out/wsse.rb +++ b/lib/wash_out/wsse.rb @@ -1,15 +1,16 @@ module WashOut class Wsse - - def self.authenticate(token) - wsse = self.new(token) + attr_reader :soap_config + def self.authenticate(soap_config, token) + wsse = self.new(soap_config, token) unless wsse.eligible? raise WashOut::Dispatcher::SOAPError, "Unauthorized" end end - def initialize(token) + def initialize(soap_config, token) + @soap_config = soap_config if token.blank? && required? raise WashOut::Dispatcher::SOAPError, "Missing required UsernameToken" end @@ -17,15 +18,15 @@ def initialize(token) end def required? - !WashOut::Engine.wsse_username.blank? + !soap_config.wsse_username.blank? end def expected_user - WashOut::Engine.wsse_username + soap_config.wsse_username end def expected_password - WashOut::Engine.wsse_password + soap_config.wsse_password end def matches_expected_digest?(password) diff --git a/spec/lib/wash_out/dispatcher_spec.rb b/spec/lib/wash_out/dispatcher_spec.rb index 668203db..69dab1e0 100644 --- a/spec/lib/wash_out/dispatcher_spec.rb +++ b/spec/lib/wash_out/dispatcher_spec.rb @@ -5,7 +5,7 @@ describe WashOut::Dispatcher do class Dispatcher < ApplicationController - include WashOut::SOAP + soap_service def self.mock(text="") dispatcher = self.new @@ -60,27 +60,27 @@ def params describe "#_load_params" do let(:dispatcher) { Dispatcher.new } - + let(:soap_config) { WashOut::SoapConfig.new({ camelize_wsdl: false }) } it "should load params for an array" do - spec = WashOut::Param.parse_def( {:my_array => [:integer] } ) + spec = WashOut::Param.parse_def(soap_config, {:my_array => [:integer] } ) xml_data = {:my_array => [1, 2, 3]} dispatcher._load_params(spec, xml_data).should == {"my_array" => [1, 2, 3]} end it "should load params for an empty array" do - spec = WashOut::Param.parse_def( {:my_array => [:integer] } ) + spec = WashOut::Param.parse_def(soap_config, {:my_array => [:integer] } ) xml_data = {} dispatcher._load_params(spec, xml_data).should == {} end it "should load params for a nested array" do - spec = WashOut::Param.parse_def( {:nested => {:my_array => [:integer]}} ) + spec = WashOut::Param.parse_def(soap_config, {:nested => {:my_array => [:integer]}} ) xml_data = {:nested => {:my_array => [1, 2, 3]}} dispatcher._load_params(spec, xml_data).should == {"nested" => {"my_array" => [1, 2, 3]}} end it "should load params for an empty nested array" do - spec = WashOut::Param.parse_def( {:nested => {:empty => [:integer] }} ) + spec = WashOut::Param.parse_def(soap_config, {:nested => {:empty => [:integer] }} ) xml_data = {:nested => nil} dispatcher._load_params(spec, xml_data).should == {"nested" => {}} end diff --git a/spec/lib/wash_out/param_spec.rb b/spec/lib/wash_out/param_spec.rb index d74d784c..ca9f3b57 100644 --- a/spec/lib/wash_out/param_spec.rb +++ b/spec/lib/wash_out/param_spec.rb @@ -5,18 +5,21 @@ describe WashOut::Param do context "custom types" do + class Abraka1 < WashOut::Type map( :test => :string ) end + class Abraka2 < WashOut::Type type_name 'test' map :foo => Abraka1 end it "loads custom_types" do - map = WashOut::Param.parse_def Abraka2 + soap_config = WashOut::SoapConfig.new({ camelize_wsdl: false }) + map = WashOut::Param.parse_def soap_config, Abraka2 map.should be_a_kind_of(Array) map[0].name.should == 'foo' @@ -24,9 +27,9 @@ class Abraka2 < WashOut::Type end it "respects camelization setting" do - WashOut::Engine.camelize_wsdl = true + soap_config = WashOut::SoapConfig.new({ camelize_wsdl: true }) - map = WashOut::Param.parse_def Abraka2 + map = WashOut::Param.parse_def soap_config, Abraka2 map.should be_a_kind_of(Array) map[0].name.should == 'Foo' @@ -35,14 +38,16 @@ class Abraka2 < WashOut::Type end it "should accept nested empty arrays" do - map = WashOut::Param.parse_def( {:nested => {:some_attr => :string, :empty => [:integer] }} ) + soap_config = WashOut::SoapConfig.new({ camelize_wsdl: false }) + map = WashOut::Param.parse_def(soap_config, {:nested => {:some_attr => :string, :empty => [:integer] }} ) map[0].load( {:nested => nil}, :nested).should == {} end describe "booleans" do + let(:soap_config) { WashOut::SoapConfig.new({ camelize_wsdl: false }) } # following http://www.w3.org/TR/xmlschema-2/#boolean, only true, false, 0 and 1 are allowed. # Nori maps the strings true and false to TrueClass and FalseClass, but not 0 and 1. - let(:map) { WashOut::Param.parse_def(:value => :boolean) } + let(:map) { WashOut::Param.parse_def(soap_config, :value => :boolean) } it "should accept 'true' and '1'" do map[0].load({:value => true}, :value).should be_true diff --git a/spec/lib/wash_out/type_spec.rb b/spec/lib/wash_out/type_spec.rb index 9daf837b..02c9eb1e 100644 --- a/spec/lib/wash_out/type_spec.rb +++ b/spec/lib/wash_out/type_spec.rb @@ -5,9 +5,11 @@ describe WashOut::Type do it "defines custom type" do + class Abraka1 < WashOut::Type map :test => :string end + class Abraka2 < WashOut::Type type_name 'test' map :foo => Abraka1 diff --git a/spec/lib/wash_out_spec.rb b/spec/lib/wash_out_spec.rb index 9734aa85..0d399b67 100644 --- a/spec/lib/wash_out_spec.rb +++ b/spec/lib/wash_out_spec.rb @@ -3,11 +3,6 @@ require 'spec_helper' describe WashOut do - before :each do - WashOut::Engine.snakecase_input = true - WashOut::Engine.camelize_wsdl = true - WashOut::Engine.namespace = false - end let :nori do Nori.new( @@ -54,11 +49,12 @@ def savon!(method, message={}, &block) mock_controller do soap_action :result, :args => nil, :return => :int - soap_action "getArea", :args => { :circle => [{ :center => { :x => [:integer], - :y => :integer }, - :radius => :double }] }, - :return => { :area => :double } - + soap_action "getArea", :args => { + :circle => [{ + :center => { :x => [:integer], :y => :integer }, + :radius => :double + }]}, + :return => { :area => :double } soap_action "rocky", :args => { :circle1 => { :x => :integer } }, :return => { :circle2 => { :y => :integer } } end @@ -429,7 +425,7 @@ def date context "errors" do it "raise for incorrect requests" do mock_controller do - soap_action "duty", + soap_action "duty", :args => {:bad => {:a => :string, :b => :string}, :good => {:a => :string, :b => :string}}, :return => nil def duty @@ -564,10 +560,7 @@ def specific end it "handles snakecase option properly" do - WashOut::Engine.snakecase_input = false - WashOut::Engine.camelize_wsdl = false - - mock_controller do + mock_controller(snakecase_input: false, camelize_wsdl: false) do soap_action "rocknroll", :args => {:ZOMG => :string}, :return => nil def rocknroll params["ZOMG"].should == "yam!" @@ -583,10 +576,7 @@ def rocknroll describe "WS Security" do it "appends username_token to params" do - WashOut::Engine.wsse_username = nil - WashOut::Engine.wsse_password = nil - - mock_controller do + mock_controller(wsse_username: "gorilla", wsse_password: "secret") do soap_action "checkToken", :args => :integer, :return => nil, :to => 'check_token' def check_token request.env['WSSE_TOKEN']['username'].should == "gorilla" @@ -601,10 +591,7 @@ def check_token end it "handles PasswordText auth" do - WashOut::Engine.wsse_username = "gorilla" - WashOut::Engine.wsse_password = "secret" - - mock_controller do + mock_controller(wsse_username: "gorilla", wsse_password: "secret") do soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth' def check_auth render :soap => (params[:value] == 42) @@ -629,10 +616,7 @@ def check_auth end it "handles PasswordDigest auth" do - WashOut::Engine.wsse_username = "gorilla" - WashOut::Engine.wsse_password = "secret" - - mock_controller do + mock_controller(wsse_username: "gorilla", wsse_password: "secret") do soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth' def check_auth render :soap => (params[:value] == 42) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9c965a66..92f79477 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,9 +27,11 @@ config.mock_with :rspec config.before(:all) do - WashOut::Engine.snakecase_input = false - WashOut::Engine.camelize_wsdl = false - WashOut::Engine.namespace = false + WashOut::Engine.config.wash_out = { + snakecase_input: false, + camelize_wsdl: false, + namespace: false + } end config.after(:suite) do @@ -51,11 +53,14 @@ wash_out :api end -def mock_controller(&block) +def mock_controller(options = {}, &block) Object.send :remove_const, :ApiController if defined?(ApiController) Object.send :const_set, :ApiController, Class.new(ApplicationController) { - include WashOut::SOAP - + soap_service options.reverse_merge({ + snakecase_input: true, + camelize_wsdl: true, + namespace: false + }) class_exec &block if block } diff --git a/wash_out.gemspec b/wash_out.gemspec index bbc5a83f..c3c2f4ce 100644 --- a/wash_out.gemspec +++ b/wash_out.gemspec @@ -12,6 +12,9 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") s.require_paths = ["lib"] - + s.post_install_message = <<-EOS + Please replace `include WashOut::SOAP` with `soap_service` + in your controllers if you are upgrading from a version before 0.8.5. + EOS s.add_dependency("nori", ">= 2.0.0") end