sealink/quicktravel_client

View on GitHub
lib/quick_travel/init_from_hash.rb

Summary

Maintainability
A
1 hr
Test Coverage
module QuickTravel
  module InitFromHash
    def initialize(hash = {})
      return nil if hash.blank?

      define_readers(hash.keys)
      Parser.new(hash).parsed_attributes.each do |attr, val|
        # set datamember of the object using hash key and value
        if respond_to?("#{attr}=")
          send("#{attr}=", val)
        else
          instance_variable_set("@#{attr}".to_sym, val)
        end
      end
    end

    def define_readers(keys)
      keys.each do |key|
        next if respond_to?(key)
        define_singleton_method(key) { instance_variable_get("@#{key}") }
        if key.to_s.ends_with? '_cents'
          name = key.to_s.gsub(/_in_cents$/, '')
          define_singleton_method(name) {
            cents = instance_variable_get("@#{key}")
            return nil unless cents
            Money.new(cents)
          }
        end
      end
    end

    def to_hash
      instance_values
    end
  end

  class Parser
    def initialize(attributes)
      @attributes = attributes
    end

    def attributes
      @attributes ||= {}
    end

    def parsed_attributes
      @parsed_attributes ||= parse_attributes
    end

    private

    def parse_attributes
      attributes.map.with_object({}) do |(attribute, value), hash|
        hash[attribute] = parse(attribute, value)
      end
    end

    def parse(attribute, value)
      return nil if value.nil?
      return convert(value, :to_date) if attribute.to_s.ends_with?('_date')
      return convert(value, :to_date) if attribute.to_s.ends_with?('_on')
      # to_datetime as it converts to app time zone, to_time converts to system time zone
      return convert(value, :to_datetime) if attribute.to_s.ends_with?('_time')
      return convert(value, :to_datetime) if attribute.to_s.ends_with?('_at')
      value
    end

    def convert(value, conversion_method)
      convertable_value = value.is_a?(Hash) ? value['_value'] : value
      convertable_value.try(conversion_method) || convertable_value
    end
  end
end