axtro-actionwebservice 2.3.5.1.20101118142125

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 (85) hide show
  1. data/CHANGELOG +335 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +381 -0
  4. data/Rakefile +184 -0
  5. data/TODO +32 -0
  6. data/examples/googlesearch/README +143 -0
  7. data/examples/googlesearch/autoloading/google_search_api.rb +51 -0
  8. data/examples/googlesearch/autoloading/google_search_controller.rb +58 -0
  9. data/examples/googlesearch/delegated/google_search_service.rb +109 -0
  10. data/examples/googlesearch/delegated/search_controller.rb +8 -0
  11. data/examples/googlesearch/direct/google_search_api.rb +51 -0
  12. data/examples/googlesearch/direct/search_controller.rb +59 -0
  13. data/examples/metaWeblog/README +17 -0
  14. data/examples/metaWeblog/apis/blogger_api.rb +61 -0
  15. data/examples/metaWeblog/apis/blogger_service.rb +35 -0
  16. data/examples/metaWeblog/apis/meta_weblog_api.rb +68 -0
  17. data/examples/metaWeblog/apis/meta_weblog_service.rb +49 -0
  18. data/examples/metaWeblog/controllers/xmlrpc_controller.rb +17 -0
  19. data/generators/web_service/USAGE +28 -0
  20. data/generators/web_service/templates/api_definition.rb +6 -0
  21. data/generators/web_service/templates/controller.rb +9 -0
  22. data/generators/web_service/templates/functional_test.rb +20 -0
  23. data/generators/web_service/web_service_generator.rb +30 -0
  24. data/lib/action_web_service.rb +61 -0
  25. data/lib/action_web_service/acts_as_web_service.rb +26 -0
  26. data/lib/action_web_service/api.rb +298 -0
  27. data/lib/action_web_service/base.rb +39 -0
  28. data/lib/action_web_service/casting.rb +160 -0
  29. data/lib/action_web_service/client.rb +4 -0
  30. data/lib/action_web_service/client/base.rb +29 -0
  31. data/lib/action_web_service/client/soap_client.rb +114 -0
  32. data/lib/action_web_service/client/xmlrpc_client.rb +59 -0
  33. data/lib/action_web_service/container.rb +4 -0
  34. data/lib/action_web_service/container/action_controller_container.rb +94 -0
  35. data/lib/action_web_service/container/delegated_container.rb +87 -0
  36. data/lib/action_web_service/container/direct_container.rb +70 -0
  37. data/lib/action_web_service/dispatcher.rb +3 -0
  38. data/lib/action_web_service/dispatcher/abstract.rb +209 -0
  39. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +397 -0
  40. data/lib/action_web_service/invocation.rb +203 -0
  41. data/lib/action_web_service/protocol.rb +5 -0
  42. data/lib/action_web_service/protocol/abstract.rb +113 -0
  43. data/lib/action_web_service/protocol/discovery.rb +38 -0
  44. data/lib/action_web_service/protocol/soap_protocol.rb +177 -0
  45. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +243 -0
  46. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb~ +243 -0
  47. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +124 -0
  48. data/lib/action_web_service/scaffolding.rb +282 -0
  49. data/lib/action_web_service/simple.rb +54 -0
  50. data/lib/action_web_service/string_to_datetime_for_soap.rb +17 -0
  51. data/lib/action_web_service/struct.rb +69 -0
  52. data/lib/action_web_service/support/class_inheritable_options.rb +27 -0
  53. data/lib/action_web_service/support/signature_types.rb +262 -0
  54. data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
  55. data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
  56. data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
  57. data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
  58. data/lib/action_web_service/test_invoke.rb +111 -0
  59. data/lib/action_web_service/version.rb +11 -0
  60. data/lib/action_web_service/version.rb~ +10 -0
  61. data/lib/actionwebservice.rb +2 -0
  62. data/setup.rb +1380 -0
  63. data/test/abstract_client.rb +185 -0
  64. data/test/abstract_dispatcher.rb +550 -0
  65. data/test/abstract_unit.rb +44 -0
  66. data/test/api_test.rb +103 -0
  67. data/test/apis/auto_load_api.rb +4 -0
  68. data/test/apis/broken_auto_load_api.rb +3 -0
  69. data/test/base_test.rb +43 -0
  70. data/test/casting_test.rb +96 -0
  71. data/test/client_soap_test.rb +157 -0
  72. data/test/client_xmlrpc_test.rb +155 -0
  73. data/test/container_test.rb +76 -0
  74. data/test/dispatcher_action_controller_soap_test.rb +147 -0
  75. data/test/dispatcher_action_controller_xmlrpc_test.rb +60 -0
  76. data/test/fixtures/db_definitions/mysql.sql +8 -0
  77. data/test/fixtures/db_definitions/sqlite3.sql +8 -0
  78. data/test/fixtures/users.yml +12 -0
  79. data/test/gencov +3 -0
  80. data/test/invocation_test.rb +187 -0
  81. data/test/run +6 -0
  82. data/test/scaffolded_controller_test.rb +148 -0
  83. data/test/struct_test.rb +85 -0
  84. data/test/test_invoke_test.rb +114 -0
  85. metadata +167 -0
@@ -0,0 +1,124 @@
1
+ # encoding: UTF-8
2
+ require 'xmlrpc/marshal'
3
+ require 'action_web_service/client/xmlrpc_client'
4
+
5
+ module XMLRPC # :nodoc:
6
+ class FaultException # :nodoc:
7
+ alias :message :faultString
8
+ end
9
+
10
+ class Create
11
+ def wrong_type(value)
12
+ if BigDecimal === value
13
+ [true, value.to_f]
14
+ else
15
+ false
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ module ActionWebService # :nodoc:
22
+ module API # :nodoc:
23
+ class Base # :nodoc:
24
+ def self.xmlrpc_client(endpoint_uri, options={})
25
+ ActionWebService::Client::XmlRpc.new self, endpoint_uri, options
26
+ end
27
+ end
28
+ end
29
+
30
+ module Protocol # :nodoc:
31
+ module XmlRpc # :nodoc:
32
+ def self.included(base)
33
+ base.register_protocol(XmlRpcProtocol)
34
+ end
35
+
36
+ class XmlRpcProtocol < AbstractProtocol # :nodoc:
37
+ def self.create(controller)
38
+ XmlRpcProtocol.new
39
+ end
40
+
41
+ def decode_action_pack_request(action_pack_request)
42
+ service_name = action_pack_request.parameters['action']
43
+ decode_request(action_pack_request.raw_post, service_name)
44
+ end
45
+
46
+ def decode_request(raw_request, service_name)
47
+ method_name, params = XMLRPC::Marshal.load_call(raw_request)
48
+ Request.new(self, method_name, params, service_name)
49
+ rescue
50
+ return nil
51
+ end
52
+
53
+ def encode_request(method_name, params, param_types)
54
+ if param_types
55
+ params = params.dup
56
+ param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
57
+ end
58
+ XMLRPC::Marshal.dump_call(method_name, *params)
59
+ end
60
+
61
+ def decode_response(raw_response)
62
+ [nil, XMLRPC::Marshal.load_response(raw_response)]
63
+ end
64
+
65
+ def encode_response(method_name, return_value, return_type, protocol_options={})
66
+ if return_value && return_type
67
+ return_value = value_to_xmlrpc_wire_format(return_value, return_type)
68
+ end
69
+ return_value = false if return_value.nil?
70
+ raw_response = XMLRPC::Marshal.dump_response(return_value)
71
+ Response.new(raw_response, 'text/xml', return_value)
72
+ end
73
+
74
+ def encode_multicall_response(responses, protocol_options={})
75
+ result = responses.map do |return_value, return_type|
76
+ if return_value && return_type
77
+ return_value = value_to_xmlrpc_wire_format(return_value, return_type)
78
+ return_value = [return_value] unless return_value.nil?
79
+ end
80
+ return_value = false if return_value.nil?
81
+ return_value
82
+ end
83
+ raw_response = XMLRPC::Marshal.dump_response(result)
84
+ Response.new(raw_response, 'text/xml', result)
85
+ end
86
+
87
+ def protocol_client(api, protocol_name, endpoint_uri, options={})
88
+ return nil unless protocol_name == :xmlrpc
89
+ ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
90
+ end
91
+
92
+ def value_to_xmlrpc_wire_format(value, value_type)
93
+ if value_type.array?
94
+ value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
95
+ else
96
+ if value.is_a?(ActionWebService::Struct)
97
+ struct = {}
98
+ value.class.members.each do |name, type_options|
99
+ type, options = type_options
100
+ member_value = value[name]
101
+ next if member_value.nil?
102
+ struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
103
+ end
104
+ struct
105
+ elsif value.is_a?(ActiveRecord::Base)
106
+ struct = {}
107
+ value.attributes.each do |key, member_value|
108
+ next if member_value.nil?
109
+ struct[key.to_s] = member_value
110
+ end
111
+ struct
112
+ elsif value.is_a?(ActionWebService::Base64)
113
+ XMLRPC::Base64.new(value)
114
+ elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
115
+ XMLRPC::FaultException.new(2, value.message)
116
+ else
117
+ value
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,282 @@
1
+ # encoding: UTF-8
2
+ require 'benchmark'
3
+ require 'pathname'
4
+
5
+ module ActionWebService
6
+ module Scaffolding # :nodoc:
7
+ class ScaffoldingError < ActionWebServiceError # :nodoc:
8
+ end
9
+
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+ end
13
+
14
+ # Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
15
+ # generated scaffold actions have default views to let you enter the method parameters and view the
16
+ # results.
17
+ #
18
+ # Example:
19
+ #
20
+ # class ApiController < ActionController
21
+ # web_service_scaffold :invoke
22
+ # end
23
+ #
24
+ # This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
25
+ # your browser, select the API method, enter its parameters, and perform the invocation.
26
+ #
27
+ # If you want to customize the default views, create the following views in "app/views":
28
+ #
29
+ # * <tt>action_name/methods.html.erb</tt>
30
+ # * <tt>action_name/parameters.html.erb</tt>
31
+ # * <tt>action_name/result.html.erb</tt>
32
+ # * <tt>action_name/layout.html.erb</tt>
33
+ #
34
+ # Where <tt>action_name</tt> is the name of the action you gave to ClassMethods#web_service_scaffold.
35
+ #
36
+ # You can use the default views in <tt>RAILS_DIR/lib/action_web_service/templates/scaffolds</tt> as
37
+ # a guide.
38
+ module ClassMethods
39
+ # Generates web service invocation scaffolding for the current controller. The given action name
40
+ # can then be used as the entry point for invoking API methods from a web browser.
41
+ def web_service_scaffold(action_name)
42
+ add_template_helper(Helpers)
43
+ module_eval <<-"end_eval", __FILE__, __LINE__ + 1
44
+ def #{action_name}
45
+ if request.method == :get
46
+ setup_invocation_assigns
47
+ render_invocation_scaffold 'methods'
48
+ end
49
+ end
50
+
51
+ def #{action_name}_method_params
52
+ if request.method == :get
53
+ setup_invocation_assigns
54
+ render_invocation_scaffold 'parameters'
55
+ end
56
+ end
57
+
58
+ def #{action_name}_submit
59
+ if request.method == :post
60
+ setup_invocation_assigns
61
+ protocol_name = params['protocol'] ? params['protocol'].to_sym : :soap
62
+ case protocol_name
63
+ when :soap
64
+ @protocol = Protocol::Soap::SoapProtocol.create(self)
65
+ when :xmlrpc
66
+ @protocol = Protocol::XmlRpc::XmlRpcProtocol.create(self)
67
+ end
68
+ bm = Benchmark.measure do
69
+ @protocol.register_api(@scaffold_service.api)
70
+ post_params = params['method_params'] ? params['method_params'].dup : nil
71
+ params = []
72
+ @scaffold_method.expects.each_with_index do |spec, i|
73
+ params << post_params[i.to_s]
74
+ end if @scaffold_method.expects
75
+ params = @scaffold_method.cast_expects(params)
76
+ method_name = public_method_name(@scaffold_service.name, @scaffold_method.public_name)
77
+ @method_request_xml = @protocol.encode_request(method_name, params, @scaffold_method.expects)
78
+ new_request = @protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
79
+ prepare_request(new_request, @scaffold_service.name, @scaffold_method.public_name)
80
+ self.request = new_request
81
+ if @scaffold_container.dispatching_mode != :direct
82
+ request.parameters['action'] = @scaffold_service.name
83
+ end
84
+ dispatch_web_service_request
85
+ @method_response_xml = response.body
86
+ method_name, obj = @protocol.decode_response(@method_response_xml)
87
+ return if handle_invocation_exception(obj)
88
+ @method_return_value = @scaffold_method.cast_returns(obj)
89
+ end
90
+ @method_elapsed = bm.real
91
+ reset_invocation_response
92
+ render_invocation_scaffold 'result'
93
+ end
94
+ end
95
+
96
+ private
97
+ def setup_invocation_assigns
98
+ @scaffold_class = self.class
99
+ @scaffold_action_name = "#{action_name}"
100
+ @scaffold_container = WebServiceModel::Container.new(self)
101
+ if params['service'] && params['method']
102
+ @scaffold_service = @scaffold_container.services.find{ |x| x.name == params['service'] }
103
+ @scaffold_method = @scaffold_service.api_methods[params['method']]
104
+ end
105
+ end
106
+
107
+ def render_invocation_scaffold(action)
108
+ customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
109
+ default_template = scaffold_path(action)
110
+ begin
111
+ content = @template.render(:file => customized_template)
112
+ rescue ActionView::MissingTemplate
113
+ content = @template.render(:file => default_template)
114
+ end
115
+ @template.instance_variable_set("@content_for_layout", content)
116
+ if self.active_layout.nil?
117
+ render :file => scaffold_path("layout")
118
+ else
119
+ render :file => self.active_layout, :use_full_path => true
120
+ end
121
+ end
122
+
123
+ def scaffold_path(template_name)
124
+ File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".html.erb"
125
+ end
126
+
127
+ def reset_invocation_response
128
+ erase_render_results
129
+ response.instance_variable_set :@header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
130
+ end
131
+
132
+ def public_method_name(service_name, method_name)
133
+ if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
134
+ service_name + '.' + method_name
135
+ else
136
+ method_name
137
+ end
138
+ end
139
+
140
+ def prepare_request(new_request, service_name, method_name)
141
+ new_request.parameters.update(request.parameters)
142
+ request.env.each{ |k, v| new_request.env[k] = v unless new_request.env.has_key?(k) }
143
+ if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
144
+ new_request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
145
+ end
146
+ end
147
+
148
+ def handle_invocation_exception(obj)
149
+ exception = nil
150
+ if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
151
+ exception = obj.detail.cause
152
+ elsif obj.is_a?(XMLRPC::FaultException)
153
+ exception = obj
154
+ end
155
+ return unless exception
156
+ reset_invocation_response
157
+ rescue_action(exception)
158
+ true
159
+ end
160
+ end_eval
161
+ end
162
+ end
163
+
164
+ module Helpers # :nodoc:
165
+ def method_parameter_input_fields(method, type, field_name_base, idx, was_structured=false)
166
+ if type.array?
167
+ return content_tag('em', "Typed array input fields not supported yet (#{type.name})")
168
+ end
169
+ if type.structured?
170
+ return content_tag('em', "Nested structural types not supported yet (#{type.name})") if was_structured
171
+ parameters = ""
172
+ type.each_member do |member_name, member_type|
173
+ label = method_parameter_label(member_name, member_type)
174
+ nested_content = method_parameter_input_fields(
175
+ method,
176
+ member_type,
177
+ "#{field_name_base}[#{idx}][#{member_name}]",
178
+ idx,
179
+ true)
180
+ if member_type.custom?
181
+ parameters << content_tag('li', label)
182
+ parameters << content_tag('ul', nested_content)
183
+ else
184
+ parameters << content_tag('li', label + ' ' + nested_content)
185
+ end
186
+ end
187
+ content_tag('ul', parameters)
188
+ else
189
+ # If the data source was structured previously we already have the index set
190
+ field_name_base = "#{field_name_base}[#{idx}]" unless was_structured
191
+
192
+ case type.type
193
+ when :int
194
+ text_field_tag "#{field_name_base}"
195
+ when :string
196
+ text_field_tag "#{field_name_base}"
197
+ when :base64
198
+ text_area_tag "#{field_name_base}", nil, :size => "40x5"
199
+ when :bool
200
+ radio_button_tag("#{field_name_base}", "true") + " True" +
201
+ radio_button_tag("#{field_name_base}", "false") + "False"
202
+ when :float
203
+ text_field_tag "#{field_name_base}"
204
+ when :time, :datetime
205
+ time = Time.now
206
+ i = 0
207
+ %w|year month day hour minute second|.map do |name|
208
+ i += 1
209
+ send("select_#{name}", time, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
210
+ end.join
211
+ when :date
212
+ date = Date.today
213
+ i = 0
214
+ %w|year month day|.map do |name|
215
+ i += 1
216
+ send("select_#{name}", date, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
217
+ end.join
218
+ end
219
+ end
220
+ end
221
+
222
+ def method_parameter_label(name, type)
223
+ name.to_s.capitalize + ' (' + type.human_name(false) + ')'
224
+ end
225
+
226
+ def service_method_list(service)
227
+ action = @scaffold_action_name + '_method_params'
228
+ methods = service.api_methods_full.sort {|a, b| a[1] <=> b[1]}.map do |desc, name|
229
+ content_tag("li", link_to(name, :action => action, :service => service.name, :method => name))
230
+ end
231
+ content_tag("ul", methods.join("\n"))
232
+ end
233
+ end
234
+
235
+ module WebServiceModel # :nodoc:
236
+ class Container # :nodoc:
237
+ attr :services
238
+ attr :dispatching_mode
239
+
240
+ def initialize(real_container)
241
+ @real_container = real_container
242
+ @dispatching_mode = @real_container.class.web_service_dispatching_mode
243
+ @services = []
244
+ if @dispatching_mode == :direct
245
+ @services << Service.new(@real_container.controller_name, @real_container)
246
+ else
247
+ @real_container.class.web_services.each do |name, obj|
248
+ @services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ class Service # :nodoc:
255
+ attr :name
256
+ attr :object
257
+ attr :api
258
+ attr :api_methods
259
+ attr :api_methods_full
260
+
261
+ def initialize(name, real_service)
262
+ @name = name.to_s
263
+ @object = real_service
264
+ @api = @object.class.web_service_api
265
+ if @api.nil?
266
+ raise ScaffoldingError, "No web service API attached to #{object.class}"
267
+ end
268
+ @api_methods = {}
269
+ @api_methods_full = []
270
+ @api.api_methods.each do |name, method|
271
+ @api_methods[method.public_name.to_s] = method
272
+ @api_methods_full << [method.to_s, method.public_name.to_s]
273
+ end
274
+ end
275
+
276
+ def to_s
277
+ self.name.camelize
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+ module ActionWebService
3
+ # To send simple types across the wire, derive from ActionWebService::Simple,
4
+ # and use +base+ to declare the base type for it restriction and,
5
+ # use +restriction+ to declare valid W3C restriction element for SimpleType.
6
+ #
7
+ # ActionWebService::Simple should be used when you want to declare valid custom simple type
8
+ # to be used inside expects, returns and structured types
9
+ #
10
+ #
11
+ # There plenty documentation available at W3C Archives
12
+ # http://www.w3.org/TR/xmlschema-2/
13
+ #
14
+ # === Example
15
+ #
16
+ # class Gender < ActionWebService::Simple
17
+ # base :string
18
+ # restriction :enumeration, "male|female"
19
+ # end
20
+ #
21
+ #
22
+ class Simple
23
+
24
+ def initialize(value)
25
+ @value = value
26
+ end
27
+
28
+ class << self
29
+ def base(type)
30
+ type = type.to_s.camelize(:lower)
31
+ write_inheritable_attribute("xml_base", type)
32
+ class_eval <<-END
33
+ def xml_base; "#{type}"; end
34
+ END
35
+ end
36
+
37
+ def restriction_base
38
+ read_inheritable_attribute("xml_base") || ""
39
+ end
40
+
41
+ def restriction(type, value)
42
+ type = type.to_s.camelize(:lower)
43
+ write_inheritable_hash("simple_restrictions", type => value)
44
+ end
45
+
46
+ def restrictions
47
+ read_inheritable_attribute("simple_restrictions") || {}
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end