aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib/action_web_service/vendor/ws/types.rb
blob: 24b96dc3270ec22c673e3bfa06dd630d1bd097f1 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
require 'time'
require 'date'

module WS
  module BaseTypes
    class << self
      def type_name_to_class(name)
        case canonical_type_name(name)
        when :int
          Integer
        when :string
          String
        when :bool
          TrueClass
        when :float
          Float
        when :time
          Time
        when :date
          Date
        end
      end

      def class_to_type_name(klass)
        if WS.derived_from?(Integer, klass) || WS.derived_from?(Fixnum, klass) || WS.derived_from?(Bignum, klass)
          :int
        elsif klass == String
          :string
        elsif klass == TrueClass || klass == FalseClass
          :bool
        elsif WS.derived_from?(Float, klass) || WS.derived_from?(Precision, klass) || WS.derived_from?(Numeric, klass)
          :float
        elsif klass == Time || klass == DateTime
          :time
        elsif klass == Date
          :date
        else
          raise(TypeError, "#{klass} is not a valid base type")
        end
      end

      def base_type?(klass)
        !(canonical_type_class(klass) rescue nil).nil?
      end

      def canonical_type_class(klass)
        type_name_to_class(class_to_type_name(klass))
      end

      def canonical_param_type_class(spec)
        klass = spec.is_a?(Hash) ? spec.values[0] : spec
        array_element_class = klass.is_a?(Array) ? klass[0] : nil
        klass = array_element_class ? array_element_class : klass
        klass = type_name_to_class(klass) if klass.is_a?(Symbol) || klass.is_a?(String)
        base_class = canonical_type_class(klass) rescue nil
        klass = base_class unless base_class.nil?
        array_element_class ? [klass] : klass
      end

      def canonical_param_type_spec(spec)
        klass = canonical_param_type_class(spec)
        spec.is_a?(Hash) ? {spec.keys[0]=>klass} : klass
      end

      def canonical_type_name(name)
        name = name.to_sym
        case name
          when :int, :integer, :fixnum, :bignum
            :int
          when :string, :base64
            :string
          when :bool, :boolean
            :bool
          when :float, :double
            :float
          when :time, :datetime, :timestamp
            :time
          when :date
            :date
          else
            raise(TypeError, "#{name} is not a valid base type")
        end
      end
    end
  end

  class Param
    attr_accessor :value
    attr_accessor :info

    def initialize(value, info)
      @value = value
      @info = info
    end
  end

  class ParamInfo
    attr_accessor :name
    attr_accessor :type
    attr_accessor :data

    def initialize(name, type, data=nil)
      @name = name
      @type = type
      @data = data
    end

    def self.create(spec, index=nil, data=nil)
      name = spec.is_a?(Hash) ? spec.keys[0].to_s : (index ? "param#{index}" : nil)
      type = BaseTypes.canonical_param_type_class(spec)
      ParamInfo.new(name, type, data)
    end
  end

  class BaseTypeCaster
    def initialize
      @handlers = {}
      install_handlers
    end

    def cast(value, klass)
      type_class = BaseTypes.canonical_type_class(klass)
      return value unless type_class
      @handlers[type_class].call(value, type_class)
    end

    protected
      def install_handlers
        handler = method(:cast_base_type)
        [:int, :string, :bool, :float, :time, :date].each do |name|
          type = BaseTypes.type_name_to_class(name)
          @handlers[type] = handler
        end
        @handlers[Fixnum] = handler
      end

      def cast_base_type(value, type_class)
        desired_class = BaseTypes.canonical_type_class(type_class)
        value_class = BaseTypes.canonical_type_class(value.class)
        return value if desired_class == value_class
        desired_name = BaseTypes.class_to_type_name(desired_class)
        case desired_name
        when :int
          Integer(value)
        when :string
          value.to_s
        when :bool
          return false if value.nil?
          value = value.to_s
          return true if value == 'true'
          return false if value == 'false'
          raise(TypeError, "can't convert #{value} to boolean")
        when :float
          Float(value)
        when :time
          Time.parse(value.to_s)
        when :date
          Date.parse(value.to_s)
        end
      end
  end
end