actionwebservice 0.5.0 → 0.6.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 → CHANGELOG} +20 -0
- data/README +45 -1
- data/Rakefile +12 -10
- data/TODO +8 -9
- data/lib/action_web_service.rb +10 -6
- data/lib/action_web_service/api.rb +1 -2
- data/lib/action_web_service/api/{abstract.rb → base.rb} +14 -71
- data/lib/action_web_service/base.rb +0 -3
- data/lib/action_web_service/client/base.rb +1 -12
- data/lib/action_web_service/client/soap_client.rb +49 -17
- data/lib/action_web_service/client/xmlrpc_client.rb +20 -15
- data/lib/action_web_service/container.rb +3 -85
- data/lib/action_web_service/{api/action_controller.rb → container/action_controller_container.rb} +2 -2
- data/lib/action_web_service/container/delegated_container.rb +87 -0
- data/lib/action_web_service/container/direct_container.rb +70 -0
- data/lib/action_web_service/dispatcher/abstract.rb +100 -102
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +199 -137
- data/lib/action_web_service/protocol.rb +1 -1
- data/lib/action_web_service/protocol/abstract.rb +14 -112
- data/lib/action_web_service/protocol/discovery.rb +37 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +32 -458
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +29 -149
- data/lib/action_web_service/struct.rb +2 -5
- data/lib/action_web_service/test_invoke.rb +130 -0
- data/lib/action_web_service/vendor/ws.rb +4 -0
- data/lib/action_web_service/vendor/ws/common.rb +8 -0
- data/lib/action_web_service/vendor/ws/encoding.rb +3 -0
- data/lib/action_web_service/vendor/ws/encoding/abstract.rb +26 -0
- data/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb +90 -0
- data/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb +53 -0
- data/lib/action_web_service/vendor/ws/marshaling.rb +3 -0
- data/lib/action_web_service/vendor/ws/marshaling/abstract.rb +17 -0
- data/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb +277 -0
- data/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb +116 -0
- data/lib/action_web_service/vendor/ws/types.rb +162 -0
- data/test/abstract_client.rb +8 -11
- data/test/abstract_dispatcher.rb +370 -0
- data/test/abstract_unit.rb +1 -0
- data/test/api_test.rb +18 -1
- data/test/apis/auto_load_api.rb +3 -0
- data/test/apis/broken_auto_load_api.rb +2 -0
- data/test/client_soap_test.rb +16 -3
- data/test/client_xmlrpc_test.rb +16 -4
- data/test/container_test.rb +28 -8
- data/test/dispatcher_action_controller_soap_test.rb +106 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +44 -0
- data/test/gencov +1 -1
- data/test/invocation_test.rb +39 -3
- data/test/run +4 -4
- data/test/test_invoke_test.rb +77 -0
- data/test/ws/abstract_encoding.rb +68 -0
- data/test/ws/abstract_unit.rb +13 -0
- data/test/ws/gencov +3 -0
- data/test/ws/run +5 -0
- data/test/ws/soap_marshaling_test.rb +91 -0
- data/test/ws/soap_rpc_encoding_test.rb +47 -0
- data/test/ws/types_test.rb +41 -0
- data/test/ws/xmlrpc_encoding_test.rb +34 -0
- metadata +48 -19
- data/lib/action_web_service/protocol/registry.rb +0 -55
- data/lib/action_web_service/support/signature.rb +0 -100
- data/test/abstract_soap.rb +0 -58
- data/test/dispatcher_action_controller_test.rb +0 -186
- data/test/protocol_registry_test.rb +0 -53
- data/test/protocol_soap_test.rb +0 -252
- data/test/protocol_xmlrpc_test.rb +0 -147
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'builder/xmlmarkup'
|
3
|
+
|
1
4
|
module ActionWebService # :nodoc:
|
2
5
|
module Dispatcher # :nodoc:
|
3
6
|
module ActionController # :nodoc:
|
@@ -7,106 +10,123 @@ module ActionWebService # :nodoc:
|
|
7
10
|
class << self
|
8
11
|
alias_method :inherited_without_action_controller, :inherited
|
9
12
|
end
|
10
|
-
alias_method :
|
11
|
-
alias_method :after_direct_invoke_without_action_controller, :after_direct_invoke
|
13
|
+
alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
|
12
14
|
end
|
13
15
|
base.add_web_service_api_callback do |klass, api|
|
14
16
|
if klass.web_service_dispatching_mode == :direct
|
15
|
-
klass.class_eval
|
16
|
-
def api
|
17
|
-
controller_dispatch_web_service_request
|
18
|
-
end
|
19
|
-
EOS
|
17
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
20
18
|
end
|
21
19
|
end
|
22
20
|
base.add_web_service_definition_callback do |klass, name, info|
|
23
21
|
if klass.web_service_dispatching_mode == :delegated
|
24
|
-
klass.class_eval
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
EOS
|
22
|
+
klass.class_eval "def #{name}; dispatch_web_service_request; end"
|
23
|
+
elsif klass.web_service_dispatching_mode == :layered
|
24
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
29
25
|
end
|
30
26
|
end
|
31
27
|
base.extend(ClassMethods)
|
32
|
-
base.send(:include, ActionWebService::Dispatcher::ActionController::
|
28
|
+
base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
|
33
29
|
end
|
34
30
|
|
35
31
|
module ClassMethods # :nodoc:
|
36
32
|
def inherited(child)
|
37
33
|
inherited_without_action_controller(child)
|
38
|
-
child.send(:include, ActionWebService::Dispatcher::ActionController::
|
34
|
+
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
42
|
-
module
|
38
|
+
module InstanceMethods # :nodoc:
|
43
39
|
private
|
44
|
-
def
|
45
|
-
request
|
46
|
-
if
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
40
|
+
def dispatch_web_service_request
|
41
|
+
request = discover_web_service_request(@request)
|
42
|
+
if request
|
43
|
+
log_request(request, @request.raw_post)
|
44
|
+
response = nil
|
45
|
+
exception = nil
|
46
|
+
bm = Benchmark.measure do
|
47
|
+
begin
|
48
|
+
response = invoke_web_service_request(request)
|
49
|
+
rescue Exception => e
|
50
|
+
exception = e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
if exception
|
54
|
+
log_error(exception) unless logger.nil?
|
55
|
+
send_web_service_error_response(request, exception)
|
56
|
+
else
|
57
|
+
send_web_service_response(response, bm.real)
|
56
58
|
end
|
57
59
|
else
|
58
|
-
|
59
|
-
|
60
|
+
exception = DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
61
|
+
send_web_service_error_response(request, exception)
|
60
62
|
end
|
63
|
+
rescue Exception => e
|
64
|
+
log_error(e) unless logger.nil?
|
65
|
+
send_web_service_error_response(request, e)
|
61
66
|
end
|
62
67
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
def send_web_service_response(response, elapsed=nil)
|
69
|
+
log_response(response, elapsed)
|
70
|
+
options = { :type => response.content_type, :disposition => 'inline' }
|
71
|
+
send_data(response.body, options)
|
72
|
+
end
|
73
|
+
|
74
|
+
def send_web_service_error_response(request, exception)
|
75
|
+
if request
|
76
|
+
unless self.class.web_service_exception_reporting
|
77
|
+
exception = DispatcherError.new("Internal server error (exception raised)")
|
78
|
+
end
|
79
|
+
response = request.protocol.marshal_response(request.method_name, exception, exception.class)
|
80
|
+
send_web_service_response(response)
|
81
|
+
else
|
82
|
+
if self.class.web_service_exception_reporting
|
83
|
+
message = exception.message
|
84
|
+
else
|
85
|
+
message = "Exception raised"
|
74
86
|
end
|
87
|
+
render_text("Internal protocol error: #{message}", "500 #{message}")
|
75
88
|
end
|
76
|
-
@params['action'] = request.method_name.to_s
|
77
|
-
@session ||= {}
|
78
|
-
@assigns ||= {}
|
79
|
-
return nil if before_action == false
|
80
|
-
true
|
81
89
|
end
|
82
90
|
|
83
|
-
def
|
84
|
-
|
91
|
+
def web_service_direct_invoke(invocation)
|
92
|
+
@params ||= {}
|
93
|
+
invocation.method_named_params.each do |name, value|
|
94
|
+
@params[name] = value
|
95
|
+
end
|
96
|
+
@session ||= {}
|
97
|
+
@assigns ||= {}
|
98
|
+
@params['action'] = invocation.api_method_name.to_s
|
99
|
+
if before_action == false
|
100
|
+
raise(DispatcherError, "Method filtered")
|
101
|
+
end
|
102
|
+
return_value = web_service_direct_invoke_without_controller(invocation)
|
85
103
|
after_action
|
104
|
+
return_value
|
86
105
|
end
|
87
106
|
|
88
|
-
def log_request(request)
|
89
|
-
unless logger.nil?
|
90
|
-
|
91
|
-
|
92
|
-
|
107
|
+
def log_request(request, body)
|
108
|
+
unless logger.nil?
|
109
|
+
name = request.method_name
|
110
|
+
params = request.method_params.map{|x| "#{x.info.name}=>#{x.value.inspect}"}
|
111
|
+
service = request.service_name
|
112
|
+
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
|
113
|
+
logger.debug(indent(body))
|
93
114
|
end
|
94
115
|
end
|
95
116
|
|
96
|
-
def log_response(response, elapsed)
|
97
|
-
unless logger.nil?
|
98
|
-
logger.debug("\nWeb Service Response (%f):" % elapsed)
|
99
|
-
|
100
|
-
logger.debug(indented)
|
117
|
+
def log_response(response, elapsed=nil)
|
118
|
+
unless logger.nil?
|
119
|
+
logger.debug("\nWeb Service Response" + (elapsed ? " (%f):" % elapsed : ":"))
|
120
|
+
logger.debug(indent(response.body))
|
101
121
|
end
|
102
122
|
end
|
103
123
|
|
104
|
-
|
105
|
-
|
124
|
+
def indent(body)
|
125
|
+
body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
106
126
|
end
|
107
127
|
end
|
108
128
|
|
109
|
-
module
|
129
|
+
module WsdlAction # :nodoc:
|
110
130
|
XsdNs = 'http://www.w3.org/2001/XMLSchema'
|
111
131
|
WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
|
112
132
|
SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
|
@@ -117,40 +137,53 @@ module ActionWebService # :nodoc:
|
|
117
137
|
case @request.method
|
118
138
|
when :get
|
119
139
|
begin
|
120
|
-
|
121
|
-
|
122
|
-
soap_action_base = "/#{controller_name}"
|
123
|
-
xml = to_wsdl(self, uri, soap_action_base)
|
124
|
-
send_data(xml, :type => 'text/xml', :disposition => 'inline')
|
140
|
+
options = { :type => 'text/xml', :disposition => 'inline' }
|
141
|
+
send_data(to_wsdl, options)
|
125
142
|
rescue Exception => e
|
126
|
-
log_error
|
127
|
-
render_text('', "500 #{e.message}")
|
143
|
+
log_error(e) unless logger.nil?
|
128
144
|
end
|
129
145
|
when :post
|
130
|
-
render_text('',
|
146
|
+
render_text('POST not supported', '500 POST not supported')
|
131
147
|
end
|
132
148
|
end
|
133
149
|
|
134
150
|
private
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
151
|
+
def base_uri
|
152
|
+
host = @request ? (@request.env['HTTP_HOST'] || @request.env['SERVER_NAME']) : 'localhost'
|
153
|
+
'http://%s/%s/' % [host, controller_name]
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_wsdl
|
157
|
+
xml = ''
|
158
|
+
dispatching_mode = web_service_dispatching_mode
|
159
|
+
global_service_name = wsdl_service_name
|
160
|
+
namespace = 'urn:ActionWebService'
|
161
|
+
soap_action_base = "/#{controller_name}"
|
162
|
+
|
163
|
+
marshaler = WS::Marshaling::SoapMarshaler.new(namespace)
|
164
|
+
apis = {}
|
165
|
+
case dispatching_mode
|
166
|
+
when :direct
|
167
|
+
api = self.class.web_service_api
|
168
|
+
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
169
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
170
|
+
when :delegated
|
171
|
+
self.class.web_services.each do |web_service_name, info|
|
172
|
+
service = web_service_object(web_service_name)
|
173
|
+
api = service.class.web_service_api
|
174
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
custom_types = []
|
178
|
+
apis.values.each do |api, bindings|
|
179
|
+
bindings.each do |b|
|
180
|
+
custom_types << b
|
181
|
+
end
|
146
182
|
end
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2)
|
183
|
+
|
184
|
+
xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
|
151
185
|
xm.instruct!
|
152
|
-
|
153
|
-
xm.definitions('name' => wsdl_service_name,
|
186
|
+
xm.definitions('name' => wsdl_service_name,
|
154
187
|
'targetNamespace' => namespace,
|
155
188
|
'xmlns:typens' => namespace,
|
156
189
|
'xmlns:xsd' => XsdNs,
|
@@ -158,95 +191,96 @@ module ActionWebService # :nodoc:
|
|
158
191
|
'xmlns:soapenc' => SoapEncodingNs,
|
159
192
|
'xmlns:wsdl' => WsdlNs,
|
160
193
|
'xmlns' => WsdlNs) do
|
161
|
-
|
162
|
-
# Custom type XSD generation
|
194
|
+
# Generate XSD
|
163
195
|
if custom_types.size > 0
|
164
196
|
xm.types do
|
165
197
|
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
166
|
-
custom_types.each do |
|
198
|
+
custom_types.each do |binding|
|
167
199
|
case
|
168
|
-
when
|
169
|
-
xm.xsd(:complexType, 'name' =>
|
200
|
+
when binding.is_typed_array?
|
201
|
+
xm.xsd(:complexType, 'name' => binding.type_name) do
|
170
202
|
xm.xsd(:complexContent) do
|
171
203
|
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
172
204
|
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
173
|
-
'wsdl:arrayType' =>
|
205
|
+
'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
|
174
206
|
end
|
175
207
|
end
|
176
208
|
end
|
177
|
-
when
|
178
|
-
xm.xsd(:complexType, 'name' =>
|
209
|
+
when binding.is_typed_struct?
|
210
|
+
xm.xsd(:complexType, 'name' => binding.type_name) do
|
179
211
|
xm.xsd(:all) do
|
180
|
-
|
181
|
-
|
212
|
+
binding.each_member do |name, spec|
|
213
|
+
b = marshaler.register_type(spec)
|
214
|
+
xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
|
182
215
|
end
|
183
216
|
end
|
184
217
|
end
|
185
|
-
else
|
186
|
-
raise(WsdlError, "unsupported mapping type #{mapping.class.name}")
|
187
218
|
end
|
188
219
|
end
|
189
220
|
end
|
190
221
|
end
|
191
222
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
api_methods.each do |
|
223
|
+
|
224
|
+
# APIs
|
225
|
+
apis.each do |api_name, values|
|
226
|
+
api = values[0]
|
227
|
+
api.api_methods.each do |name, info|
|
197
228
|
gen = lambda do |msg_name, direction|
|
198
229
|
xm.message('name' => msg_name) do
|
199
230
|
sym = nil
|
200
231
|
if direction == :out
|
201
|
-
|
202
|
-
|
232
|
+
returns = info[:returns]
|
233
|
+
if returns
|
234
|
+
binding = marshaler.register_type(returns[0])
|
235
|
+
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
203
236
|
end
|
204
237
|
else
|
205
|
-
|
238
|
+
expects = info[:expects]
|
206
239
|
i = 1
|
207
|
-
|
208
|
-
if
|
209
|
-
param_name =
|
210
|
-
|
240
|
+
expects.each do |type|
|
241
|
+
if type.is_a?(Hash)
|
242
|
+
param_name = type.keys.shift
|
243
|
+
type = type.values.shift
|
211
244
|
else
|
212
245
|
param_name = "param#{i}"
|
213
246
|
end
|
214
|
-
|
247
|
+
binding = marshaler.register_type(type)
|
248
|
+
xm.part('name' => param_name, 'type' => binding.qualified_type_name('typens'))
|
215
249
|
i += 1
|
216
|
-
end if
|
250
|
+
end if expects
|
217
251
|
end
|
218
252
|
end
|
219
253
|
end
|
220
|
-
public_name =
|
254
|
+
public_name = api.public_api_method_name(name)
|
221
255
|
gen.call(public_name, :in)
|
222
256
|
gen.call("#{public_name}Response", :out)
|
223
257
|
end
|
224
|
-
|
225
|
-
#
|
226
|
-
port_name = port_name_for(
|
258
|
+
|
259
|
+
# Port
|
260
|
+
port_name = port_name_for(global_service_name, api_name)
|
227
261
|
xm.portType('name' => port_name) do
|
228
|
-
api_methods.each do |
|
229
|
-
public_name =
|
262
|
+
api.api_methods.each do |name, info|
|
263
|
+
public_name = api.public_api_method_name(name)
|
230
264
|
xm.operation('name' => public_name) do
|
231
265
|
xm.input('message' => "typens:#{public_name}")
|
232
266
|
xm.output('message' => "typens:#{public_name}Response")
|
233
267
|
end
|
234
268
|
end
|
235
269
|
end
|
236
|
-
|
237
|
-
# Bind
|
238
|
-
binding_name = binding_name_for(
|
270
|
+
|
271
|
+
# Bind it
|
272
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
239
273
|
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
240
274
|
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
241
|
-
api_methods.each do |
|
242
|
-
public_name =
|
275
|
+
api.api_methods.each do |name, info|
|
276
|
+
public_name = api.public_api_method_name(name)
|
243
277
|
xm.operation('name' => public_name) do
|
244
278
|
case web_service_dispatching_mode
|
245
|
-
when :direct
|
279
|
+
when :direct, :layered
|
246
280
|
soap_action = soap_action_base + "/api/" + public_name
|
247
281
|
when :delegated
|
248
282
|
soap_action = soap_action_base \
|
249
|
-
+ "/" +
|
283
|
+
+ "/" + api_name.to_s \
|
250
284
|
+ "/" + public_name
|
251
285
|
end
|
252
286
|
xm.soap(:operation, 'soapAction' => soap_action)
|
@@ -266,34 +300,62 @@ module ActionWebService # :nodoc:
|
|
266
300
|
end
|
267
301
|
end
|
268
302
|
end
|
269
|
-
|
270
|
-
# Define
|
271
|
-
xm.service('name' => "#{
|
272
|
-
|
273
|
-
port_name = port_name_for(
|
274
|
-
binding_name = binding_name_for(
|
303
|
+
|
304
|
+
# Define it
|
305
|
+
xm.service('name' => "#{global_service_name}Service") do
|
306
|
+
apis.each do |api_name, values|
|
307
|
+
port_name = port_name_for(global_service_name, api_name)
|
308
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
275
309
|
case web_service_dispatching_mode
|
276
310
|
when :direct
|
277
311
|
binding_target = 'api'
|
278
312
|
when :delegated
|
279
|
-
binding_target =
|
313
|
+
binding_target = api_name.to_s
|
280
314
|
end
|
281
315
|
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
282
|
-
xm.soap(:address, 'location' => "#{
|
316
|
+
xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
|
283
317
|
end
|
284
318
|
end
|
285
319
|
end
|
286
320
|
end
|
287
321
|
end
|
288
322
|
|
289
|
-
def port_name_for(
|
290
|
-
"#{
|
323
|
+
def port_name_for(global_service, service)
|
324
|
+
"#{global_service}#{service.to_s.camelize}Port"
|
291
325
|
end
|
292
326
|
|
293
|
-
def binding_name_for(
|
294
|
-
"#{
|
327
|
+
def binding_name_for(global_service, service)
|
328
|
+
"#{global_service}#{service.to_s.camelize}Binding"
|
295
329
|
end
|
296
|
-
|
330
|
+
|
331
|
+
def register_api(api, marshaler)
|
332
|
+
bindings = {}
|
333
|
+
traverse_custom_types(api, marshaler) do |binding|
|
334
|
+
bindings[binding] = nil unless bindings.has_key?(binding.type_class)
|
335
|
+
end
|
336
|
+
bindings.keys
|
337
|
+
end
|
338
|
+
|
339
|
+
def traverse_custom_types(api, marshaler, &block)
|
340
|
+
api.api_methods.each do |name, info|
|
341
|
+
expects, returns = info[:expects], info[:returns]
|
342
|
+
expects.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if expects
|
343
|
+
returns.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if returns
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def traverse_custom_type_spec(marshaler, spec, &block)
|
348
|
+
binding = marshaler.register_type(spec)
|
349
|
+
if binding.is_typed_struct?
|
350
|
+
binding.each_member do |name, member_spec|
|
351
|
+
traverse_custom_type_spec(marshaler, member_spec, &block)
|
352
|
+
end
|
353
|
+
elsif binding.is_typed_array?
|
354
|
+
traverse_custom_type_spec(marshaler, binding.element_binding.type_class, &block)
|
355
|
+
end
|
356
|
+
yield binding
|
357
|
+
end
|
358
|
+
end
|
297
359
|
end
|
298
360
|
end
|
299
361
|
end
|