removing log dir from .gitignore
[monkeycharger.git] / vendor / rails / actionwebservice / lib / action_web_service / protocol / soap_protocol / marshaler.rb
blob187339627770eb3586329b2070301701a7e368f5
1 require 'soap/mapping'
3 module ActionWebService
4   module Protocol
5     module Soap
6       # Workaround for SOAP4R return values changing
7       class Registry < SOAP::Mapping::Registry
8         if SOAP::Version >= "1.5.4"
9           def find_mapped_soap_class(obj_class)
10             return @map.instance_eval { @obj2soap[obj_class][0] }
11           end
13           def find_mapped_obj_class(soap_class)
14             return @map.instance_eval { @soap2obj[soap_class][0] }
15           end
16         end
17       end
19       class SoapMarshaler
20         attr :namespace
21         attr :registry
23         def initialize(namespace=nil)
24           @namespace = namespace || 'urn:ActionWebService'
25           @registry = Registry.new
26           @type2binding = {}
27           register_static_factories
28         end
30         def soap_to_ruby(obj)
31           SOAP::Mapping.soap2obj(obj, @registry)
32         end
34         def ruby_to_soap(obj)
35           soap = SOAP::Mapping.obj2soap(obj, @registry)
36           soap.elename = XSD::QName.new if SOAP::Version >= "1.5.5" && soap.elename == XSD::QName::EMPTY
37           soap
38         end
40         def register_type(type)
41           return @type2binding[type] if @type2binding.has_key?(type)
43           if type.array?
44             array_mapping = @registry.find_mapped_soap_class(Array)
45             qname = XSD::QName.new(@namespace, soap_type_name(type.element_type.type_class.name) + 'Array')
46             element_type_binding = register_type(type.element_type)
47             @type2binding[type] = SoapBinding.new(self, qname, type, array_mapping, element_type_binding)
48           elsif (mapping = @registry.find_mapped_soap_class(type.type_class) rescue nil)
49             qname = mapping[2] ? mapping[2][:type] : nil
50             qname ||= soap_base_type_name(mapping[0])
51             @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
52           else
53             qname = XSD::QName.new(@namespace, soap_type_name(type.type_class.name))
54             @registry.add(type.type_class,
55               SOAP::SOAPStruct,
56               typed_struct_factory(type.type_class),
57               { :type => qname })
58             mapping = @registry.find_mapped_soap_class(type.type_class)
59             @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
60           end
62           if type.structured?
63             type.each_member do |m_name, m_type|
64               register_type(m_type)
65             end
66           end
67           
68           @type2binding[type]
69         end
70         alias :lookup_type :register_type
72         def annotate_arrays(binding, value)
73           if value.nil?
74             return
75           elsif binding.type.array?
76             mark_typed_array(value, binding.element_binding.qname)
77             if binding.element_binding.type.custom?
78               value.each do |element|
79                 annotate_arrays(binding.element_binding, element)
80               end
81             end
82           elsif binding.type.structured?
83             binding.type.each_member do |name, type|
84               member_binding = register_type(type)
85               member_value = value.respond_to?('[]') ? value[name] : value.send(name)
86               annotate_arrays(member_binding, member_value) if type.custom?
87             end
88           end
89         end
91         private
92           def typed_struct_factory(type_class)
93             if Object.const_defined?('ActiveRecord')
94               if type_class.ancestors.include?(ActiveRecord::Base)
95                 qname =  XSD::QName.new(@namespace, soap_type_name(type_class.name))
96                 type_class.instance_variable_set('@qname', qname)
97                 return SoapActiveRecordStructFactory.new
98               end
99             end
100             SOAP::Mapping::Registry::TypedStructFactory
101           end
103           def mark_typed_array(array, qname)
104             (class << array; self; end).class_eval do 
105               define_method(:arytype) do
106                 qname
107               end
108             end
109           end
111           def soap_base_type_name(type)
112             xsd_type = type.ancestors.find{ |c| c.const_defined? 'Type' }
113             xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
114           end
116           def soap_type_name(type_name)
117             type_name.gsub(/::/, '..')
118           end
120           def register_static_factories
121             @registry.add(ActionWebService::Base64, SOAP::SOAPBase64, SoapBase64Factory.new, nil)
122             mapping = @registry.find_mapped_soap_class(ActionWebService::Base64)
123             @type2binding[ActionWebService::Base64] =
124               SoapBinding.new(self, SOAP::SOAPBase64::Type, ActionWebService::Base64, mapping)
125             @registry.add(Array, SOAP::SOAPArray, SoapTypedArrayFactory.new, nil)
126             @registry.add(::BigDecimal, SOAP::SOAPDouble, SOAP::Mapping::Registry::BasetypeFactory, {:derived_class => true})
127           end
128       end
130       class SoapBinding
131         attr :qname
132         attr :type
133         attr :mapping
134         attr :element_binding
136         def initialize(marshaler, qname, type, mapping, element_binding=nil)
137           @marshaler = marshaler
138           @qname = qname
139           @type = type
140           @mapping = mapping
141           @element_binding = element_binding
142         end
144         def type_name
145           @type.custom? ? @qname.name : nil
146         end
148         def qualified_type_name(ns=nil)
149           if @type.custom?
150             "#{ns ? ns : @qname.namespace}:#{@qname.name}"
151           else
152             ns = XSD::NS.new
153             ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
154             ns.assign(SOAP::EncodingNamespace, "soapenc")
155             xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
156             return ns.name(XSD::AnyTypeName) unless xsd_klass
157             ns.name(xsd_klass.const_get('Type'))
158           end
159         end
161         def eql?(other)
162           @qname == other.qname
163         end
164         alias :== :eql?
166         def hash
167           @qname.hash
168         end
169       end
171       class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
172         def obj2soap(soap_class, obj, info, map)
173           unless obj.is_a?(ActiveRecord::Base)
174             return nil
175           end
176           soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
177           obj.class.columns.each do |column|
178             key = column.name.to_s
179             value = obj.send(key)
180             soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
181           end
182           soap_obj
183         end
185         def soap2obj(obj_class, node, info, map)
186           unless node.type == obj_class.instance_variable_get('@qname')
187             return false
188           end
189           obj = obj_class.new
190           node.each do |key, value|
191             obj[key] = value.data
192           end
193           obj.instance_variable_set('@new_record', false)
194           return true, obj
195         end
196       end
198       class SoapTypedArrayFactory < SOAP::Mapping::Factory
199         def obj2soap(soap_class, obj, info, map)
200           unless obj.respond_to?(:arytype)
201             return nil
202           end
203           soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
204           mark_marshalled_obj(obj, soap_obj)
205           obj.each do |item|
206             child = SOAP::Mapping._obj2soap(item, map)
207             soap_obj.add(child)
208           end
209           soap_obj
210         end
211       
212         def soap2obj(obj_class, node, info, map)
213           return false
214         end
215       end
217       class SoapBase64Factory < SOAP::Mapping::Factory
218         def obj2soap(soap_class, obj, info, map)
219           unless obj.is_a?(ActionWebService::Base64)
220             return nil
221           end
222           return soap_class.new(obj)
223         end
225         def soap2obj(obj_class, node, info, map)
226           unless node.type == SOAP::SOAPBase64::Type
227             return false
228           end
229           return true, obj_class.new(node.string)
230         end
231       end
233     end
234   end