actionwebservice 0.6.2 → 0.7.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.
- data/CHANGELOG +21 -0
- data/README +50 -6
- data/Rakefile +9 -9
- data/TODO +6 -6
- data/lib/action_web_service.rb +4 -3
- data/lib/action_web_service/api.rb +248 -1
- data/lib/action_web_service/casting.rb +111 -0
- data/lib/action_web_service/client/soap_client.rb +17 -33
- data/lib/action_web_service/client/xmlrpc_client.rb +10 -34
- data/lib/action_web_service/container/delegated_container.rb +1 -1
- data/lib/action_web_service/dispatcher/abstract.rb +52 -72
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +71 -55
- data/lib/action_web_service/protocol/abstract.rb +82 -3
- data/lib/action_web_service/protocol/discovery.rb +2 -2
- data/lib/action_web_service/protocol/soap_protocol.rb +95 -22
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +197 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +56 -24
- data/lib/action_web_service/scaffolding.rb +237 -0
- data/lib/action_web_service/struct.rb +17 -4
- data/lib/action_web_service/support/signature_types.rb +194 -0
- data/lib/action_web_service/templates/scaffolds/layout.rhtml +65 -0
- data/lib/action_web_service/templates/scaffolds/methods.rhtml +6 -0
- data/lib/action_web_service/templates/scaffolds/parameters.rhtml +28 -0
- data/lib/action_web_service/templates/scaffolds/result.rhtml +30 -0
- data/lib/action_web_service/test_invoke.rb +23 -42
- data/test/abstract_dispatcher.rb +102 -48
- data/test/abstract_unit.rb +1 -1
- data/test/api_test.rb +40 -7
- data/test/casting_test.rb +82 -0
- data/test/client_soap_test.rb +3 -0
- data/test/client_xmlrpc_test.rb +5 -1
- data/test/dispatcher_action_controller_soap_test.rb +9 -12
- data/test/dispatcher_action_controller_xmlrpc_test.rb +1 -11
- data/test/run +1 -0
- data/test/scaffolded_controller_test.rb +67 -0
- data/test/struct_test.rb +33 -21
- data/test/test_invoke_test.rb +1 -1
- metadata +18 -31
- data/lib/action_web_service/api/base.rb +0 -135
- data/lib/action_web_service/vendor/ws.rb +0 -4
- data/lib/action_web_service/vendor/ws/common.rb +0 -8
- data/lib/action_web_service/vendor/ws/encoding.rb +0 -3
- data/lib/action_web_service/vendor/ws/encoding/abstract.rb +0 -26
- data/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb +0 -90
- data/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb +0 -53
- data/lib/action_web_service/vendor/ws/marshaling.rb +0 -3
- data/lib/action_web_service/vendor/ws/marshaling/abstract.rb +0 -17
- data/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb +0 -277
- data/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb +0 -116
- data/lib/action_web_service/vendor/ws/types.rb +0 -165
- data/test/ws/abstract_encoding.rb +0 -68
- data/test/ws/abstract_unit.rb +0 -13
- data/test/ws/gencov +0 -3
- data/test/ws/run +0 -5
- data/test/ws/soap_marshaling_test.rb +0 -91
- data/test/ws/soap_rpc_encoding_test.rb +0 -47
- data/test/ws/types_test.rb +0 -43
- data/test/ws/xmlrpc_encoding_test.rb +0 -34
@@ -1,3 +1,9 @@
|
|
1
|
+
require 'xmlrpc/marshal'
|
2
|
+
|
3
|
+
class XMLRPC::FaultException
|
4
|
+
alias :message :faultString
|
5
|
+
end
|
6
|
+
|
1
7
|
module ActionWebService # :nodoc:
|
2
8
|
module Protocol # :nodoc:
|
3
9
|
module XmlRpc # :nodoc:
|
@@ -5,43 +11,69 @@ module ActionWebService # :nodoc:
|
|
5
11
|
base.register_protocol(XmlRpcProtocol)
|
6
12
|
end
|
7
13
|
|
8
|
-
class XmlRpcProtocol # :nodoc:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@encoder = WS::Encoding::XmlRpcEncoding.new
|
13
|
-
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
14
|
+
class XmlRpcProtocol < AbstractProtocol # :nodoc:
|
15
|
+
def decode_action_pack_request(action_pack_request)
|
16
|
+
service_name = action_pack_request.parameters['action']
|
17
|
+
decode_request(action_pack_request.raw_post, service_name)
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
method_name, params =
|
18
|
-
params = params.map{|x| @marshaler.unmarshal(x)}
|
19
|
-
service_name = ap_request.parameters['action']
|
20
|
+
def decode_request(raw_request, service_name)
|
21
|
+
method_name, params = XMLRPC::Marshal.load_call(raw_request)
|
20
22
|
Request.new(self, method_name, params, service_name)
|
21
|
-
rescue
|
22
|
-
nil
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
if
|
27
|
-
|
28
|
-
|
29
|
-
return_value = @marshaler.marshal(WS::Param.new(return_value, info))
|
30
|
-
else
|
31
|
-
return_value = nil
|
25
|
+
def encode_request(method_name, params, param_types)
|
26
|
+
if param_types
|
27
|
+
params = params.dup
|
28
|
+
param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
|
32
29
|
end
|
33
|
-
|
34
|
-
|
30
|
+
XMLRPC::Marshal.dump_call(method_name, *params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def decode_response(raw_response)
|
34
|
+
[nil, XMLRPC::Marshal.load_response(raw_response)]
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
38
|
-
nil
|
37
|
+
def encode_response(method_name, return_value, return_type)
|
38
|
+
return_value = true if return_value.nil?
|
39
|
+
if return_type
|
40
|
+
return_value = value_to_xmlrpc_wire_format(return_value, return_type)
|
41
|
+
end
|
42
|
+
raw_response = XMLRPC::Marshal.dump_response(return_value)
|
43
|
+
Response.new(raw_response, 'text/xml', return_value)
|
39
44
|
end
|
40
45
|
|
41
|
-
def protocol_client(api, protocol_name, endpoint_uri, options)
|
46
|
+
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
42
47
|
return nil unless protocol_name == :xmlrpc
|
43
48
|
ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
|
44
49
|
end
|
50
|
+
|
51
|
+
def value_to_xmlrpc_wire_format(value, value_type)
|
52
|
+
if value_type.array?
|
53
|
+
value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
|
54
|
+
else
|
55
|
+
if value.is_a?(ActionWebService::Struct)
|
56
|
+
struct = {}
|
57
|
+
value.class.members.each do |name, type|
|
58
|
+
member_value = value[name]
|
59
|
+
next if member_value.nil?
|
60
|
+
struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
|
61
|
+
end
|
62
|
+
struct
|
63
|
+
elsif value.is_a?(ActiveRecord::Base)
|
64
|
+
struct = {}
|
65
|
+
value.attributes.each do |key, member_value|
|
66
|
+
next if member_value.nil?
|
67
|
+
struct[key.to_s] = member_value
|
68
|
+
end
|
69
|
+
struct
|
70
|
+
elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
|
71
|
+
XMLRPC::FaultException.new(2, value.message)
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
45
77
|
end
|
46
78
|
end
|
47
79
|
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'uri'
|
3
|
+
require 'benchmark'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module ActionWebService
|
7
|
+
module Scaffolding # :nodoc:
|
8
|
+
class ScaffoldingError < ActionWebServiceError # :nodoc:
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.append_features(base)
|
12
|
+
super
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
|
17
|
+
# generated scaffold actions have default views to let you enter the method parameters and view the
|
18
|
+
# results.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
#
|
22
|
+
# class ApiController < ActionController
|
23
|
+
# web_service_scaffold :invoke
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
|
27
|
+
# your browser, select the API method, enter its parameters, and perform the invocation.
|
28
|
+
#
|
29
|
+
# If you want to customize the default views, create the following views in "app/views":
|
30
|
+
#
|
31
|
+
# * <tt>action_name/methods.rhtml</tt>
|
32
|
+
# * <tt>action_name/parameters.rhtml</tt>
|
33
|
+
# * <tt>action_name/result.rhtml</tt>
|
34
|
+
# * <tt>action_name/layout.rhtml</tt>
|
35
|
+
#
|
36
|
+
# Where <tt>action_name</tt> is the name of the action you gave to ClassMethods#web_service_scaffold.
|
37
|
+
#
|
38
|
+
# You can use the default views in <tt>RAILS_DIR/lib/action_web_service/templates/scaffolds</tt> as
|
39
|
+
# a guide.
|
40
|
+
module ClassMethods
|
41
|
+
# Generates web service invocation scaffolding for the current controller. The given action name
|
42
|
+
# can then be used as the entry point for invoking API methods from a web browser.
|
43
|
+
def web_service_scaffold(action_name)
|
44
|
+
add_template_helper(Helpers)
|
45
|
+
module_eval <<-END, __FILE__, __LINE__
|
46
|
+
def #{action_name}
|
47
|
+
if @request.method == :get
|
48
|
+
setup_invocation_assigns
|
49
|
+
render_invocation_scaffold 'methods'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def #{action_name}_method_params
|
54
|
+
if @request.method == :get
|
55
|
+
setup_invocation_assigns
|
56
|
+
render_invocation_scaffold 'parameters'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def #{action_name}_submit
|
61
|
+
if @request.method == :post
|
62
|
+
setup_invocation_assigns
|
63
|
+
protocol_name = @params['protocol'] ? @params['protocol'].to_sym : :soap
|
64
|
+
case protocol_name
|
65
|
+
when :soap
|
66
|
+
@protocol = Protocol::Soap::SoapProtocol.new
|
67
|
+
when :xmlrpc
|
68
|
+
@protocol = Protocol::XmlRpc::XmlRpcProtocol.new
|
69
|
+
end
|
70
|
+
@invocation_cgi = @request.respond_to?(:cgi) ? @request.cgi : nil
|
71
|
+
bm = Benchmark.measure do
|
72
|
+
@protocol.register_api(@scaffold_service.api)
|
73
|
+
params = @params['method_params'] ? @params['method_params'].dup : nil
|
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
|
+
@request = new_request
|
80
|
+
dispatch_web_service_request
|
81
|
+
@method_response_xml = @response.body
|
82
|
+
method_name, obj = @protocol.decode_response(@method_response_xml)
|
83
|
+
return if handle_invocation_exception(obj)
|
84
|
+
@method_return_value = @scaffold_method.cast_returns(obj)
|
85
|
+
end
|
86
|
+
@method_elapsed = bm.real
|
87
|
+
add_instance_variables_to_assigns
|
88
|
+
reset_invocation_response
|
89
|
+
render_invocation_scaffold 'result'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def setup_invocation_assigns
|
95
|
+
@scaffold_class = self.class
|
96
|
+
@scaffold_action_name = "#{action_name}"
|
97
|
+
@scaffold_container = WebServiceModel::Container.new(self)
|
98
|
+
if @params['service'] && @params['method']
|
99
|
+
@scaffold_service = @scaffold_container.services.find{ |x| x.name == @params['service'] }
|
100
|
+
@scaffold_method = @scaffold_service.api_methods[@params['method']]
|
101
|
+
end
|
102
|
+
add_instance_variables_to_assigns
|
103
|
+
end
|
104
|
+
|
105
|
+
def render_invocation_scaffold(action)
|
106
|
+
customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
|
107
|
+
default_template = scaffold_path(action)
|
108
|
+
@content_for_layout = template_exists?(customized_template) ? @template.render_file(customized_template) : @template.render_file(default_template, false)
|
109
|
+
self.active_layout ? render_file(self.active_layout, "200 OK", true) : render_file(scaffold_path("layout"))
|
110
|
+
end
|
111
|
+
|
112
|
+
def scaffold_path(template_name)
|
113
|
+
Pathname.new(File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml").realpath.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def reset_invocation_response
|
117
|
+
template = @response.template
|
118
|
+
if @invocation_cgi
|
119
|
+
@response = ::ActionController::CgiResponse.new(@invocation_cgi)
|
120
|
+
else
|
121
|
+
@response = ::ActionController::TestResponse.new
|
122
|
+
end
|
123
|
+
@response.template = template
|
124
|
+
@performed_render = false
|
125
|
+
end
|
126
|
+
|
127
|
+
def public_method_name(service_name, method_name)
|
128
|
+
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
|
129
|
+
service_name + '.' + method_name
|
130
|
+
else
|
131
|
+
method_name
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def prepare_request(request, service_name, method_name)
|
136
|
+
request.parameters.update(@request.parameters)
|
137
|
+
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
|
138
|
+
request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def handle_invocation_exception(obj)
|
143
|
+
exception = nil
|
144
|
+
if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
|
145
|
+
exception = obj.detail.cause
|
146
|
+
elsif obj.is_a?(XMLRPC::FaultException)
|
147
|
+
exception = obj
|
148
|
+
end
|
149
|
+
return unless exception
|
150
|
+
reset_invocation_response
|
151
|
+
rescue_action(exception)
|
152
|
+
true
|
153
|
+
end
|
154
|
+
END
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module Helpers # :nodoc:
|
159
|
+
def method_parameter_input_fields(method, type)
|
160
|
+
name = type.name.to_s
|
161
|
+
type_name = type.type
|
162
|
+
unless type_name.is_a?(Symbol)
|
163
|
+
raise "Parameter #{name}: Structured/array types not supported in scaffolding input fields yet"
|
164
|
+
end
|
165
|
+
field_name = "method_params[]"
|
166
|
+
case type_name
|
167
|
+
when :int
|
168
|
+
text_field_tag field_name
|
169
|
+
when :string
|
170
|
+
text_field_tag field_name
|
171
|
+
when :bool
|
172
|
+
radio_button_tag field_name, "True"
|
173
|
+
radio_button_tag field_name, "False"
|
174
|
+
when :float
|
175
|
+
text_field_tag field_name
|
176
|
+
when :time
|
177
|
+
select_datetime Time.now, 'name' => field_name
|
178
|
+
when :date
|
179
|
+
select_date Date.today, 'name' => field_name
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def service_method_list(service)
|
184
|
+
action = @scaffold_action_name + '_method_params'
|
185
|
+
methods = service.api_methods_full.map do |desc, name|
|
186
|
+
content_tag("li", link_to(desc, :action => action, :service => service.name, :method => name))
|
187
|
+
end
|
188
|
+
content_tag("ul", methods.join("\n"))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
module WebServiceModel # :nodoc:
|
193
|
+
class Container # :nodoc:
|
194
|
+
attr :services
|
195
|
+
|
196
|
+
def initialize(real_container)
|
197
|
+
@real_container = real_container
|
198
|
+
@services = []
|
199
|
+
if @real_container.class.web_service_dispatching_mode == :direct
|
200
|
+
@services << Service.new(@real_container.controller_name, @real_container)
|
201
|
+
else
|
202
|
+
@real_container.class.web_services.each do |name, obj|
|
203
|
+
@services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class Service # :nodoc:
|
210
|
+
attr :name
|
211
|
+
attr :object
|
212
|
+
attr :api
|
213
|
+
attr :api_methods
|
214
|
+
attr :api_methods_full
|
215
|
+
|
216
|
+
def initialize(name, real_service)
|
217
|
+
@name = name.to_s
|
218
|
+
@object = real_service
|
219
|
+
@api = @object.class.web_service_api
|
220
|
+
if @api.nil?
|
221
|
+
raise ScaffoldingError, "No web service API attached to #{object.class}"
|
222
|
+
end
|
223
|
+
@api_methods = {}
|
224
|
+
@api_methods_full = []
|
225
|
+
@api.api_methods.each do |name, method|
|
226
|
+
@api_methods[method.public_name.to_s] = method
|
227
|
+
@api_methods_full << [method.to_s, method.public_name.to_s]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def to_s
|
232
|
+
self.name.camelize
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -16,15 +16,15 @@ module ActionWebService
|
|
16
16
|
# end
|
17
17
|
# person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe')
|
18
18
|
#
|
19
|
-
# Active Record model classes are already implicitly supported
|
20
|
-
#
|
19
|
+
# Active Record model classes are already implicitly supported in method
|
20
|
+
# signatures.
|
21
21
|
class Struct
|
22
22
|
|
23
23
|
# If a Hash is given as argument to an ActionWebService::Struct constructor,
|
24
24
|
# it can contain initial values for the structure member.
|
25
25
|
def initialize(values={})
|
26
26
|
if values.is_a?(Hash)
|
27
|
-
values.map{|k,v|
|
27
|
+
values.map{|k,v| __send__('%s=' % k.to_s, v)}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -33,11 +33,20 @@ module ActionWebService
|
|
33
33
|
send(name.to_s)
|
34
34
|
end
|
35
35
|
|
36
|
+
# Iterates through each member
|
37
|
+
def each_pair(&block)
|
38
|
+
self.class.members.each do |name, type|
|
39
|
+
yield name, self.__send__(name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
36
43
|
class << self
|
37
44
|
# Creates a structure member with the specified +name+ and +type+. Generates
|
38
45
|
# accessor methods for reading and writing the member value.
|
39
46
|
def member(name, type)
|
40
|
-
|
47
|
+
name = name.to_sym
|
48
|
+
type = ActionWebService::SignatureTypes.canonical_signature_entry({ name => type }, 0)
|
49
|
+
write_inheritable_hash("struct_members", name => type)
|
41
50
|
class_eval <<-END
|
42
51
|
def #{name}; @#{name}; end
|
43
52
|
def #{name}=(value); @#{name} = value; end
|
@@ -47,6 +56,10 @@ module ActionWebService
|
|
47
56
|
def members # :nodoc:
|
48
57
|
read_inheritable_attribute("struct_members") || {}
|
49
58
|
end
|
59
|
+
|
60
|
+
def member_type(name) # :nodoc:
|
61
|
+
members[name.to_sym]
|
62
|
+
end
|
50
63
|
end
|
51
64
|
end
|
52
65
|
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module SignatureTypes # :nodoc:
|
3
|
+
def canonical_signature(signature)
|
4
|
+
return nil if signature.nil?
|
5
|
+
unless signature.is_a?(Array)
|
6
|
+
raise(ActionWebServiceError, "Expected signature to be an Array")
|
7
|
+
end
|
8
|
+
i = -1
|
9
|
+
signature.map{ |spec| canonical_signature_entry(spec, i += 1) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def canonical_signature_entry(spec, i)
|
13
|
+
orig_spec = spec
|
14
|
+
name = "param#{i}"
|
15
|
+
if spec.is_a?(Hash)
|
16
|
+
name, spec = spec.keys.first, spec.values.first
|
17
|
+
end
|
18
|
+
type = spec
|
19
|
+
if spec.is_a?(Array)
|
20
|
+
ArrayType.new(orig_spec, canonical_signature_entry(spec[0], 0), name)
|
21
|
+
else
|
22
|
+
type = canonical_type(type)
|
23
|
+
if type.is_a?(Symbol)
|
24
|
+
BaseType.new(orig_spec, type, name)
|
25
|
+
else
|
26
|
+
StructuredType.new(orig_spec, type, name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def canonical_type(type)
|
32
|
+
type_name = symbol_name(type) || class_to_type_name(type)
|
33
|
+
type = type_name || type
|
34
|
+
return canonical_type_name(type) if type.is_a?(Symbol)
|
35
|
+
type
|
36
|
+
end
|
37
|
+
|
38
|
+
def canonical_type_name(name)
|
39
|
+
name = name.to_sym
|
40
|
+
case name
|
41
|
+
when :int, :integer, :fixnum, :bignum
|
42
|
+
:int
|
43
|
+
when :string, :base64
|
44
|
+
:string
|
45
|
+
when :bool, :boolean
|
46
|
+
:bool
|
47
|
+
when :float, :double
|
48
|
+
:float
|
49
|
+
when :time, :timestamp
|
50
|
+
:time
|
51
|
+
when :datetime
|
52
|
+
:datetime
|
53
|
+
when :date
|
54
|
+
:date
|
55
|
+
else
|
56
|
+
raise(TypeError, "#{name} is not a valid base type")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def canonical_type_class(type)
|
61
|
+
type = canonical_type(type)
|
62
|
+
type.is_a?(Symbol) ? type_name_to_class(type) : type
|
63
|
+
end
|
64
|
+
|
65
|
+
def symbol_name(name)
|
66
|
+
return name.to_sym if name.is_a?(Symbol) || name.is_a?(String)
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def class_to_type_name(klass)
|
71
|
+
klass = klass.class unless klass.is_a?(Class)
|
72
|
+
if derived_from?(Integer, klass) || derived_from?(Fixnum, klass) || derived_from?(Bignum, klass)
|
73
|
+
:int
|
74
|
+
elsif klass == String
|
75
|
+
:string
|
76
|
+
elsif klass == TrueClass || klass == FalseClass
|
77
|
+
:bool
|
78
|
+
elsif derived_from?(Float, klass) || derived_from?(Precision, klass) || derived_from?(Numeric, klass)
|
79
|
+
:float
|
80
|
+
elsif klass == Time
|
81
|
+
:time
|
82
|
+
elsif klass == DateTime
|
83
|
+
:datetime
|
84
|
+
elsif klass == Date
|
85
|
+
:date
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def type_name_to_class(name)
|
92
|
+
case canonical_type_name(name)
|
93
|
+
when :int
|
94
|
+
Integer
|
95
|
+
when :string
|
96
|
+
String
|
97
|
+
when :bool
|
98
|
+
TrueClass
|
99
|
+
when :float
|
100
|
+
Float
|
101
|
+
when :time
|
102
|
+
Time
|
103
|
+
when :date
|
104
|
+
Date
|
105
|
+
when :datetime
|
106
|
+
DateTime
|
107
|
+
else
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def derived_from?(ancestor, child)
|
113
|
+
child.ancestors.include?(ancestor)
|
114
|
+
end
|
115
|
+
|
116
|
+
module_function :type_name_to_class
|
117
|
+
module_function :class_to_type_name
|
118
|
+
module_function :symbol_name
|
119
|
+
module_function :canonical_type_class
|
120
|
+
module_function :canonical_type_name
|
121
|
+
module_function :canonical_type
|
122
|
+
module_function :canonical_signature_entry
|
123
|
+
module_function :canonical_signature
|
124
|
+
module_function :derived_from?
|
125
|
+
end
|
126
|
+
|
127
|
+
class BaseType # :nodoc:
|
128
|
+
include SignatureTypes
|
129
|
+
|
130
|
+
attr :spec
|
131
|
+
attr :type
|
132
|
+
attr :type_class
|
133
|
+
attr :name
|
134
|
+
|
135
|
+
def initialize(spec, type, name)
|
136
|
+
@spec = spec
|
137
|
+
@type = canonical_type(type)
|
138
|
+
@type_class = canonical_type_class(@type)
|
139
|
+
@name = name
|
140
|
+
end
|
141
|
+
|
142
|
+
def custom?
|
143
|
+
false
|
144
|
+
end
|
145
|
+
|
146
|
+
def array?
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
def structured?
|
151
|
+
false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class ArrayType < BaseType # :nodoc:
|
156
|
+
attr :element_type
|
157
|
+
|
158
|
+
def initialize(spec, element_type, name)
|
159
|
+
super(spec, Array, name)
|
160
|
+
@element_type = element_type
|
161
|
+
end
|
162
|
+
|
163
|
+
def custom?
|
164
|
+
true
|
165
|
+
end
|
166
|
+
|
167
|
+
def array?
|
168
|
+
true
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class StructuredType < BaseType # :nodoc:
|
173
|
+
def each_member
|
174
|
+
if @type_class.respond_to?(:members)
|
175
|
+
@type_class.members.each do |name, type|
|
176
|
+
yield name, type
|
177
|
+
end
|
178
|
+
elsif @type_class.respond_to?(:columns)
|
179
|
+
i = -1
|
180
|
+
@type_class.columns.each do |column|
|
181
|
+
yield column.name, canonical_signature_entry(column.klass, i += 1)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def custom?
|
187
|
+
true
|
188
|
+
end
|
189
|
+
|
190
|
+
def structured?
|
191
|
+
true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|