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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +23 -3
  4. data/Appraisals +11 -5
  5. data/Gemfile +2 -2
  6. data/README.md +66 -2
  7. data/Rakefile +6 -7
  8. data/app/helpers/wash_out_helper.rb +49 -18
  9. data/app/views/{wash_with_soap → wash_out}/document/error.builder +0 -0
  10. data/app/views/{wash_with_soap → wash_out}/document/response.builder +1 -3
  11. data/app/views/{wash_with_soap → wash_out}/document/wsdl.builder +15 -15
  12. data/app/views/{wash_with_soap → wash_out}/rpc/error.builder +0 -0
  13. data/app/views/{wash_with_soap → wash_out}/rpc/response.builder +1 -3
  14. data/app/views/{wash_with_soap → wash_out}/rpc/wsdl.builder +16 -16
  15. data/gemfiles/rails_3.1.3.gemfile +20 -0
  16. data/gemfiles/rails_3.2.12.gemfile +20 -0
  17. data/gemfiles/rails_4.0.0.gemfile +19 -0
  18. data/gemfiles/rails_4.1.0.gemfile +19 -0
  19. data/gemfiles/rails_4.2.0.gemfile +19 -0
  20. data/lib/wash_out.rb +48 -16
  21. data/lib/wash_out/configurable.rb +41 -0
  22. data/lib/wash_out/dispatcher.rb +212 -0
  23. data/lib/wash_out/engine.rb +12 -0
  24. data/lib/wash_out/middleware.rb +41 -0
  25. data/lib/wash_out/model.rb +29 -0
  26. data/lib/wash_out/param.rb +43 -16
  27. data/lib/wash_out/router.rb +95 -0
  28. data/lib/wash_out/soap.rb +48 -0
  29. data/lib/wash_out/soap_config.rb +93 -0
  30. data/lib/wash_out/type.rb +20 -13
  31. data/lib/wash_out/version.rb +1 -1
  32. data/lib/wash_out/wsse.rb +29 -10
  33. data/spec/dummy/config/environments/test.rb +1 -0
  34. data/spec/lib/wash_out/dispatcher_spec.rb +28 -13
  35. data/spec/lib/wash_out/middleware_spec.rb +33 -0
  36. data/spec/lib/wash_out/param_spec.rb +47 -17
  37. data/spec/lib/wash_out/router_spec.rb +22 -0
  38. data/spec/lib/wash_out/type_spec.rb +9 -9
  39. data/spec/lib/wash_out_spec.rb +139 -87
  40. data/spec/spec_helper.rb +7 -1
  41. metadata +32 -25
  42. data/lib/wash_out/exceptions/programmer_error.rb +0 -10
  43. data/lib/wash_out/exceptions/soap_error.rb +0 -19
  44. data/lib/wash_out/middlewares/catcher.rb +0 -42
  45. data/lib/wash_out/middlewares/router.rb +0 -132
  46. data/lib/wash_out/rails/active_record.rb +0 -27
  47. data/lib/wash_out/rails/controller.rb +0 -201
  48. data/lib/wash_out/rails/engine.rb +0 -74
  49. 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
@@ -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 = WashOut.normalize(name, soap_config)
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
- elsif type.is_a?(Hash)
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
- definition = { :value => definition } unless definition.is_a?(Hash)
139
+ if definition.is_a?(Class) && definition.ancestors.include?(WashOut::Type)
140
+ definition = definition.wash_out_param_map
141
+ end
134
142
 
135
- definition.map do |name, opt|
136
- if opt.is_a? WashOut::Param
137
- opt
138
- elsif opt.is_a? Array
139
- WashOut::Param.new(soap_config, name, opt[0], true)
140
- else
141
- WashOut::Param.new(soap_config, name, opt)
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
- struct[param.raw_name] = yield param, data, param.raw_name
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
@@ -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
- def map(value)
9
- raise RuntimeError, "Wrong definition: #{value.inspect}" unless value.is_a?(Hash)
10
- @param_map = value
11
- end
4
+ def self.type_name(value)
5
+ @param_type_name = value.to_s
6
+ end
12
7
 
13
- def wash_out_param_map
14
- @param_map
15
- end
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
- def wash_out_param_name(soap_config = nil)
18
- WashOut.normalize(@param_type_name || name.underscore.gsub('/', '.'), soap_config)
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
@@ -1,3 +1,3 @@
1
1
  module WashOut
2
- VERSION = "0.10.0.beta.1"
2
+ VERSION = "0.10.0"
3
3
  end