module Hashme::PropertyCasting
Special property casting for reveiving data from sources without Ruby types, such as query parameters from an API or JSON documents.
Most of this code is stolen from CouchRest Model typecasting, with a few simplifications.
Constants
- CASTABLE_TYPES
Public Instance Methods
Automatically typecast the provided value into an instance of the provided type.
# File lib/hashme/property_casting.rb, line 13 def cast(property, owner, value) return nil if value.nil? type = property.type if value.instance_of?(type) || type == Object value elsif CASTABLE_TYPES.include?(type) send('typecast_to_'+type.to_s.downcase, value) else type.new(value) end end
Protected Instance Methods
Extracts the given args from the hash. If a value does not exist, it uses the value of Time.now.
# File lib/hashme/property_casting.rb, line 175 def extract_time(value) now = Time.now [:year, :month, :day, :hour, :min, :sec].map do |segment| typecast_to_numeric(value.fetch(segment, now.send(segment)), :to_i) end end
Creates a Date instance from a Hash with keys :year, :month, :day
# File lib/hashme/property_casting.rb, line 163 def typecast_hash_to_date(value) Date.new(*extract_time(value)[0, 3].map(&:to_i)) end
Creates a DateTime instance from a Hash with keys :year, :month, :day, :hour, :min, :sec
# File lib/hashme/property_casting.rb, line 158 def typecast_hash_to_datetime(value) DateTime.new(*extract_time(value)) end
Creates a Time instance from a Hash with keys :year, :month, :day, :hour, :min, :sec
# File lib/hashme/property_casting.rb, line 169 def typecast_hash_to_time(value) Time.utc(*extract_time(value)) end
# File lib/hashme/property_casting.rb, line 133 def typecast_iso8601_string_to_time(string) if (string =~ /(\d{4})[\-\/](\d{2})[\-\/](\d{2})[T\s](\d{2}):(\d{2}):(\d{2}(\.\d+)?)(Z| ?([\+\s\-])?(\d{2}):?(\d{2}))?/) # $1 = year # $2 = month # $3 = day # $4 = hours # $5 = minutes # $6 = seconds (with $7 for fraction) # $8 = UTC or Timezone # $9 = time zone direction # $10 = tz difference hours # $11 = tz difference minutes if $8 == 'Z' || $8.to_s.empty? Time.utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_r) else Time.new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_r, "#{$9 == '-' ? '-' : '+'}#{$10}:#{$11}") end else Time.parse(string) end end
Typecast a value to a BigDecimal
# File lib/hashme/property_casting.rb, line 33 def typecast_to_bigdecimal(value) typecast_to_numeric(value, :to_d) end
Typecast a value to a Class
# File lib/hashme/property_casting.rb, line 183 def typecast_to_class(value) value.to_s.constantize rescue NameError nil end
Typecasts an arbitrary value to a Date Handles both Hashes and Date instances.
# File lib/hashme/property_casting.rb, line 101 def typecast_to_date(value) if value.is_a?(Hash) typecast_hash_to_date(value) elsif value.is_a?(Time) # sometimes people think date is time! value.to_date elsif value.to_s =~ /ˆ(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})$/ # Faster than parsing the date Date.new($1.to_i, $2.to_i, $3.to_i) else Date.parse(value) end rescue ArgumentError nil end
Typecasts an arbitrary value to a DateTime. Handles both Hashes and DateTime instances. This is slow!! Use Time instead.
# File lib/hashme/property_casting.rb, line 89 def typecast_to_datetime(value) if value.is_a?(Hash) typecast_hash_to_datetime(value) else DateTime.parse(value.to_s) end rescue ArgumentError nil end
Typecast a value to a Float
# File lib/hashme/property_casting.rb, line 38 def typecast_to_float(value) typecast_to_numeric(value, :to_f) end
Typecast a value to an Integer
# File lib/hashme/property_casting.rb, line 28 def typecast_to_integer(value) typecast_to_numeric(value, :to_i) end
Convert some kind of object to a number that of the type provided.
When a string is provided, It'll attempt to filter out region specific details such as commas instead of points for decimal places, text units, and anything else that is not a number and a human could make out.
Esentially, the aim is to provide some kind of sanitary conversion from values in incoming http forms.
If what we get makes no sense at all, nil it.
# File lib/hashme/property_casting.rb, line 54 def typecast_to_numeric(value, method) if value.is_a?(String) value = value.strip.gsub(/,/, '.').gsub(/[^\d\-\.]/, '').gsub(/\.(?!\d*\Z)/, '') value.empty? ? nil : value.send(method) elsif value.respond_to?(method) value.send(method) else nil end end
Typecast a value to a String
# File lib/hashme/property_casting.rb, line 66 def typecast_to_string(value) value.to_s end
# File lib/hashme/property_casting.rb, line 70 def typecast_to_symbol(value) value.is_a?(Symbol) || !value.to_s.empty? ? value.to_sym : nil end
Typecasts an arbitrary value to a Time Handles both Hashes and Time instances.
# File lib/hashme/property_casting.rb, line 118 def typecast_to_time(value) case value when Float # JSON oj already parses Time, FTW. Time.at(value).utc when Hash typecast_hash_to_time(value) else typecast_iso8601_string_to_time(value.to_s) end rescue ArgumentError nil rescue TypeError nil end
Typecast a value to a true or false
# File lib/hashme/property_casting.rb, line 75 def typecast_to_trueclass(value) if value.kind_of?(Integer) return true if value == 1 return false if value == 0 elsif value.respond_to?(:to_s) return true if %w[ true 1 t ].include?(value.to_s.downcase) return false if %w[ false 0 f ].include?(value.to_s.downcase) end nil end