aboutsummaryrefslogblamecommitdiffstats
path: root/actionwebservice/lib/action_web_service/vendor/ws/types.rb
blob: 88098b5bce5439b74aa07a7be52bb60c99ceb6b1 (plain) (tree)










































































































                                                                                                                      
                                          







































                                                                                   


                                               













                                                               
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, data, index=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?
          int_value = Integer(value) rescue nil
          return true if int_value == 1
          return false if int_value == 0
          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