aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
blob: 87154f87e18ed863cb4158fcdf409a8e39814105 (plain) (blame)
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
module WS
  module Marshaling
    class XmlRpcError < WSError
    end

    class XmlRpcMarshaler < AbstractMarshaler
      def initialize
        @caster = BaseTypeCaster.new
        @spec2binding = {}
      end

      def marshal(param)
        transform_outbound(param)
      end

      def unmarshal(obj)
        obj.param.value = transform_inbound(obj.param)
        obj.param
      end

      def typed_unmarshal(obj, spec)
        param = obj.param
        param.info.data = register_type(spec)
        param.value = transform_inbound(param)
        param
      end

      def register_type(spec)
        if @spec2binding.has_key?(spec)
          return @spec2binding[spec]
        end

        klass = BaseTypes.canonical_param_type_class(spec)
        type_binding = nil
        if klass.is_a?(Array)
          type_binding = XmlRpcArrayBinding.new(klass[0])
        else
          type_binding = XmlRpcBinding.new(klass)
        end

        @spec2binding[spec] = type_binding
      end

      def transform_outbound(param)
        binding = param.info.data
        case binding
        when XmlRpcArrayBinding
          param.value.map{|x| cast_outbound(x, binding.element_klass)}
        when XmlRpcBinding
          cast_outbound(param.value, param.info.type)
        end
      end

      def transform_inbound(param)
        return param.value if param.info.data.nil?
        binding = param.info.data
        param.info.type = binding.klass
        case binding
        when XmlRpcArrayBinding
          param.value.map{|x| cast_inbound(x, binding.element_klass)}
        when XmlRpcBinding
          cast_inbound(param.value, param.info.type)
        end
      end

      def cast_outbound(value, klass)
        if BaseTypes.base_type?(klass)
          @caster.cast(value, klass)
        elsif value.is_a?(Exception)
          XMLRPC::FaultException.new(2, value.message)
        elsif Object.const_defined?('ActiveRecord') && value.is_a?(ActiveRecord::Base)
          value.attributes
        else
          struct = {}
          value.instance_variables.each do |name|
            key = name.sub(/^@/, '')
            struct[key] = value.instance_variable_get(name)
          end
          struct
        end
      end

      def cast_inbound(value, klass)
        if BaseTypes.base_type?(klass)
          value = value.to_time if value.is_a?(XMLRPC::DateTime)
          @caster.cast(value, klass)
        elsif value.is_a?(XMLRPC::FaultException)
          value
        else
          obj = klass.new
          value.each do |name, val|
            obj.send('%s=' % name.to_s, val)
          end
          obj
        end
      end
    end

    class XmlRpcBinding
      attr :klass
      
      def initialize(klass)
        @klass = klass
      end
    end

    class XmlRpcArrayBinding < XmlRpcBinding
      attr :element_klass

      def initialize(element_klass)
        super(Array)
        @element_klass = element_klass
      end
    end
  end
end