wash_out 0.10.0.beta.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +23 -3
- data/Appraisals +11 -5
- data/Gemfile +2 -2
- data/README.md +66 -2
- data/Rakefile +6 -7
- data/app/helpers/wash_out_helper.rb +49 -18
- data/app/views/{wash_with_soap → wash_out}/document/error.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/document/response.builder +1 -3
- data/app/views/{wash_with_soap → wash_out}/document/wsdl.builder +15 -15
- data/app/views/{wash_with_soap → wash_out}/rpc/error.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/rpc/response.builder +1 -3
- data/app/views/{wash_with_soap → wash_out}/rpc/wsdl.builder +16 -16
- data/gemfiles/rails_3.1.3.gemfile +20 -0
- data/gemfiles/rails_3.2.12.gemfile +20 -0
- data/gemfiles/rails_4.0.0.gemfile +19 -0
- data/gemfiles/rails_4.1.0.gemfile +19 -0
- data/gemfiles/rails_4.2.0.gemfile +19 -0
- data/lib/wash_out.rb +48 -16
- data/lib/wash_out/configurable.rb +41 -0
- data/lib/wash_out/dispatcher.rb +212 -0
- data/lib/wash_out/engine.rb +12 -0
- data/lib/wash_out/middleware.rb +41 -0
- data/lib/wash_out/model.rb +29 -0
- data/lib/wash_out/param.rb +43 -16
- data/lib/wash_out/router.rb +95 -0
- data/lib/wash_out/soap.rb +48 -0
- data/lib/wash_out/soap_config.rb +93 -0
- data/lib/wash_out/type.rb +20 -13
- data/lib/wash_out/version.rb +1 -1
- data/lib/wash_out/wsse.rb +29 -10
- data/spec/dummy/config/environments/test.rb +1 -0
- data/spec/lib/wash_out/dispatcher_spec.rb +28 -13
- data/spec/lib/wash_out/middleware_spec.rb +33 -0
- data/spec/lib/wash_out/param_spec.rb +47 -17
- data/spec/lib/wash_out/router_spec.rb +22 -0
- data/spec/lib/wash_out/type_spec.rb +9 -9
- data/spec/lib/wash_out_spec.rb +139 -87
- data/spec/spec_helper.rb +7 -1
- metadata +32 -25
- data/lib/wash_out/exceptions/programmer_error.rb +0 -10
- data/lib/wash_out/exceptions/soap_error.rb +0 -19
- data/lib/wash_out/middlewares/catcher.rb +0 -42
- data/lib/wash_out/middlewares/router.rb +0 -132
- data/lib/wash_out/rails/active_record.rb +0 -27
- data/lib/wash_out/rails/controller.rb +0 -201
- data/lib/wash_out/rails/engine.rb +0 -74
- data/spec/lib/wash_out/rack_spec.rb +0 -55
@@ -0,0 +1,29 @@
|
|
1
|
+
module WashOut
|
2
|
+
module Model
|
3
|
+
def wash_out_columns
|
4
|
+
columns_hash
|
5
|
+
end
|
6
|
+
|
7
|
+
def wash_out_param_map
|
8
|
+
types = {
|
9
|
+
:text => :string,
|
10
|
+
:float => :double,
|
11
|
+
:decimal => :double,
|
12
|
+
:timestamp => :string
|
13
|
+
}
|
14
|
+
map = {}
|
15
|
+
|
16
|
+
wash_out_columns.each do |key, column|
|
17
|
+
type = column.type
|
18
|
+
type = types[type] if types.has_key?(type)
|
19
|
+
map[key] = type
|
20
|
+
end
|
21
|
+
|
22
|
+
map
|
23
|
+
end
|
24
|
+
|
25
|
+
def wash_out_param_name(*args)
|
26
|
+
return name.underscore
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/wash_out/param.rb
CHANGED
@@ -7,28 +7,33 @@ module WashOut
|
|
7
7
|
attr_accessor :multiplied
|
8
8
|
attr_accessor :value
|
9
9
|
attr_accessor :source_class
|
10
|
+
attr_accessor :soap_config
|
10
11
|
|
11
12
|
# Defines a WSDL parameter with name +name+ and type specifier +type+.
|
12
13
|
# The type specifier format is described in #parse_def.
|
13
14
|
def initialize(soap_config, name, type, multiplied = false)
|
14
15
|
type ||= {}
|
15
16
|
@soap_config = soap_config
|
16
|
-
@name =
|
17
|
+
@name = name.to_s
|
17
18
|
@raw_name = name.to_s
|
18
19
|
@map = {}
|
19
20
|
@multiplied = multiplied
|
20
21
|
|
22
|
+
if soap_config.camelize_wsdl.to_s == 'lower'
|
23
|
+
@name = @name.camelize(:lower)
|
24
|
+
elsif soap_config.camelize_wsdl
|
25
|
+
@name = @name.camelize
|
26
|
+
end
|
27
|
+
|
21
28
|
if type.is_a?(Symbol)
|
22
29
|
@type = type.to_s
|
23
30
|
elsif type.is_a?(Class)
|
24
31
|
@type = 'struct'
|
25
32
|
@map = self.class.parse_def(soap_config, type.wash_out_param_map)
|
26
33
|
@source_class = type
|
27
|
-
|
34
|
+
else
|
28
35
|
@type = 'struct'
|
29
36
|
@map = self.class.parse_def(soap_config, type)
|
30
|
-
else
|
31
|
-
raise RuntimeError, "Wrong definition: #{type.inspect}"
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -36,7 +41,7 @@ module WashOut
|
|
36
41
|
# Hash, to a native Ruby object according to the definition of this type.
|
37
42
|
def load(data, key)
|
38
43
|
if !data.has_key? key
|
39
|
-
raise WashOut::SOAPError, "Required SOAP parameter '#{key}' is missing"
|
44
|
+
raise WashOut::Dispatcher::SOAPError, "Required SOAP parameter '#{key}' is missing"
|
40
45
|
end
|
41
46
|
|
42
47
|
data = data[key]
|
@@ -59,6 +64,7 @@ module WashOut
|
|
59
64
|
operation = case type
|
60
65
|
when 'string'; :to_s
|
61
66
|
when 'integer'; :to_i
|
67
|
+
when 'long'; :to_i
|
62
68
|
when 'double'; :to_f
|
63
69
|
when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat}
|
64
70
|
when 'date'; :to_date
|
@@ -80,7 +86,7 @@ module WashOut
|
|
80
86
|
operation.call(data)
|
81
87
|
end
|
82
88
|
rescue
|
83
|
-
raise WashOut::SOAPError, "Invalid SOAP parameter '#{key}' format"
|
89
|
+
raise WashOut::Dispatcher::SOAPError, "Invalid SOAP parameter '#{key}' format"
|
84
90
|
end
|
85
91
|
end
|
86
92
|
end
|
@@ -130,31 +136,51 @@ module WashOut
|
|
130
136
|
raise RuntimeError, "[] should not be used in your params. Use nil if you want to mark empty set." if definition == []
|
131
137
|
return [] if definition == nil
|
132
138
|
|
133
|
-
|
139
|
+
if definition.is_a?(Class) && definition.ancestors.include?(WashOut::Type)
|
140
|
+
definition = definition.wash_out_param_map
|
141
|
+
end
|
134
142
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
WashOut::Param
|
143
|
+
if [Array, Symbol].include?(definition.class)
|
144
|
+
definition = { :value => definition }
|
145
|
+
end
|
146
|
+
|
147
|
+
if definition.is_a? Hash
|
148
|
+
definition.map do |name, opt|
|
149
|
+
if opt.is_a? WashOut::Param
|
150
|
+
opt
|
151
|
+
elsif opt.is_a? Array
|
152
|
+
WashOut::Param.new(soap_config, name, opt[0], true)
|
153
|
+
else
|
154
|
+
WashOut::Param.new(soap_config, name, opt)
|
155
|
+
end
|
142
156
|
end
|
157
|
+
else
|
158
|
+
raise RuntimeError, "Wrong definition: #{definition.inspect}"
|
143
159
|
end
|
144
160
|
end
|
145
161
|
|
146
162
|
def flat_copy
|
147
163
|
copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied)
|
148
164
|
copy.raw_name = raw_name
|
165
|
+
copy.source_class = source_class
|
149
166
|
copy
|
150
167
|
end
|
151
168
|
|
169
|
+
def attribute?
|
170
|
+
name[0] == "@"
|
171
|
+
end
|
172
|
+
|
173
|
+
def attr_name
|
174
|
+
raise 'Not attribute' unless attribute?
|
175
|
+
name[1..-1]
|
176
|
+
end
|
177
|
+
|
152
178
|
private
|
153
179
|
|
154
180
|
# Used to load an entire structure.
|
155
181
|
def map_struct(data)
|
156
182
|
unless data.is_a?(Hash)
|
157
|
-
raise WashOut::SOAPError, "SOAP message structure is broken"
|
183
|
+
raise WashOut::Dispatcher::SOAPError, "SOAP message structure is broken"
|
158
184
|
end
|
159
185
|
|
160
186
|
data = data.with_indifferent_access
|
@@ -163,7 +189,8 @@ module WashOut
|
|
163
189
|
# RUBY18 Enumerable#each_with_object is better, but 1.9 only.
|
164
190
|
@map.map do |param|
|
165
191
|
if data.has_key? param.raw_name
|
166
|
-
|
192
|
+
param_name = param.attribute? ? param.attr_name : param.raw_name
|
193
|
+
struct[param_name] = yield param, data, param.raw_name
|
167
194
|
end
|
168
195
|
end
|
169
196
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'nori'
|
2
|
+
|
3
|
+
module WashOut
|
4
|
+
# This class is a Rack middleware used to route SOAP requests to a proper
|
5
|
+
# action of a given SOAP controller.
|
6
|
+
class Router
|
7
|
+
def initialize(controller_name)
|
8
|
+
@controller_name = "#{controller_name.to_s}_controller".camelize
|
9
|
+
end
|
10
|
+
|
11
|
+
def controller
|
12
|
+
@controller
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_soap_action(env)
|
16
|
+
return env['wash_out.soap_action'] if env['wash_out.soap_action']
|
17
|
+
|
18
|
+
soap_action = controller.soap_config.soap_action_routing ? env['HTTP_SOAPACTION'].to_s.gsub(/^"(.*)"$/, '\1')
|
19
|
+
: ''
|
20
|
+
|
21
|
+
if soap_action.blank?
|
22
|
+
parsed_soap_body = nori(controller.soap_config.snakecase_input).parse(soap_body env)
|
23
|
+
return nil if parsed_soap_body.blank?
|
24
|
+
|
25
|
+
soap_action = parsed_soap_body
|
26
|
+
.values_at(:envelope, :Envelope).compact.first
|
27
|
+
.values_at(:body, :Body).compact.first
|
28
|
+
.keys.first.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# RUBY18 1.8 does not have force_encoding.
|
32
|
+
soap_action.force_encoding('UTF-8') if soap_action.respond_to? :force_encoding
|
33
|
+
|
34
|
+
if controller.soap_config.namespace
|
35
|
+
namespace = Regexp.escape controller.soap_config.namespace.to_s
|
36
|
+
soap_action.gsub!(/^(#{namespace}(\/|#)?)?([^"]*)$/, '\3')
|
37
|
+
end
|
38
|
+
|
39
|
+
env['wash_out.soap_action'] = soap_action
|
40
|
+
end
|
41
|
+
|
42
|
+
def nori(snakecase=false)
|
43
|
+
Nori.new(
|
44
|
+
:parser => controller.soap_config.parser,
|
45
|
+
:strip_namespaces => true,
|
46
|
+
:advanced_typecasting => true,
|
47
|
+
:convert_tags_to => (
|
48
|
+
snakecase ? lambda { |tag| tag.snakecase.to_sym }
|
49
|
+
: lambda { |tag| tag.to_sym }
|
50
|
+
)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def soap_body(env)
|
55
|
+
body = env['rack.input']
|
56
|
+
body.rewind if body.respond_to? :rewind
|
57
|
+
body.respond_to?(:string) ? body.string : body.read
|
58
|
+
ensure
|
59
|
+
body.rewind if body.respond_to? :rewind
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_soap_parameters(env)
|
63
|
+
return env['wash_out.soap_data'] if env['wash_out.soap_data']
|
64
|
+
|
65
|
+
env['wash_out.soap_data'] = nori(controller.soap_config.snakecase_input).parse(soap_body env)
|
66
|
+
references = WashOut::Dispatcher.deep_select(env['wash_out.soap_data']){|k,v| v.is_a?(Hash) && v.has_key?(:@id)}
|
67
|
+
|
68
|
+
unless references.blank?
|
69
|
+
replaces = {}; references.each{|r| replaces['#'+r[:@id]] = r}
|
70
|
+
env['wash_out.soap_data'] = WashOut::Dispatcher.deep_replace_href(env['wash_out.soap_data'], replaces)
|
71
|
+
end
|
72
|
+
|
73
|
+
env['wash_out.soap_data']
|
74
|
+
end
|
75
|
+
|
76
|
+
def call(env)
|
77
|
+
@controller = @controller_name.constantize
|
78
|
+
|
79
|
+
soap_action = parse_soap_action(env)
|
80
|
+
return [200, {}, ['OK']] if soap_action.blank?
|
81
|
+
|
82
|
+
soap_parameters = parse_soap_parameters(env)
|
83
|
+
|
84
|
+
action_spec = controller.soap_actions[soap_action]
|
85
|
+
|
86
|
+
if action_spec
|
87
|
+
action = action_spec[:to]
|
88
|
+
else
|
89
|
+
action = '_invalid_action'
|
90
|
+
end
|
91
|
+
|
92
|
+
controller.action(action).call(env)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module WashOut
|
4
|
+
module SOAP
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
attr_accessor :soap_actions
|
9
|
+
|
10
|
+
# Define a SOAP action +action+. The function has two required +options+:
|
11
|
+
# :args and :return. Each is a type +definition+ of format described in
|
12
|
+
# WashOut::Param#parse_def.
|
13
|
+
#
|
14
|
+
# An optional option :to can be passed to allow for names of SOAP actions
|
15
|
+
# which are not valid Ruby function names.
|
16
|
+
def soap_action(action, options={})
|
17
|
+
if action.is_a?(Symbol)
|
18
|
+
if soap_config.camelize_wsdl.to_s == 'lower'
|
19
|
+
options[:to] ||= action.to_s
|
20
|
+
action = action.to_s.camelize(:lower)
|
21
|
+
elsif soap_config.camelize_wsdl
|
22
|
+
options[:to] ||= action.to_s
|
23
|
+
action = action.to_s.camelize
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
default_response_tag = soap_config.camelize_wsdl ? 'Response' : '_response'
|
29
|
+
default_response_tag = action+default_response_tag
|
30
|
+
|
31
|
+
self.soap_actions[action] = options.merge(
|
32
|
+
:in => WashOut::Param.parse_def(soap_config, options[:args]),
|
33
|
+
:request_tag => options[:as] || action,
|
34
|
+
:out => WashOut::Param.parse_def(soap_config, options[:return]),
|
35
|
+
:to => options[:to] || action,
|
36
|
+
:response_tag => options[:response_tag] || default_response_tag
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
included do
|
42
|
+
include WashOut::Configurable
|
43
|
+
include WashOut::Dispatcher
|
44
|
+
include WashOut::WsseParams
|
45
|
+
self.soap_actions = {}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module WashOut
|
2
|
+
require 'forwardable'
|
3
|
+
# Configuration options for {Client}, defaulting to values
|
4
|
+
# in {Default}
|
5
|
+
class SoapConfig
|
6
|
+
extend Forwardable
|
7
|
+
DEFAULT_CONFIG = {
|
8
|
+
parser: :rexml,
|
9
|
+
namespace: 'urn:WashOut',
|
10
|
+
wsdl_style: 'rpc',
|
11
|
+
snakecase_input: false,
|
12
|
+
camelize_wsdl: false,
|
13
|
+
catch_xml_errors: false,
|
14
|
+
wsse_username: nil,
|
15
|
+
wsse_password: nil,
|
16
|
+
wsse_auth_callback: nil,
|
17
|
+
soap_action_routing: true,
|
18
|
+
}
|
19
|
+
|
20
|
+
attr_reader :config
|
21
|
+
def_delegators :@config, :[], :[]=, :sort
|
22
|
+
|
23
|
+
|
24
|
+
# The keys allowed
|
25
|
+
def self.keys
|
26
|
+
@keys ||= config.keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.config
|
30
|
+
DEFAULT_CONFIG
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.soap_accessor(*syms)
|
34
|
+
syms.each do |sym|
|
35
|
+
|
36
|
+
unless sym =~ /^[_A-Za-z]\w*$/
|
37
|
+
raise NameError.new("invalid class attribute name: #{sym}")
|
38
|
+
end
|
39
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
40
|
+
unless defined? @#{sym}
|
41
|
+
@#{sym} = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def #{sym}
|
45
|
+
@#{sym}
|
46
|
+
end
|
47
|
+
|
48
|
+
def #{sym}=(obj)
|
49
|
+
@#{sym} = obj
|
50
|
+
end
|
51
|
+
EOS
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
soap_accessor(*WashOut::SoapConfig.keys)
|
56
|
+
|
57
|
+
def initialize(options = {})
|
58
|
+
@config = {}
|
59
|
+
options.reverse_merge!(engine_config) if engine_config
|
60
|
+
options.reverse_merge!(DEFAULT_CONFIG)
|
61
|
+
configure options
|
62
|
+
end
|
63
|
+
|
64
|
+
def default?
|
65
|
+
DEFAULT_CONFIG.sort == config.sort
|
66
|
+
end
|
67
|
+
|
68
|
+
def configure(options = {})
|
69
|
+
@config.merge! validate_config!(options)
|
70
|
+
|
71
|
+
config.each do |key,value|
|
72
|
+
send("#{key}=", value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def engine_config
|
79
|
+
@engine_config ||= WashOut::Engine.config.wash_out
|
80
|
+
end
|
81
|
+
|
82
|
+
def validate_config!(options = {})
|
83
|
+
rejected_keys = options.keys.reject do |key|
|
84
|
+
WashOut::SoapConfig.keys.include?(key)
|
85
|
+
end
|
86
|
+
|
87
|
+
if rejected_keys.any?
|
88
|
+
raise "The following keys are not allows: #{rejected_keys}\n Did you intend for one of the following? #{WashOut::SoapConfig.keys}"
|
89
|
+
end
|
90
|
+
options
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/wash_out/type.rb
CHANGED
@@ -1,22 +1,29 @@
|
|
1
1
|
module WashOut
|
2
2
|
class Type
|
3
|
-
class <<self
|
4
|
-
def type_name(value)
|
5
|
-
@param_type_name = value.to_s
|
6
|
-
end
|
7
3
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
4
|
+
def self.type_name(value)
|
5
|
+
@param_type_name = value.to_s
|
6
|
+
end
|
12
7
|
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
def self.map(value)
|
9
|
+
raise RuntimeError, "Wrong definition: #{value.inspect}" unless value.is_a?(Hash)
|
10
|
+
@param_map = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.wash_out_param_map
|
14
|
+
@param_map
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.wash_out_param_name(soap_config = nil)
|
18
|
+
soap_config ||= WashOut::SoapConfig.new({})
|
19
|
+
@param_type_name ||= name.underscore.gsub '/', '.'
|
16
20
|
|
17
|
-
|
18
|
-
|
21
|
+
if soap_config.camelize_wsdl.to_s == 'lower'
|
22
|
+
@param_type_name = @param_type_name.camelize(:lower)
|
23
|
+
elsif soap_config.camelize_wsdl
|
24
|
+
@param_type_name = @param_type_name.camelize
|
19
25
|
end
|
26
|
+
@param_type_name
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
data/lib/wash_out/version.rb
CHANGED