Skip to content

Commit

Permalink
Merge pull request #109 from mhenrixon/controller_specific_configuration
Browse files Browse the repository at this point in the history
Per SOAP Service configuration
  • Loading branch information
inossidabile committed Sep 8, 2013
2 parents 3b736ec + 62fef7f commit bdd4b85
Show file tree
Hide file tree
Showing 22 changed files with 270 additions and 150 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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",
Expand Down Expand Up @@ -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:

Expand All @@ -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.
2 changes: 1 addition & 1 deletion app/helpers/wash_out_helper.rb
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
2 changes: 1 addition & 1 deletion app/views/wash_with_soap/document/response.builder
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions app/views/wash_with_soap/document/wsdl.builder
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion app/views/wash_with_soap/rpc/response.builder
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions app/views/wash_with_soap/rpc/wsdl.builder
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 16 additions & 5 deletions lib/wash_out.rb
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
41 changes: 41 additions & 0 deletions lib/wash_out/configurable.rb
Original file line number Diff line number Diff line change
@@ -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
36 changes: 21 additions & 15 deletions lib/wash_out/dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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
Expand All @@ -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]

Expand All @@ -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)
Expand Down Expand Up @@ -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]

Expand Down Expand Up @@ -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'
Expand All @@ -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'
Expand All @@ -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

Expand Down
44 changes: 2 additions & 42 deletions lib/wash_out/engine.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit bdd4b85

Please sign in to comment.