wash-out 0.10.1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.travis.yml +35 -0
- data/Appraisals +25 -0
- data/CHANGELOG.md +102 -0
- data/Gemfile +16 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/README.md +246 -0
- data/Rakefile +13 -0
- data/app/helpers/wash_out_helper.rb +106 -0
- data/app/views/wash_out/document/error.builder +9 -0
- data/app/views/wash_out/document/response.builder +10 -0
- data/app/views/wash_out/document/wsdl.builder +68 -0
- data/app/views/wash_out/rpc/error.builder +10 -0
- data/app/views/wash_out/rpc/response.builder +11 -0
- data/app/views/wash_out/rpc/wsdl.builder +68 -0
- 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/gemfiles/rails_5.0.0.beta2.gemfile +19 -0
- data/init.rb +1 -0
- data/lib/wash_out.rb +53 -0
- data/lib/wash_out/configurable.rb +41 -0
- data/lib/wash_out/dispatcher.rb +218 -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 +200 -0
- 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 +29 -0
- data/lib/wash_out/version.rb +3 -0
- data/lib/wash_out/wsse.rb +101 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +51 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +23 -0
- data/spec/dummy/config/environments/test.rb +30 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +8 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/lib/wash_out/dispatcher_spec.rb +99 -0
- data/spec/lib/wash_out/middleware_spec.rb +33 -0
- data/spec/lib/wash_out/param_spec.rb +94 -0
- data/spec/lib/wash_out/router_spec.rb +22 -0
- data/spec/lib/wash_out/type_spec.rb +41 -0
- data/spec/lib/wash_out_spec.rb +754 -0
- data/spec/spec_helper.rb +82 -0
- data/wash_out.gemspec +21 -0
- metadata +128 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module WashOut
|
2
|
+
module Configurable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
cattr_reader :soap_config
|
7
|
+
class_variable_set :@@soap_config, WashOut::SoapConfig.new({})
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def soap_config=(obj)
|
13
|
+
|
14
|
+
unless obj.is_a?(Hash)
|
15
|
+
raise "Value needs to be a Hash."
|
16
|
+
end
|
17
|
+
|
18
|
+
if class_variable_defined?(:@@soap_config)
|
19
|
+
class_variable_get(:@@soap_config).configure obj
|
20
|
+
else
|
21
|
+
class_variable_set :@@soap_config, WashOut::SoapConfig.new(obj)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def soap_config=(obj)
|
27
|
+
|
28
|
+
unless obj.is_a?(Hash)
|
29
|
+
raise "Value needs to be a Hash."
|
30
|
+
end
|
31
|
+
|
32
|
+
class_eval do
|
33
|
+
if class_variable_defined?(:@@soap_config)
|
34
|
+
class_variable_get(:@@soap_config).configure obj
|
35
|
+
else
|
36
|
+
class_variable_set :@@soap_config, WashOut::SoapConfig.new(obj)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module WashOut
|
2
|
+
# The WashOut::Dispatcher module should be included in a controller acting
|
3
|
+
# as a SOAP endpoint. It includes actions for generating WSDL and handling
|
4
|
+
# SOAP requests.
|
5
|
+
module Dispatcher
|
6
|
+
# A SOAPError exception can be raised to return a correct SOAP error
|
7
|
+
# response.
|
8
|
+
class SOAPError < Exception
|
9
|
+
attr_accessor :code
|
10
|
+
def initialize(message, code=nil)
|
11
|
+
super(message)
|
12
|
+
@code = code
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ProgrammerError < Exception; end
|
17
|
+
|
18
|
+
def _authenticate_wsse
|
19
|
+
|
20
|
+
begin
|
21
|
+
xml_security = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
|
22
|
+
xml_security = xml_security.values_at(:header, :Header).compact.first
|
23
|
+
xml_security = xml_security.values_at(:security, :Security).compact.first
|
24
|
+
username_token = xml_security.values_at(:username_token, :UsernameToken).compact.first
|
25
|
+
rescue
|
26
|
+
username_token = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
WashOut::Wsse.authenticate soap_config, username_token
|
30
|
+
|
31
|
+
request.env['WSSE_TOKEN'] = username_token.with_indifferent_access unless username_token.blank?
|
32
|
+
end
|
33
|
+
|
34
|
+
def _map_soap_parameters
|
35
|
+
@_params = _load_params action_spec[:in],
|
36
|
+
_strip_empty_nodes(action_spec[:in], xml_data)
|
37
|
+
end
|
38
|
+
|
39
|
+
def _strip_empty_nodes(params, hash)
|
40
|
+
hash.keys.each do |key|
|
41
|
+
param = params.detect { |a| a.raw_name.to_s == key.to_s }
|
42
|
+
next if !(param && hash[key].is_a?(Hash))
|
43
|
+
|
44
|
+
value = hash[key].delete_if do |k, _|
|
45
|
+
k.to_s[0] == '@' && !param.map.detect { |a| a.raw_name.to_s == k.to_s }
|
46
|
+
end
|
47
|
+
|
48
|
+
if value.length > 0
|
49
|
+
hash[key] = _strip_empty_nodes param.map, value
|
50
|
+
else
|
51
|
+
hash[key] = nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates the final parameter hash based on the request spec and xml_data from the request
|
59
|
+
def _load_params(spec, xml_data)
|
60
|
+
params = HashWithIndifferentAccess.new
|
61
|
+
spec.each do |param|
|
62
|
+
key = param.raw_name.to_sym
|
63
|
+
if xml_data.has_key? key
|
64
|
+
params[param.raw_name] = param.load(xml_data, key)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
params
|
68
|
+
end
|
69
|
+
|
70
|
+
# This action generates the WSDL for defined SOAP methods.
|
71
|
+
def _generate_wsdl
|
72
|
+
|
73
|
+
@map = self.class.soap_actions
|
74
|
+
@namespace = soap_config.namespace
|
75
|
+
@name = controller_path.gsub('/', '_')
|
76
|
+
|
77
|
+
render :template => "wash_out/#{soap_config.wsdl_style}/wsdl", :layout => false,
|
78
|
+
:content_type => 'text/xml'
|
79
|
+
end
|
80
|
+
|
81
|
+
# Render a SOAP response.
|
82
|
+
def _render_soap(result, options)
|
83
|
+
@namespace = soap_config.namespace
|
84
|
+
@operation = soap_action = request.env['wash_out.soap_action']
|
85
|
+
@action_spec = self.class.soap_actions[soap_action]
|
86
|
+
|
87
|
+
result = { 'value' => result } unless result.is_a? Hash
|
88
|
+
result = HashWithIndifferentAccess.new(result)
|
89
|
+
|
90
|
+
inject = lambda {|data, map|
|
91
|
+
result_spec = []
|
92
|
+
return result_spec if data.nil?
|
93
|
+
|
94
|
+
map.each_with_index do |param, i|
|
95
|
+
result_spec[i] = param.flat_copy
|
96
|
+
|
97
|
+
unless data.is_a?(Hash)
|
98
|
+
raise ProgrammerError,
|
99
|
+
"SOAP response used #{data.inspect} (which is #{data.class.name}), " +
|
100
|
+
"in the context where a Hash with key of '#{param.raw_name}' " +
|
101
|
+
"was expected."
|
102
|
+
end
|
103
|
+
|
104
|
+
value = data[param.raw_name]
|
105
|
+
|
106
|
+
unless value.nil?
|
107
|
+
if param.multiplied && !value.is_a?(Array)
|
108
|
+
raise ProgrammerError,
|
109
|
+
"SOAP response tried to use '#{value.inspect}' " +
|
110
|
+
"(which is of type #{value.class.name}), as the value for " +
|
111
|
+
"'#{param.raw_name}' (which expects an Array)."
|
112
|
+
end
|
113
|
+
|
114
|
+
# Inline complex structure {:foo => {bar: ...}}
|
115
|
+
if param.struct? && !param.multiplied
|
116
|
+
result_spec[i].map = inject.call(value, param.map)
|
117
|
+
|
118
|
+
# Inline array of complex structures {:foo => [{bar: ...}]}
|
119
|
+
elsif param.struct? && param.multiplied
|
120
|
+
result_spec[i].map = value.map{|e| inject.call(e, param.map)}
|
121
|
+
|
122
|
+
# Inline scalar {:foo => :string}
|
123
|
+
else
|
124
|
+
result_spec[i].value = value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
return result_spec
|
130
|
+
}
|
131
|
+
|
132
|
+
render :template => "wash_out/#{soap_config.wsdl_style}/response",
|
133
|
+
:layout => false,
|
134
|
+
:locals => { :result => inject.call(result, @action_spec[:out]) },
|
135
|
+
:content_type => 'text/xml'
|
136
|
+
end
|
137
|
+
|
138
|
+
# This action is a fallback for all undefined SOAP actions.
|
139
|
+
def _invalid_action
|
140
|
+
render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out.soap_action']}")
|
141
|
+
end
|
142
|
+
|
143
|
+
def _catch_soap_errors
|
144
|
+
yield
|
145
|
+
rescue SOAPError => error
|
146
|
+
render_soap_error(error.message, error.code)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Render a SOAP error response.
|
150
|
+
#
|
151
|
+
# Rails do not support sequental rescue_from handling, that is, rescuing an
|
152
|
+
# exception from a rescue_from handler. Hence this function is a public API.
|
153
|
+
def render_soap_error(message, code=nil)
|
154
|
+
render :template => "wash_out/#{soap_config.wsdl_style}/error", :status => 500,
|
155
|
+
:layout => false,
|
156
|
+
:locals => { :error_message => message, :error_code => (code || 'Server') },
|
157
|
+
:content_type => 'text/xml'
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.included(controller)
|
161
|
+
entity = if defined?(Rails::VERSION::MAJOR) && (Rails::VERSION::MAJOR >= 4)
|
162
|
+
'action'
|
163
|
+
else
|
164
|
+
'filter'
|
165
|
+
end
|
166
|
+
|
167
|
+
controller.send :"around_#{entity}", :_catch_soap_errors
|
168
|
+
controller.send :helper, :wash_out
|
169
|
+
controller.send :"before_#{entity}", :_authenticate_wsse, :except => [
|
170
|
+
:_generate_wsdl, :_invalid_action ]
|
171
|
+
controller.send :"before_#{entity}", :_map_soap_parameters, :except => [
|
172
|
+
:_generate_wsdl, :_invalid_action ]
|
173
|
+
controller.send :"skip_before_#{entity}", :verify_authenticity_token
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.deep_select(hash, result=[], &blk)
|
177
|
+
result += Hash[hash.select(&blk)].values
|
178
|
+
|
179
|
+
hash.each do |key, value|
|
180
|
+
result = deep_select(value, result, &blk) if value.is_a? Hash
|
181
|
+
end
|
182
|
+
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.deep_replace_href(hash, replace)
|
187
|
+
return replace[hash[:@href]] if hash.has_key?(:@href)
|
188
|
+
|
189
|
+
hash.keys.each do |key, value|
|
190
|
+
hash[key] = deep_replace_href(hash[key], replace) if hash[key].is_a?(Hash)
|
191
|
+
end
|
192
|
+
|
193
|
+
hash
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def action_spec
|
199
|
+
self.class.soap_actions[soap_action]
|
200
|
+
end
|
201
|
+
|
202
|
+
def request_input_tag
|
203
|
+
action_spec[:request_tag]
|
204
|
+
end
|
205
|
+
|
206
|
+
def soap_action
|
207
|
+
request.env['wash_out.soap_action']
|
208
|
+
end
|
209
|
+
|
210
|
+
def xml_data
|
211
|
+
xml_data = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
|
212
|
+
xml_data = xml_data.values_at(:body, :Body).compact.first || {}
|
213
|
+
return xml_data if soap_config.wsdl_style == "document"
|
214
|
+
xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym, request_input_tag.to_sym).compact.first || {}
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
module WashOut
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
config.wash_out = ActiveSupport::OrderedOptions.new
|
5
|
+
initializer "wash_out.configuration" do |app|
|
6
|
+
if app.config.wash_out[:catch_xml_errors]
|
7
|
+
app.config.middleware.insert_after 'ActionDispatch::ShowExceptions', WashOut::Middleware
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class WashOut::Middleware
|
2
|
+
def initialize app
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call env
|
7
|
+
begin
|
8
|
+
@app.call env
|
9
|
+
rescue REXML::ParseException => e
|
10
|
+
self.class.raise_or_render_rexml_parse_error e, env
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.raise_or_render_rexml_parse_error e, env
|
15
|
+
raise e unless env.has_key? 'HTTP_SOAPACTION'
|
16
|
+
|
17
|
+
# Normally input would be a StringIO, but Passenger has a different API:
|
18
|
+
input = env['rack.input']
|
19
|
+
req = if input.respond_to? :string then input.string else input.read end
|
20
|
+
|
21
|
+
env['rack.errors'].puts <<-EOERR
|
22
|
+
WashOut::Exception: #{e.continued_exception} for:
|
23
|
+
#{req}
|
24
|
+
EOERR
|
25
|
+
[400, {'Content-Type' => 'text/xml'},
|
26
|
+
[render_client_soap_fault("Error parsing SOAP Request XML")]]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.render_client_soap_fault msg
|
30
|
+
xml = Builder::XmlMarkup.new
|
31
|
+
xml.tag! 'soap:Envelope', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
32
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' do
|
33
|
+
xml.tag! 'soap:Body' do
|
34
|
+
xml.tag! 'soap:Fault', :encodingStyle => 'http://schemas.xmlsoap.org/soap/encoding/' do
|
35
|
+
xml.faultcode 'Client'
|
36
|
+
xml.faultstring msg
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -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
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module WashOut
|
2
|
+
class Param
|
3
|
+
attr_accessor :raw_name
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :map
|
6
|
+
attr_accessor :type
|
7
|
+
attr_accessor :multiplied
|
8
|
+
attr_accessor :value
|
9
|
+
attr_accessor :source_class
|
10
|
+
attr_accessor :soap_config
|
11
|
+
|
12
|
+
# Defines a WSDL parameter with name +name+ and type specifier +type+.
|
13
|
+
# The type specifier format is described in #parse_def.
|
14
|
+
def initialize(soap_config, name, type, multiplied = false)
|
15
|
+
type ||= {}
|
16
|
+
@soap_config = soap_config
|
17
|
+
@name = name.to_s
|
18
|
+
@raw_name = name.to_s
|
19
|
+
@map = {}
|
20
|
+
@multiplied = multiplied
|
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
|
+
|
28
|
+
if type.is_a?(Symbol)
|
29
|
+
@type = type.to_s
|
30
|
+
elsif type.is_a?(Class)
|
31
|
+
@type = 'struct'
|
32
|
+
@map = self.class.parse_def(soap_config, type.wash_out_param_map)
|
33
|
+
@source_class = type
|
34
|
+
else
|
35
|
+
@type = 'struct'
|
36
|
+
@map = self.class.parse_def(soap_config, type)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Converts a generic externally derived Ruby value, such as String or
|
41
|
+
# Hash, to a native Ruby object according to the definition of this type.
|
42
|
+
def load(data, key)
|
43
|
+
if !data.has_key? key
|
44
|
+
raise WashOut::Dispatcher::SOAPError, "Required SOAP parameter '#{key}' is missing"
|
45
|
+
end
|
46
|
+
|
47
|
+
data = data[key]
|
48
|
+
data = [data] if @multiplied && !data.is_a?(Array)
|
49
|
+
|
50
|
+
if struct?
|
51
|
+
data ||= {}
|
52
|
+
if @multiplied
|
53
|
+
data.map do |x|
|
54
|
+
map_struct x do |param, dat, elem|
|
55
|
+
param.load(dat, elem)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
map_struct data do |param, dat, elem|
|
60
|
+
param.load(dat, elem)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
operation = case type
|
65
|
+
when 'string'; :to_s
|
66
|
+
when 'integer'; :to_i
|
67
|
+
when 'long'; :to_i
|
68
|
+
when 'double'; :to_f
|
69
|
+
when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat}
|
70
|
+
when 'date'; :to_date
|
71
|
+
when 'datetime'; :to_datetime
|
72
|
+
when 'time'; :to_time
|
73
|
+
when 'base64Binary'; lambda{|dat| Base64.decode64(dat)}
|
74
|
+
else raise RuntimeError, "Invalid WashOut simple type: #{type}"
|
75
|
+
end
|
76
|
+
|
77
|
+
begin
|
78
|
+
if data.nil?
|
79
|
+
data
|
80
|
+
elsif @multiplied
|
81
|
+
return data.map{|x| x.send(operation)} if operation.is_a?(Symbol)
|
82
|
+
return data.map{|x| operation.call(x)} if operation.is_a?(Proc)
|
83
|
+
elsif operation.is_a? Symbol
|
84
|
+
data.send(operation)
|
85
|
+
else
|
86
|
+
operation.call(data)
|
87
|
+
end
|
88
|
+
rescue
|
89
|
+
raise WashOut::Dispatcher::SOAPError, "Invalid SOAP parameter '#{key}' format"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Checks if this Param defines a complex type.
|
95
|
+
def struct?
|
96
|
+
type == 'struct'
|
97
|
+
end
|
98
|
+
|
99
|
+
def classified?
|
100
|
+
!source_class.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
def basic_type
|
104
|
+
return name unless classified?
|
105
|
+
return source_class.wash_out_param_name(@soap_config)
|
106
|
+
end
|
107
|
+
|
108
|
+
def xsd_type
|
109
|
+
return 'int' if type.to_s == 'integer'
|
110
|
+
return 'dateTime' if type.to_s == 'datetime'
|
111
|
+
return type
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns a WSDL namespaced identifier for this type.
|
115
|
+
def namespaced_type
|
116
|
+
struct? ? "tns:#{basic_type}" : "xsd:#{xsd_type}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Parses a +definition+. The format of the definition is best described
|
120
|
+
# by the following BNF-like grammar.
|
121
|
+
#
|
122
|
+
# simple_type := :string | :integer | :double | :boolean
|
123
|
+
# nested_type := type_hash | simple_type | WashOut::Param instance
|
124
|
+
# type_hash := { :parameter_name => nested_type, ... }
|
125
|
+
# definition := [ WashOut::Param, ... ] |
|
126
|
+
# type_hash |
|
127
|
+
# simple_type
|
128
|
+
#
|
129
|
+
# If a simple type is passed as the +definition+, a single Param is returned
|
130
|
+
# with the +name+ set to "value".
|
131
|
+
# If a WashOut::Param instance is passed as a +nested_type+, the corresponding
|
132
|
+
# +:parameter_name+ is ignored.
|
133
|
+
#
|
134
|
+
# This function returns an array of WashOut::Param objects.
|
135
|
+
def self.parse_def(soap_config, definition)
|
136
|
+
raise RuntimeError, "[] should not be used in your params. Use nil if you want to mark empty set." if definition == []
|
137
|
+
return [] if definition == nil
|
138
|
+
|
139
|
+
if definition.is_a?(Class) && definition.ancestors.include?(WashOut::Type)
|
140
|
+
definition = definition.wash_out_param_map
|
141
|
+
end
|
142
|
+
|
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
|
156
|
+
end
|
157
|
+
else
|
158
|
+
raise RuntimeError, "Wrong definition: #{definition.inspect}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def flat_copy
|
163
|
+
copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied)
|
164
|
+
copy.raw_name = raw_name
|
165
|
+
copy.source_class = source_class
|
166
|
+
copy
|
167
|
+
end
|
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
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
# Used to load an entire structure.
|
181
|
+
def map_struct(data)
|
182
|
+
unless data.is_a?(Hash)
|
183
|
+
raise WashOut::Dispatcher::SOAPError, "SOAP message structure is broken"
|
184
|
+
end
|
185
|
+
|
186
|
+
data = data.with_indifferent_access
|
187
|
+
struct = {}.with_indifferent_access
|
188
|
+
|
189
|
+
# RUBY18 Enumerable#each_with_object is better, but 1.9 only.
|
190
|
+
@map.map do |param|
|
191
|
+
if data.has_key? param.raw_name
|
192
|
+
param_name = param.attribute? ? param.attr_name : param.raw_name
|
193
|
+
struct[param_name] = yield param, data, param.raw_name
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
struct
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|