wash_out_fork 0.0.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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +36 -0
  5. data/Appraisals +25 -0
  6. data/CHANGELOG.md +102 -0
  7. data/Gemfile +17 -0
  8. data/Guardfile +12 -0
  9. data/LICENSE +22 -0
  10. data/README.md +242 -0
  11. data/Rakefile +25 -0
  12. data/app/helpers/wash_out_fork_helper.rb +110 -0
  13. data/app/views/wash_out_fork/document/error.builder +9 -0
  14. data/app/views/wash_out_fork/document/response.builder +8 -0
  15. data/app/views/wash_out_fork/document/wsdl.builder +68 -0
  16. data/app/views/wash_out_fork/rpc/error.builder +10 -0
  17. data/app/views/wash_out_fork/rpc/response.builder +9 -0
  18. data/app/views/wash_out_fork/rpc/wsdl.builder +68 -0
  19. data/gemfiles/rails_3.2.13.gemfile +21 -0
  20. data/gemfiles/rails_4.0.0.gemfile +20 -0
  21. data/gemfiles/rails_4.1.0.gemfile +20 -0
  22. data/gemfiles/rails_4.2.0.gemfile +20 -0
  23. data/gemfiles/rails_5.0.0.beta2.gemfile +19 -0
  24. data/gemfiles/rails_5.0.0.gemfile +19 -0
  25. data/init.rb +1 -0
  26. data/lib/wash_out_fork.rb +60 -0
  27. data/lib/wash_out_fork/configurable.rb +41 -0
  28. data/lib/wash_out_fork/dispatcher.rb +232 -0
  29. data/lib/wash_out_fork/engine.rb +12 -0
  30. data/lib/wash_out_fork/middleware.rb +41 -0
  31. data/lib/wash_out_fork/model.rb +29 -0
  32. data/lib/wash_out_fork/param.rb +200 -0
  33. data/lib/wash_out_fork/router.rb +131 -0
  34. data/lib/wash_out_fork/soap.rb +48 -0
  35. data/lib/wash_out_fork/soap_config.rb +93 -0
  36. data/lib/wash_out_fork/type.rb +29 -0
  37. data/lib/wash_out_fork/version.rb +3 -0
  38. data/lib/wash_out_fork/wsse.rb +101 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  43. data/spec/dummy/config.ru +4 -0
  44. data/spec/dummy/config/application.rb +51 -0
  45. data/spec/dummy/config/boot.rb +10 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +23 -0
  48. data/spec/dummy/config/environments/test.rb +30 -0
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/dummy/config/initializers/inflections.rb +10 -0
  51. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  52. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  53. data/spec/dummy/config/initializers/session_store.rb +8 -0
  54. data/spec/dummy/config/locales/en.yml +5 -0
  55. data/spec/dummy/config/routes.rb +58 -0
  56. data/spec/dummy/public/404.html +26 -0
  57. data/spec/dummy/public/422.html +26 -0
  58. data/spec/dummy/public/500.html +26 -0
  59. data/spec/dummy/public/favicon.ico +0 -0
  60. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  61. data/spec/dummy/script/rails +6 -0
  62. data/spec/fixtures/nested_refs_to_arrays.xml +19 -0
  63. data/spec/fixtures/ref_to_one_array.xml +11 -0
  64. data/spec/fixtures/refs_to_arrays.xml +16 -0
  65. data/spec/lib/wash_out/dispatcher_spec.rb +206 -0
  66. data/spec/lib/wash_out/middleware_spec.rb +33 -0
  67. data/spec/lib/wash_out/param_spec.rb +94 -0
  68. data/spec/lib/wash_out/router_spec.rb +50 -0
  69. data/spec/lib/wash_out/type_spec.rb +41 -0
  70. data/spec/lib/wash_out_spec.rb +797 -0
  71. data/spec/spec_helper.rb +88 -0
  72. data/wash_out_fork.gemspec +20 -0
  73. metadata +130 -0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'wash_out_fork'
@@ -0,0 +1,60 @@
1
+ require 'wash_out_fork/configurable'
2
+ require 'wash_out_fork/soap_config'
3
+ require 'wash_out_fork/soap'
4
+ require 'wash_out_fork/engine'
5
+ require 'wash_out_fork/param'
6
+ require 'wash_out_fork/dispatcher'
7
+ require 'wash_out_fork/soap'
8
+ require 'wash_out_fork/router'
9
+ require 'wash_out_fork/type'
10
+ require 'wash_out_fork/model'
11
+ require 'wash_out_fork/wsse'
12
+ require 'wash_out_fork/middleware'
13
+
14
+ module WashOutFork
15
+ def self.root
16
+ File.expand_path '../..', __FILE__
17
+ end
18
+ end
19
+
20
+ module ActionDispatch::Routing
21
+ class Mapper
22
+ # Adds the routes for a SOAP endpoint at +controller+.
23
+ def wash_out_fork(controller_name, options={})
24
+ if @scope
25
+ scope_frame = @scope.respond_to?(:frame) ? @scope.frame : @scope
26
+ options.each{ |key, value| scope_frame[key] = value }
27
+ end
28
+
29
+ controller_class_name = [scope_frame[:module], controller_name].compact.join("/").underscore
30
+
31
+ match "#{controller_name}/wsdl" => "#{controller_name}#_generate_wsdl", :via => :get, :format => false,
32
+ :as => "#{controller_class_name}_wsdl"
33
+ match "#{controller_name}/action" => WashOutFork::Router.new(controller_class_name), :via => [:get, :post],
34
+ :defaults => { :controller => controller_class_name, :action => 'soap' }, :format => false,
35
+ :as => "#{controller_class_name}_soap"
36
+ end
37
+ end
38
+ end
39
+
40
+ Mime::Type.register "application/soap+xml", :soap
41
+ ActiveRecord::Base.send :extend, WashOutFork::Model if defined?(ActiveRecord)
42
+
43
+ ActionController::Renderers.add :soap do |what, options|
44
+ _render_soap(what, options)
45
+ end
46
+
47
+ ActionController::Base.class_eval do
48
+
49
+ # Define a SOAP service. The function has no required +options+:
50
+ # but allow any of :parser, :namespace, :wsdl_style, :snakecase_input,
51
+ # :camelize_wsdl, :wsse_username, :wsse_password and :catch_xml_errors.
52
+ #
53
+ # Any of the the params provided allows for overriding the defaults
54
+ # (like supporting multiple namespaces instead of application wide such)
55
+ #
56
+ def self.soap_service(options={})
57
+ include WashOutFork::SOAP
58
+ self.soap_config = options
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ module WashOutFork
2
+ module Configurable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ cattr_reader :soap_config
7
+ class_variable_set :@@soap_config, WashOutFork::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, WashOutFork::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, WashOutFork::SoapConfig.new(obj)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,232 @@
1
+ module WashOutFork
2
+ # The WashOutFork::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
+ begin
20
+ xml_security = request.env['wash_out_fork.soap_data'].values_at(:envelope, :Envelope).compact.first
21
+ xml_security = xml_security.values_at(:header, :Header).compact.first
22
+ xml_security = xml_security.values_at(:security, :Security).compact.first
23
+ username_token = xml_security.values_at(:username_token, :UsernameToken).compact.first
24
+ rescue
25
+ username_token = nil
26
+ end
27
+
28
+ WashOutFork::Wsse.authenticate soap_config, username_token
29
+
30
+ request.env['WSSE_TOKEN'] = username_token.with_indifferent_access unless username_token.blank?
31
+ end
32
+
33
+ def _map_soap_parameters
34
+ @_params = _load_params action_spec[:in],
35
+ _strip_empty_nodes(action_spec[:in], xml_data)
36
+ end
37
+
38
+ def _strip_empty_nodes(params, hash)
39
+ hash.keys.each do |key|
40
+ param = params.detect { |a| a.raw_name.to_s == key.to_s }
41
+ next if !(param && hash[key].is_a?(Hash))
42
+
43
+ value = hash[key].delete_if do |k, _|
44
+ k.to_s[0] == '@' && !param.map.detect { |a| a.raw_name.to_s == k.to_s }
45
+ end
46
+
47
+ if value.length > 0
48
+ hash[key] = _strip_empty_nodes param.map, value
49
+ else
50
+ hash[key] = nil
51
+ end
52
+ end
53
+
54
+ hash
55
+ end
56
+
57
+ # Creates the final parameter hash based on the request spec and xml_data from the request
58
+ def _load_params(spec, xml_data)
59
+ params = HashWithIndifferentAccess.new
60
+ spec.each do |param|
61
+ key = param.raw_name.to_sym
62
+ if xml_data.has_key? key
63
+ params[param.raw_name] = param.load(xml_data, key)
64
+ end
65
+ end
66
+ params
67
+ end
68
+
69
+ # This action generates the WSDL for defined SOAP methods.
70
+ def _generate_wsdl
71
+ @map = self.class.soap_actions
72
+ @namespace = soap_config.namespace
73
+ @name = controller_path
74
+
75
+ render :template => "wash_out_fork/#{soap_config.wsdl_style}/wsdl", :layout => false,
76
+ :content_type => 'text/xml'
77
+ end
78
+
79
+ # Render a SOAP response.
80
+ def _render_soap(result, options)
81
+ @namespace = soap_config.namespace
82
+ @operation = soap_action = request.env['wash_out_fork.soap_action']
83
+ @action_spec = self.class.soap_actions[soap_action]
84
+
85
+ result = { 'value' => result } unless result.is_a? Hash
86
+ result = HashWithIndifferentAccess.new(result)
87
+
88
+ inject = lambda {|data, map|
89
+ result_spec = []
90
+ return result_spec if data.nil?
91
+
92
+ map.each_with_index do |param, i|
93
+ result_spec[i] = param.flat_copy
94
+
95
+ unless data.is_a?(Hash)
96
+ raise ProgrammerError,
97
+ "SOAP response used #{data.inspect} (which is #{data.class.name}), " +
98
+ "in the context where a Hash with key of '#{param.raw_name}' " +
99
+ "was expected."
100
+ end
101
+
102
+ value = data[param.raw_name]
103
+
104
+ unless value.nil?
105
+ if param.multiplied && !value.is_a?(Array)
106
+ raise ProgrammerError,
107
+ "SOAP response tried to use '#{value.inspect}' " +
108
+ "(which is of type #{value.class.name}), as the value for " +
109
+ "'#{param.raw_name}' (which expects an Array)."
110
+ end
111
+
112
+ # Inline complex structure {:foo => {bar: ...}}
113
+ if param.struct? && !param.multiplied
114
+ result_spec[i].map = inject.call(value, param.map)
115
+
116
+ # Inline array of complex structures {:foo => [{bar: ...}]}
117
+ elsif param.struct? && param.multiplied
118
+ result_spec[i].map = value.map{|e| inject.call(e, param.map)}
119
+
120
+ # Inline scalar {:foo => :string}
121
+ else
122
+ result_spec[i].value = value
123
+ end
124
+ end
125
+ end
126
+
127
+ return result_spec
128
+ }
129
+
130
+ render :template => "wash_out_fork/#{soap_config.wsdl_style}/response",
131
+ :layout => false,
132
+ :locals => { :result => inject.call(result, @action_spec[:out]) },
133
+ :content_type => 'text/xml'
134
+ end
135
+
136
+ # This action is a fallback for all undefined SOAP actions.
137
+ def _invalid_action
138
+ render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out_fork.soap_action']}")
139
+ end
140
+
141
+ def _invalid_request
142
+ render_soap_error("Invalid SOAP request")
143
+ end
144
+
145
+ def _catch_soap_errors
146
+ yield
147
+ rescue SOAPError => error
148
+ render_soap_error(error.message, error.code)
149
+ end
150
+
151
+ # Render a SOAP error response.
152
+ #
153
+ # Rails do not support sequental rescue_from handling, that is, rescuing an
154
+ # exception from a rescue_from handler. Hence this function is a public API.
155
+ def render_soap_error(message, code=nil)
156
+ render :template => "wash_out_fork/#{soap_config.wsdl_style}/error", :status => 500,
157
+ :layout => false,
158
+ :locals => { :error_message => message, :error_code => (code || 'Server') },
159
+ :content_type => 'text/xml'
160
+ end
161
+
162
+ def self.included(controller)
163
+ entity = if defined?(Rails::VERSION::MAJOR) && (Rails::VERSION::MAJOR >= 4)
164
+ 'action'
165
+ else
166
+ 'filter'
167
+ end
168
+
169
+ controller.send :"around_#{entity}", :_catch_soap_errors
170
+ controller.send :helper, :wash_out_fork
171
+ controller.send :"before_#{entity}", :_authenticate_wsse, :if => :soap_action?
172
+ controller.send :"before_#{entity}", :_map_soap_parameters, :if => :soap_action?
173
+ controller.send :"skip_before_#{entity}", :verify_authenticity_token
174
+ end
175
+
176
+ def self.deep_select(collection, result=[], &blk)
177
+ values = collection.respond_to?(:values) ? collection.values : collection
178
+ result += values.select(&blk)
179
+
180
+ values.each do |value|
181
+ if value.is_a?(Hash) || value.is_a?(Array)
182
+ result = deep_select(value, result, &blk)
183
+ end
184
+ end
185
+
186
+ result
187
+ end
188
+
189
+ def self.deep_replace_href(element, replace)
190
+ return element unless element.is_a?(Array) || element.is_a?(Hash)
191
+
192
+ if element.is_a?(Array) # Traverse arrays
193
+ return element.map{|x| deep_replace_href(x, replace)}
194
+ end
195
+
196
+ if element.has_key?(:@href) # Replace needle and traverse replacement
197
+ return deep_replace_href(replace[element[:@href]], replace)
198
+ end
199
+
200
+ element.each do |key, value| # Traverse hashes
201
+ element[key] = deep_replace_href(value, replace)
202
+ end
203
+
204
+ element
205
+ end
206
+
207
+ private
208
+ def soap_action?
209
+ soap_action.present?
210
+ end
211
+
212
+ def action_spec
213
+ self.class.soap_actions[soap_action]
214
+ end
215
+
216
+ def request_input_tag
217
+ action_spec[:request_tag]
218
+ end
219
+
220
+ def soap_action
221
+ request.env['wash_out_fork.soap_action']
222
+ end
223
+
224
+ def xml_data
225
+ xml_data = request.env['wash_out_fork.soap_data'].values_at(:envelope, :Envelope).compact.first
226
+ xml_data = xml_data.values_at(:body, :Body).compact.first || {}
227
+ return xml_data if soap_config.wsdl_style == "document"
228
+ xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym, request_input_tag.to_sym).compact.first || {}
229
+ end
230
+
231
+ end
232
+ end
@@ -0,0 +1,12 @@
1
+
2
+ module WashOutFork
3
+ class Engine < ::Rails::Engine
4
+ config.wash_out_fork = ActiveSupport::OrderedOptions.new
5
+ initializer "wash_out_fork.configuration" do |app|
6
+ if app.config.wash_out_fork[:catch_xml_errors]
7
+ app.config.middleware.insert_after 'ActionDispatch::ShowExceptions', WashOutFork::Middleware
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ class WashOutFork::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
+ WashOutFork::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 WashOutFork
2
+ module Model
3
+ def wash_out_fork_columns
4
+ columns_hash
5
+ end
6
+
7
+ def wash_out_fork_param_map
8
+ types = {
9
+ :text => :string,
10
+ :float => :double,
11
+ :decimal => :double,
12
+ :timestamp => :string
13
+ }
14
+ map = {}
15
+
16
+ wash_out_fork_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_fork_param_name(*args)
26
+ return name.underscore
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,200 @@
1
+ module WashOutFork
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_fork_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 WashOutFork::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 WashOutFork 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 WashOutFork::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_fork_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 | WashOutFork::Param instance
124
+ # type_hash := { :parameter_name => nested_type, ... }
125
+ # definition := [ WashOutFork::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 WashOutFork::Param instance is passed as a +nested_type+, the corresponding
132
+ # +:parameter_name+ is ignored.
133
+ #
134
+ # This function returns an array of WashOutFork::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?(WashOutFork::Type)
140
+ definition = definition.wash_out_fork_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? WashOutFork::Param
150
+ opt
151
+ elsif opt.is_a? Array
152
+ WashOutFork::Param.new(soap_config, name, opt[0], true)
153
+ else
154
+ WashOutFork::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 WashOutFork::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