1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
require 'time'
require 'date'
require 'xmlrpc/datetime'
module ActionWebService # :nodoc:
module Casting # :nodoc:
class CastingError < ActionWebServiceError # :nodoc:
end
# Performs casting of arbitrary values into the correct types for the signature
class BaseCaster # :nodoc:
def initialize(api_method)
@api_method = api_method
end
# Coerces the parameters in +params+ (an Enumerable) into the types
# this method expects
def cast_expects(params)
self.class.cast_expects(@api_method, params)
end
# Coerces the given +return_value+ into the type returned by this
# method
def cast_returns(return_value)
self.class.cast_returns(@api_method, return_value)
end
class << self
include ActionWebService::SignatureTypes
def cast_expects(api_method, params) # :nodoc:
return [] if api_method.expects.nil?
api_method.expects.zip(params).map{ |type, param| cast(param, type) }
end
def cast_returns(api_method, return_value) # :nodoc:
return nil if api_method.returns.nil?
cast(return_value, api_method.returns[0])
end
def cast(value, signature_type) # :nodoc:
return value if signature_type.nil? # signature.length != params.length
return nil if value.nil?
# XMLRPC protocol doesn't support nil values. It uses false instead.
# It should never happen for SOAP.
if signature_type.structured? && value.equal?(false)
return nil
end
unless signature_type.array? || signature_type.structured?
return value if canonical_type(value.class) == signature_type.type
end
if signature_type.array?
unless value.respond_to?(:entries) && !value.is_a?(String)
raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
end
value.entries.map do |entry|
cast(entry, signature_type.element_type)
end
elsif signature_type.structured?
cast_to_structured_type(value, signature_type)
elsif !signature_type.custom?
cast_base_type(value, signature_type)
end
end
def cast_base_type(value, signature_type) # :nodoc:
# This is a work-around for the fact that XML-RPC special-cases DateTime values into its own DateTime type
# in order to support iso8601 dates. This doesn't work too well for us, so we'll convert it into a Time,
# with the caveat that we won't be able to handle pre-1970 dates that are sent to us.
#
# See http://dev.rubyonrails.com/ticket/2516
value = value.to_time if value.is_a?(XMLRPC::DateTime)
case signature_type.type
when :int
Integer(value)
when :string
value.to_s
when :base64
if value.is_a?(ActionWebService::Base64)
value
else
ActionWebService::Base64.new(value.to_s)
end
when :bool
return false if value.nil?
return value if value == true || value == false
case value.to_s.downcase
when '1', 'true', 'y', 'yes'
true
when '0', 'false', 'n', 'no'
false
else
raise CastingError, "Don't know how to cast #{value.class} into Boolean"
end
when :float
Float(value)
when :decimal
BigDecimal(value.to_s)
when :time
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
value.kind_of?(Time) ? value : Time.parse(value.to_s)
when :date
value = "%s/%s/%s" % value.values_at(*%w[2 3 1]) if value.kind_of?(Hash)
value.kind_of?(Date) ? value : Date.parse(value.to_s)
when :datetime
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s)
end
end
def cast_to_structured_type(value, signature_type) # :nodoc:
obj = nil
obj = value if canonical_type(value.class) == canonical_type(signature_type.type)
obj ||= signature_type.type_class.new
if value.respond_to?(:each_pair)
klass = signature_type.type_class
value.each_pair do |name, val|
type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
val = cast(val, type) if type
# See http://dev.rubyonrails.com/ticket/3567
val = val.to_time if val.is_a?(XMLRPC::DateTime)
obj.__send__("#{name}=", val) if obj.respond_to?(name)
end
elsif value.respond_to?(:attributes)
signature_type.each_member do |name, type|
val = value.__send__(name)
obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
end
else
raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
end
obj
end
end
end
end
end
|