reidiculous-actionwebservice-client 3.0.4

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 (179) hide show
  1. data/CHANGELOG +320 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +381 -0
  4. data/Rakefile +180 -0
  5. data/TODO +32 -0
  6. data/lib/action_web_service.rb +71 -0
  7. data/lib/action_web_service/api.rb +297 -0
  8. data/lib/action_web_service/base.rb +38 -0
  9. data/lib/action_web_service/casting.rb +149 -0
  10. data/lib/action_web_service/client.rb +3 -0
  11. data/lib/action_web_service/client/base.rb +28 -0
  12. data/lib/action_web_service/client/soap_client.rb +113 -0
  13. data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
  14. data/lib/action_web_service/container.rb +3 -0
  15. data/lib/action_web_service/container/action_controller_container.rb +93 -0
  16. data/lib/action_web_service/container/delegated_container.rb +86 -0
  17. data/lib/action_web_service/container/direct_container.rb +69 -0
  18. data/lib/action_web_service/dispatcher.rb +2 -0
  19. data/lib/action_web_service/dispatcher/abstract.rb +207 -0
  20. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +379 -0
  21. data/lib/action_web_service/invocation.rb +202 -0
  22. data/lib/action_web_service/protocol.rb +4 -0
  23. data/lib/action_web_service/protocol/abstract.rb +115 -0
  24. data/lib/action_web_service/protocol/discovery.rb +37 -0
  25. data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
  26. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
  27. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +122 -0
  28. data/lib/action_web_service/scaffolding.rb +281 -0
  29. data/lib/action_web_service/soap/attachment.rb +107 -0
  30. data/lib/action_web_service/soap/baseData.rb +942 -0
  31. data/lib/action_web_service/soap/element.rb +258 -0
  32. data/lib/action_web_service/soap/encodingstyle/aspDotNetHandler.rb +213 -0
  33. data/lib/action_web_service/soap/encodingstyle/handler.rb +100 -0
  34. data/lib/action_web_service/soap/encodingstyle/literalHandler.rb +226 -0
  35. data/lib/action_web_service/soap/encodingstyle/soapHandler.rb +582 -0
  36. data/lib/action_web_service/soap/generator.rb +268 -0
  37. data/lib/action_web_service/soap/header/handler.rb +57 -0
  38. data/lib/action_web_service/soap/header/handlerset.rb +70 -0
  39. data/lib/action_web_service/soap/header/simplehandler.rb +44 -0
  40. data/lib/action_web_service/soap/httpconfigloader.rb +119 -0
  41. data/lib/action_web_service/soap/mapping.rb +10 -0
  42. data/lib/action_web_service/soap/mapping/factory.rb +355 -0
  43. data/lib/action_web_service/soap/mapping/mapping.rb +381 -0
  44. data/lib/action_web_service/soap/mapping/registry.rb +541 -0
  45. data/lib/action_web_service/soap/mapping/rubytypeFactory.rb +475 -0
  46. data/lib/action_web_service/soap/mapping/typeMap.rb +50 -0
  47. data/lib/action_web_service/soap/mapping/wsdlencodedregistry.rb +280 -0
  48. data/lib/action_web_service/soap/mapping/wsdlliteralregistry.rb +418 -0
  49. data/lib/action_web_service/soap/marshal.rb +59 -0
  50. data/lib/action_web_service/soap/mimemessage.rb +240 -0
  51. data/lib/action_web_service/soap/netHttpClient.rb +190 -0
  52. data/lib/action_web_service/soap/parser.rb +251 -0
  53. data/lib/action_web_service/soap/processor.rb +66 -0
  54. data/lib/action_web_service/soap/property.rb +333 -0
  55. data/lib/action_web_service/soap/rpc/cgistub.rb +206 -0
  56. data/lib/action_web_service/soap/rpc/driver.rb +254 -0
  57. data/lib/action_web_service/soap/rpc/element.rb +325 -0
  58. data/lib/action_web_service/soap/rpc/httpserver.rb +129 -0
  59. data/lib/action_web_service/soap/rpc/proxy.rb +497 -0
  60. data/lib/action_web_service/soap/rpc/router.rb +594 -0
  61. data/lib/action_web_service/soap/rpc/rpc.rb +25 -0
  62. data/lib/action_web_service/soap/rpc/soaplet.rb +162 -0
  63. data/lib/action_web_service/soap/rpc/standaloneServer.rb +43 -0
  64. data/lib/action_web_service/soap/soap.rb +140 -0
  65. data/lib/action_web_service/soap/streamHandler.rb +229 -0
  66. data/lib/action_web_service/soap/wsdlDriver.rb +575 -0
  67. data/lib/action_web_service/struct.rb +64 -0
  68. data/lib/action_web_service/support/class_inheritable_options.rb +28 -0
  69. data/lib/action_web_service/support/signature_types.rb +227 -0
  70. data/lib/action_web_service/test_invoke.rb +110 -0
  71. data/lib/action_web_service/version.rb +9 -0
  72. data/lib/action_web_service/wsdl/binding.rb +65 -0
  73. data/lib/action_web_service/wsdl/data.rb +64 -0
  74. data/lib/action_web_service/wsdl/definitions.rb +250 -0
  75. data/lib/action_web_service/wsdl/documentation.rb +32 -0
  76. data/lib/action_web_service/wsdl/import.rb +80 -0
  77. data/lib/action_web_service/wsdl/importer.rb +38 -0
  78. data/lib/action_web_service/wsdl/info.rb +39 -0
  79. data/lib/action_web_service/wsdl/message.rb +54 -0
  80. data/lib/action_web_service/wsdl/operation.rb +130 -0
  81. data/lib/action_web_service/wsdl/operationBinding.rb +108 -0
  82. data/lib/action_web_service/wsdl/param.rb +85 -0
  83. data/lib/action_web_service/wsdl/parser.rb +163 -0
  84. data/lib/action_web_service/wsdl/part.rb +52 -0
  85. data/lib/action_web_service/wsdl/port.rb +84 -0
  86. data/lib/action_web_service/wsdl/portType.rb +73 -0
  87. data/lib/action_web_service/wsdl/service.rb +61 -0
  88. data/lib/action_web_service/wsdl/soap/address.rb +40 -0
  89. data/lib/action_web_service/wsdl/soap/binding.rb +49 -0
  90. data/lib/action_web_service/wsdl/soap/body.rb +56 -0
  91. data/lib/action_web_service/wsdl/soap/cgiStubCreator.rb +76 -0
  92. data/lib/action_web_service/wsdl/soap/classDefCreator.rb +314 -0
  93. data/lib/action_web_service/wsdl/soap/classDefCreatorSupport.rb +126 -0
  94. data/lib/action_web_service/wsdl/soap/clientSkeltonCreator.rb +78 -0
  95. data/lib/action_web_service/wsdl/soap/complexType.rb +161 -0
  96. data/lib/action_web_service/wsdl/soap/data.rb +42 -0
  97. data/lib/action_web_service/wsdl/soap/definitions.rb +149 -0
  98. data/lib/action_web_service/wsdl/soap/driverCreator.rb +95 -0
  99. data/lib/action_web_service/wsdl/soap/element.rb +28 -0
  100. data/lib/action_web_service/wsdl/soap/fault.rb +56 -0
  101. data/lib/action_web_service/wsdl/soap/header.rb +86 -0
  102. data/lib/action_web_service/wsdl/soap/headerfault.rb +56 -0
  103. data/lib/action_web_service/wsdl/soap/mappingRegistryCreator.rb +92 -0
  104. data/lib/action_web_service/wsdl/soap/methodDefCreator.rb +228 -0
  105. data/lib/action_web_service/wsdl/soap/operation.rb +122 -0
  106. data/lib/action_web_service/wsdl/soap/servantSkeltonCreator.rb +67 -0
  107. data/lib/action_web_service/wsdl/soap/standaloneServerStubCreator.rb +85 -0
  108. data/lib/action_web_service/wsdl/soap/wsdl2ruby.rb +176 -0
  109. data/lib/action_web_service/wsdl/types.rb +43 -0
  110. data/lib/action_web_service/wsdl/wsdl.rb +23 -0
  111. data/lib/action_web_service/wsdl/xmlSchema/all.rb +69 -0
  112. data/lib/action_web_service/wsdl/xmlSchema/annotation.rb +34 -0
  113. data/lib/action_web_service/wsdl/xmlSchema/any.rb +56 -0
  114. data/lib/action_web_service/wsdl/xmlSchema/attribute.rb +127 -0
  115. data/lib/action_web_service/wsdl/xmlSchema/choice.rb +69 -0
  116. data/lib/action_web_service/wsdl/xmlSchema/complexContent.rb +92 -0
  117. data/lib/action_web_service/wsdl/xmlSchema/complexType.rb +139 -0
  118. data/lib/action_web_service/wsdl/xmlSchema/content.rb +96 -0
  119. data/lib/action_web_service/wsdl/xmlSchema/data.rb +80 -0
  120. data/lib/action_web_service/wsdl/xmlSchema/element.rb +154 -0
  121. data/lib/action_web_service/wsdl/xmlSchema/enumeration.rb +36 -0
  122. data/lib/action_web_service/wsdl/xmlSchema/import.rb +65 -0
  123. data/lib/action_web_service/wsdl/xmlSchema/importer.rb +87 -0
  124. data/lib/action_web_service/wsdl/xmlSchema/include.rb +54 -0
  125. data/lib/action_web_service/wsdl/xmlSchema/length.rb +35 -0
  126. data/lib/action_web_service/wsdl/xmlSchema/parser.rb +166 -0
  127. data/lib/action_web_service/wsdl/xmlSchema/pattern.rb +36 -0
  128. data/lib/action_web_service/wsdl/xmlSchema/schema.rb +143 -0
  129. data/lib/action_web_service/wsdl/xmlSchema/sequence.rb +69 -0
  130. data/lib/action_web_service/wsdl/xmlSchema/simpleContent.rb +65 -0
  131. data/lib/action_web_service/wsdl/xmlSchema/simpleExtension.rb +54 -0
  132. data/lib/action_web_service/wsdl/xmlSchema/simpleRestriction.rb +73 -0
  133. data/lib/action_web_service/wsdl/xmlSchema/simpleType.rb +73 -0
  134. data/lib/action_web_service/wsdl/xmlSchema/unique.rb +34 -0
  135. data/lib/action_web_service/wsdl/xmlSchema/xsd2ruby.rb +107 -0
  136. data/lib/action_web_service/xsd/charset.rb +187 -0
  137. data/lib/action_web_service/xsd/codegen.rb +12 -0
  138. data/lib/action_web_service/xsd/codegen/classdef.rb +203 -0
  139. data/lib/action_web_service/xsd/codegen/commentdef.rb +34 -0
  140. data/lib/action_web_service/xsd/codegen/gensupport.rb +166 -0
  141. data/lib/action_web_service/xsd/codegen/methoddef.rb +63 -0
  142. data/lib/action_web_service/xsd/codegen/moduledef.rb +191 -0
  143. data/lib/action_web_service/xsd/datatypes.rb +1269 -0
  144. data/lib/action_web_service/xsd/datatypes1999.rb +20 -0
  145. data/lib/action_web_service/xsd/iconvcharset.rb +33 -0
  146. data/lib/action_web_service/xsd/mapping.rb +42 -0
  147. data/lib/action_web_service/xsd/namedelements.rb +95 -0
  148. data/lib/action_web_service/xsd/ns.rb +140 -0
  149. data/lib/action_web_service/xsd/qname.rb +78 -0
  150. data/lib/action_web_service/xsd/xmlparser.rb +61 -0
  151. data/lib/action_web_service/xsd/xmlparser/parser.rb +96 -0
  152. data/lib/action_web_service/xsd/xmlparser/rexmlparser.rb +54 -0
  153. data/lib/action_web_service/xsd/xmlparser/xmlparser.rb +50 -0
  154. data/lib/action_web_service/xsd/xmlparser/xmlscanner.rb +147 -0
  155. data/lib/actionwebservice.rb +1 -0
  156. data/setup.rb +1379 -0
  157. data/test/abstract_client.rb +183 -0
  158. data/test/abstract_dispatcher.rb +548 -0
  159. data/test/abstract_unit.rb +45 -0
  160. data/test/api_test.rb +103 -0
  161. data/test/apis/auto_load_api.rb +3 -0
  162. data/test/apis/broken_auto_load_api.rb +2 -0
  163. data/test/base_test.rb +42 -0
  164. data/test/casting_test.rb +95 -0
  165. data/test/client_soap_test.rb +156 -0
  166. data/test/client_xmlrpc_test.rb +153 -0
  167. data/test/container_test.rb +73 -0
  168. data/test/dispatcher_action_controller_soap_test.rb +139 -0
  169. data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
  170. data/test/fixtures/db_definitions/mysql.sql +8 -0
  171. data/test/fixtures/db_definitions/sqlite3.sql +8 -0
  172. data/test/fixtures/users.yml +12 -0
  173. data/test/gencov +3 -0
  174. data/test/invocation_test.rb +185 -0
  175. data/test/run +6 -0
  176. data/test/scaffolded_controller_test.rb +146 -0
  177. data/test/struct_test.rb +52 -0
  178. data/test/test_invoke_test.rb +112 -0
  179. metadata +265 -0
@@ -0,0 +1,242 @@
1
+ require 'action_web_service/soap/mapping'
2
+
3
+ # hack to improve the .Net interoperability
4
+ class SOAP::Mapping::Object
5
+ def each_pair
6
+ self.__xmlele.each { |n, v| yield n.name, v.to_s }
7
+ end
8
+ end
9
+
10
+ module ActionWebService
11
+ module Protocol
12
+ module Soap
13
+ # Workaround for SOAP4R return values changing
14
+ class Registry < SOAP::Mapping::Registry
15
+ if SOAP::Version >= "1.5.4"
16
+ def find_mapped_soap_class(obj_class)
17
+ return @map.instance_eval { @obj2soap[obj_class][0] }
18
+ end
19
+
20
+ def find_mapped_obj_class(soap_class)
21
+ return @map.instance_eval { @soap2obj[soap_class][0] }
22
+ end
23
+ end
24
+ end
25
+
26
+ class SoapMarshaler
27
+ attr :namespace
28
+ attr :registry
29
+
30
+ def initialize(namespace=nil)
31
+ @namespace = namespace || 'urn:ActionWebService'
32
+ @registry = Registry.new
33
+ @type2binding = {}
34
+ register_static_factories
35
+ end
36
+
37
+ def soap_to_ruby(obj)
38
+ SOAP::Mapping.soap2obj(obj, @registry)
39
+ end
40
+
41
+ def ruby_to_soap(obj)
42
+ soap = SOAP::Mapping.obj2soap(obj, @registry)
43
+ soap.elename = XSD::QName.new if SOAP::Version >= "1.5.5" && soap.elename == XSD::QName::EMPTY
44
+ soap
45
+ end
46
+
47
+ def register_type(type)
48
+ return @type2binding[type] if @type2binding.has_key?(type)
49
+
50
+ if type.array?
51
+ array_mapping = @registry.find_mapped_soap_class(Array)
52
+ qname = XSD::QName.new(@namespace, soap_type_name(type.element_type.type_class.name) + 'Array')
53
+ element_type_binding = register_type(type.element_type)
54
+ @type2binding[type] = SoapBinding.new(self, qname, type, array_mapping, element_type_binding)
55
+ elsif (mapping = @registry.find_mapped_soap_class(type.type_class) rescue nil)
56
+ qname = mapping[2] ? mapping[2][:type] : nil
57
+ qname ||= soap_base_type_name(mapping[0])
58
+ @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
59
+ else
60
+ qname = XSD::QName.new(@namespace, soap_type_name(type.type_class.name))
61
+ @registry.add(type.type_class,
62
+ SOAP::SOAPStruct,
63
+ typed_struct_factory(type.type_class),
64
+ { :type => qname })
65
+ mapping = @registry.find_mapped_soap_class(type.type_class)
66
+ @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
67
+ end
68
+
69
+ if type.structured?
70
+ type.each_member do |m_name, m_type|
71
+ register_type(m_type)
72
+ end
73
+ end
74
+
75
+ @type2binding[type]
76
+ end
77
+ alias :lookup_type :register_type
78
+
79
+ def annotate_arrays(binding, value)
80
+ if value.nil?
81
+ return
82
+ elsif binding.type.array?
83
+ mark_typed_array(value, binding.element_binding.qname)
84
+ if binding.element_binding.type.custom?
85
+ value.each do |element|
86
+ annotate_arrays(binding.element_binding, element)
87
+ end
88
+ end
89
+ elsif binding.type.structured?
90
+ binding.type.each_member do |name, type|
91
+ member_binding = register_type(type)
92
+ member_value = value.respond_to?('[]') ? value[name] : value.send(name)
93
+ annotate_arrays(member_binding, member_value) if type.custom?
94
+ end
95
+ end
96
+ end
97
+
98
+ private
99
+ def typed_struct_factory(type_class)
100
+ if Object.const_defined?('ActiveRecord')
101
+ if type_class.ancestors.include?(ActiveRecord::Base)
102
+ qname = XSD::QName.new(@namespace, soap_type_name(type_class.name))
103
+ type_class.instance_variable_set('@qname', qname)
104
+ return SoapActiveRecordStructFactory.new
105
+ end
106
+ end
107
+ SOAP::Mapping::Registry::TypedStructFactory
108
+ end
109
+
110
+ def mark_typed_array(array, qname)
111
+ (class << array; self; end).class_eval do
112
+ define_method(:arytype) do
113
+ qname
114
+ end
115
+ end
116
+ end
117
+
118
+ def soap_base_type_name(type)
119
+ xsd_type = type.ancestors.find{ |c| c.const_defined? 'Type' }
120
+ xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
121
+ end
122
+
123
+ def soap_type_name(type_name)
124
+ type_name.gsub(/::/, '..')
125
+ end
126
+
127
+ def register_static_factories
128
+ @registry.add(ActionWebService::Base64, SOAP::SOAPBase64, SoapBase64Factory.new, nil)
129
+ mapping = @registry.find_mapped_soap_class(ActionWebService::Base64)
130
+ @type2binding[ActionWebService::Base64] =
131
+ SoapBinding.new(self, SOAP::SOAPBase64::Type, ActionWebService::Base64, mapping)
132
+ @registry.add(Array, SOAP::SOAPArray, SoapTypedArrayFactory.new, nil)
133
+ @registry.add(::BigDecimal, SOAP::SOAPDouble, SOAP::Mapping::Registry::BasetypeFactory, {:derived_class => true})
134
+ end
135
+ end
136
+
137
+ class SoapBinding
138
+ attr :qname
139
+ attr :type
140
+ attr :mapping
141
+ attr :element_binding
142
+
143
+ def initialize(marshaler, qname, type, mapping, element_binding=nil)
144
+ @marshaler = marshaler
145
+ @qname = qname
146
+ @type = type
147
+ @mapping = mapping
148
+ @element_binding = element_binding
149
+ end
150
+
151
+ def type_name
152
+ @type.custom? ? @qname.name : nil
153
+ end
154
+
155
+ def qualified_type_name(ns=nil)
156
+ if @type.custom?
157
+ "#{ns ? ns : @qname.namespace}:#{@qname.name}"
158
+ else
159
+ ns = XSD::NS.new
160
+ ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
161
+ ns.assign(SOAP::EncodingNamespace, "soapenc")
162
+ xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
163
+ return ns.name(XSD::AnyTypeName) unless xsd_klass
164
+ ns.name(xsd_klass.const_get('Type'))
165
+ end
166
+ end
167
+
168
+ def eql?(other)
169
+ @qname == other.qname
170
+ end
171
+ alias :== :eql?
172
+
173
+ def hash
174
+ @qname.hash
175
+ end
176
+ end
177
+
178
+ class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
179
+ def obj2soap(soap_class, obj, info, map)
180
+ unless obj.is_a?(ActiveRecord::Base)
181
+ return nil
182
+ end
183
+ soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
184
+ obj.class.columns.each do |column|
185
+ key = column.name.to_s
186
+ value = obj.send(key)
187
+ soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
188
+ end
189
+ soap_obj
190
+ end
191
+
192
+ def soap2obj(obj_class, node, info, map)
193
+ unless node.type == obj_class.instance_variable_get('@qname')
194
+ return false
195
+ end
196
+ obj = obj_class.new
197
+ node.each do |key, value|
198
+ obj[key] = value.data
199
+ end
200
+ obj.instance_variable_set('@new_record', false)
201
+ return true, obj
202
+ end
203
+ end
204
+
205
+ class SoapTypedArrayFactory < SOAP::Mapping::Factory
206
+ def obj2soap(soap_class, obj, info, map)
207
+ unless obj.respond_to?(:arytype)
208
+ return nil
209
+ end
210
+ soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
211
+ mark_marshalled_obj(obj, soap_obj)
212
+ obj.each do |item|
213
+ child = SOAP::Mapping._obj2soap(item, map)
214
+ soap_obj.add(child)
215
+ end
216
+ soap_obj
217
+ end
218
+
219
+ def soap2obj(obj_class, node, info, map)
220
+ return false
221
+ end
222
+ end
223
+
224
+ class SoapBase64Factory < SOAP::Mapping::Factory
225
+ def obj2soap(soap_class, obj, info, map)
226
+ unless obj.is_a?(ActionWebService::Base64)
227
+ return nil
228
+ end
229
+ return soap_class.new(obj)
230
+ end
231
+
232
+ def soap2obj(obj_class, node, info, map)
233
+ unless node.type == SOAP::SOAPBase64::Type
234
+ return false
235
+ end
236
+ return true, obj_class.new(node.string)
237
+ end
238
+ end
239
+
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,122 @@
1
+ require 'xmlrpc/marshal'
2
+ require 'action_web_service/client/xmlrpc_client'
3
+
4
+ module XMLRPC # :nodoc:
5
+ class FaultException # :nodoc:
6
+ alias :message :faultString
7
+ end
8
+
9
+ class Create
10
+ def wrong_type(value)
11
+ if BigDecimal === value
12
+ [true, value.to_f]
13
+ else
14
+ false
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ module ActionWebService # :nodoc:
21
+ module API # :nodoc:
22
+ class Base # :nodoc:
23
+ def self.xmlrpc_client(endpoint_uri, options={})
24
+ ActionWebService::Client::XmlRpc.new self, endpoint_uri, options
25
+ end
26
+ end
27
+ end
28
+
29
+ module Protocol # :nodoc:
30
+ module XmlRpc # :nodoc:
31
+ def self.included(base)
32
+ base.register_protocol(XmlRpcProtocol)
33
+ end
34
+
35
+ class XmlRpcProtocol < AbstractProtocol # :nodoc:
36
+ def self.create(controller)
37
+ XmlRpcProtocol.new
38
+ end
39
+
40
+ def decode_action_pack_request(action_pack_request)
41
+ service_name = action_pack_request.parameters['action']
42
+ decode_request(action_pack_request.raw_post, service_name)
43
+ end
44
+
45
+ def decode_request(raw_request, service_name)
46
+ method_name, params = XMLRPC::Marshal.load_call(raw_request)
47
+ Request.new(self, method_name, params, service_name)
48
+ rescue
49
+ return nil
50
+ end
51
+
52
+ def encode_request(method_name, params, param_types)
53
+ if param_types
54
+ params = params.dup
55
+ param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
56
+ end
57
+ XMLRPC::Marshal.dump_call(method_name, *params)
58
+ end
59
+
60
+ def decode_response(raw_response)
61
+ [nil, XMLRPC::Marshal.load_response(raw_response)]
62
+ end
63
+
64
+ def encode_response(method_name, return_value, return_type, protocol_options={})
65
+ if return_value && return_type
66
+ return_value = value_to_xmlrpc_wire_format(return_value, return_type)
67
+ end
68
+ return_value = false if return_value.nil?
69
+ raw_response = XMLRPC::Marshal.dump_response(return_value)
70
+ Response.new(raw_response, 'text/xml', return_value)
71
+ end
72
+
73
+ def encode_multicall_response(responses, protocol_options={})
74
+ result = responses.map do |return_value, return_type|
75
+ if return_value && return_type
76
+ return_value = value_to_xmlrpc_wire_format(return_value, return_type)
77
+ return_value = [return_value] unless return_value.nil?
78
+ end
79
+ return_value = false if return_value.nil?
80
+ return_value
81
+ end
82
+ raw_response = XMLRPC::Marshal.dump_response(result)
83
+ Response.new(raw_response, 'text/xml', result)
84
+ end
85
+
86
+ def protocol_client(api, protocol_name, endpoint_uri, options={})
87
+ return nil unless protocol_name == :xmlrpc
88
+ ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
89
+ end
90
+
91
+ def value_to_xmlrpc_wire_format(value, value_type)
92
+ if value_type.array?
93
+ value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
94
+ else
95
+ if value.is_a?(ActionWebService::Struct)
96
+ struct = {}
97
+ value.class.members.each do |name, type|
98
+ member_value = value[name]
99
+ next if member_value.nil?
100
+ struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
101
+ end
102
+ struct
103
+ elsif value.is_a?(ActiveRecord::Base)
104
+ struct = {}
105
+ value.attributes.each do |key, member_value|
106
+ next if member_value.nil?
107
+ struct[key.to_s] = member_value
108
+ end
109
+ struct
110
+ elsif value.is_a?(ActionWebService::Base64)
111
+ XMLRPC::Base64.new(value)
112
+ elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
113
+ XMLRPC::FaultException.new(2, value.message)
114
+ else
115
+ value
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,281 @@
1
+ require 'benchmark'
2
+ require 'pathname'
3
+
4
+ module ActionWebService
5
+ module Scaffolding # :nodoc:
6
+ class ScaffoldingError < ActionWebServiceError # :nodoc:
7
+ end
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ # Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
14
+ # generated scaffold actions have default views to let you enter the method parameters and view the
15
+ # results.
16
+ #
17
+ # Example:
18
+ #
19
+ # class ApiController < ActionController
20
+ # web_service_scaffold :invoke
21
+ # end
22
+ #
23
+ # This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
24
+ # your browser, select the API method, enter its parameters, and perform the invocation.
25
+ #
26
+ # If you want to customize the default views, create the following views in "app/views":
27
+ #
28
+ # * <tt>action_name/methods.html.erb</tt>
29
+ # * <tt>action_name/parameters.html.erb</tt>
30
+ # * <tt>action_name/result.html.erb</tt>
31
+ # * <tt>action_name/layout.html.erb</tt>
32
+ #
33
+ # Where <tt>action_name</tt> is the name of the action you gave to ClassMethods#web_service_scaffold.
34
+ #
35
+ # You can use the default views in <tt>RAILS_DIR/lib/action_web_service/templates/scaffolds</tt> as
36
+ # a guide.
37
+ module ClassMethods
38
+ # Generates web service invocation scaffolding for the current controller. The given action name
39
+ # can then be used as the entry point for invoking API methods from a web browser.
40
+ def web_service_scaffold(action_name)
41
+ add_template_helper(Helpers)
42
+ module_eval <<-"end_eval", __FILE__, __LINE__ + 1
43
+ def #{action_name}
44
+ if request.method == :get
45
+ setup_invocation_assigns
46
+ render_invocation_scaffold 'methods'
47
+ end
48
+ end
49
+
50
+ def #{action_name}_method_params
51
+ if request.method == :get
52
+ setup_invocation_assigns
53
+ render_invocation_scaffold 'parameters'
54
+ end
55
+ end
56
+
57
+ def #{action_name}_submit
58
+ if request.method == :post
59
+ setup_invocation_assigns
60
+ protocol_name = params['protocol'] ? params['protocol'].to_sym : :soap
61
+ case protocol_name
62
+ when :soap
63
+ @protocol = Protocol::Soap::SoapProtocol.create(self)
64
+ when :xmlrpc
65
+ @protocol = Protocol::XmlRpc::XmlRpcProtocol.create(self)
66
+ end
67
+ bm = Benchmark.measure do
68
+ @protocol.register_api(@scaffold_service.api)
69
+ post_params = params['method_params'] ? params['method_params'].dup : nil
70
+ params = []
71
+ @scaffold_method.expects.each_with_index do |spec, i|
72
+ params << post_params[i.to_s]
73
+ end if @scaffold_method.expects
74
+ params = @scaffold_method.cast_expects(params)
75
+ method_name = public_method_name(@scaffold_service.name, @scaffold_method.public_name)
76
+ @method_request_xml = @protocol.encode_request(method_name, params, @scaffold_method.expects)
77
+ new_request = @protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
78
+ prepare_request(new_request, @scaffold_service.name, @scaffold_method.public_name)
79
+ self.request = new_request
80
+ if @scaffold_container.dispatching_mode != :direct
81
+ request.parameters['action'] = @scaffold_service.name
82
+ end
83
+ dispatch_web_service_request
84
+ @method_response_xml = response.body
85
+ method_name, obj = @protocol.decode_response(@method_response_xml)
86
+ return if handle_invocation_exception(obj)
87
+ @method_return_value = @scaffold_method.cast_returns(obj)
88
+ end
89
+ @method_elapsed = bm.real
90
+ reset_invocation_response
91
+ render_invocation_scaffold 'result'
92
+ end
93
+ end
94
+
95
+ private
96
+ def setup_invocation_assigns
97
+ @scaffold_class = self.class
98
+ @scaffold_action_name = "#{action_name}"
99
+ @scaffold_container = WebServiceModel::Container.new(self)
100
+ if params['service'] && params['method']
101
+ @scaffold_service = @scaffold_container.services.find{ |x| x.name == params['service'] }
102
+ @scaffold_method = @scaffold_service.api_methods[params['method']]
103
+ end
104
+ end
105
+
106
+ def render_invocation_scaffold(action)
107
+ customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
108
+ default_template = scaffold_path(action)
109
+ begin
110
+ content = @template.render(:file => customized_template)
111
+ rescue ActionView::MissingTemplate
112
+ content = @template.render(:file => default_template)
113
+ end
114
+ @template.instance_variable_set("@content_for_layout", content)
115
+ if self.active_layout.nil?
116
+ render :file => scaffold_path("layout")
117
+ else
118
+ render :file => self.active_layout, :use_full_path => true
119
+ end
120
+ end
121
+
122
+ def scaffold_path(template_name)
123
+ File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".html.erb"
124
+ end
125
+
126
+ def reset_invocation_response
127
+ erase_render_results
128
+ response.instance_variable_set :@header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
129
+ end
130
+
131
+ def public_method_name(service_name, method_name)
132
+ if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
133
+ service_name + '.' + method_name
134
+ else
135
+ method_name
136
+ end
137
+ end
138
+
139
+ def prepare_request(new_request, service_name, method_name)
140
+ new_request.parameters.update(request.parameters)
141
+ request.env.each{ |k, v| new_request.env[k] = v unless new_request.env.has_key?(k) }
142
+ if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
143
+ new_request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
144
+ end
145
+ end
146
+
147
+ def handle_invocation_exception(obj)
148
+ exception = nil
149
+ if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
150
+ exception = obj.detail.cause
151
+ elsif obj.is_a?(XMLRPC::FaultException)
152
+ exception = obj
153
+ end
154
+ return unless exception
155
+ reset_invocation_response
156
+ rescue_action(exception)
157
+ true
158
+ end
159
+ end_eval
160
+ end
161
+ end
162
+
163
+ module Helpers # :nodoc:
164
+ def method_parameter_input_fields(method, type, field_name_base, idx, was_structured=false)
165
+ if type.array?
166
+ return content_tag('em', "Typed array input fields not supported yet (#{type.name})")
167
+ end
168
+ if type.structured?
169
+ return content_tag('em', "Nested structural types not supported yet (#{type.name})") if was_structured
170
+ parameters = ""
171
+ type.each_member do |member_name, member_type|
172
+ label = method_parameter_label(member_name, member_type)
173
+ nested_content = method_parameter_input_fields(
174
+ method,
175
+ member_type,
176
+ "#{field_name_base}[#{idx}][#{member_name}]",
177
+ idx,
178
+ true)
179
+ if member_type.custom?
180
+ parameters << content_tag('li', label)
181
+ parameters << content_tag('ul', nested_content)
182
+ else
183
+ parameters << content_tag('li', label + ' ' + nested_content)
184
+ end
185
+ end
186
+ content_tag('ul', parameters)
187
+ else
188
+ # If the data source was structured previously we already have the index set
189
+ field_name_base = "#{field_name_base}[#{idx}]" unless was_structured
190
+
191
+ case type.type
192
+ when :int
193
+ text_field_tag "#{field_name_base}"
194
+ when :string
195
+ text_field_tag "#{field_name_base}"
196
+ when :base64
197
+ text_area_tag "#{field_name_base}", nil, :size => "40x5"
198
+ when :bool
199
+ radio_button_tag("#{field_name_base}", "true") + " True" +
200
+ radio_button_tag("#{field_name_base}", "false") + "False"
201
+ when :float
202
+ text_field_tag "#{field_name_base}"
203
+ when :time, :datetime
204
+ time = Time.now
205
+ i = 0
206
+ %w|year month day hour minute second|.map do |name|
207
+ i += 1
208
+ send("select_#{name}", time, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
209
+ end.join
210
+ when :date
211
+ date = Date.today
212
+ i = 0
213
+ %w|year month day|.map do |name|
214
+ i += 1
215
+ send("select_#{name}", date, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
216
+ end.join
217
+ end
218
+ end
219
+ end
220
+
221
+ def method_parameter_label(name, type)
222
+ name.to_s.capitalize + ' (' + type.human_name(false) + ')'
223
+ end
224
+
225
+ def service_method_list(service)
226
+ action = @scaffold_action_name + '_method_params'
227
+ methods = service.api_methods_full.sort {|a, b| a[1] <=> b[1]}.map do |desc, name|
228
+ content_tag("li", link_to(name, :action => action, :service => service.name, :method => name))
229
+ end
230
+ content_tag("ul", methods.join("\n"))
231
+ end
232
+ end
233
+
234
+ module WebServiceModel # :nodoc:
235
+ class Container # :nodoc:
236
+ attr :services
237
+ attr :dispatching_mode
238
+
239
+ def initialize(real_container)
240
+ @real_container = real_container
241
+ @dispatching_mode = @real_container.class.web_service_dispatching_mode
242
+ @services = []
243
+ if @dispatching_mode == :direct
244
+ @services << Service.new(@real_container.controller_name, @real_container)
245
+ else
246
+ @real_container.class.web_services.each do |name, obj|
247
+ @services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ class Service # :nodoc:
254
+ attr :name
255
+ attr :object
256
+ attr :api
257
+ attr :api_methods
258
+ attr :api_methods_full
259
+
260
+ def initialize(name, real_service)
261
+ @name = name.to_s
262
+ @object = real_service
263
+ @api = @object.class.web_service_api
264
+ if @api.nil?
265
+ raise ScaffoldingError, "No web service API attached to #{object.class}"
266
+ end
267
+ @api_methods = {}
268
+ @api_methods_full = []
269
+ @api.api_methods.each do |name, method|
270
+ @api_methods[method.public_name.to_s] = method
271
+ @api_methods_full << [method.to_s, method.public_name.to_s]
272
+ end
273
+ end
274
+
275
+ def to_s
276
+ self.name.camelize
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end