lib/countries/country.rb



module ISO3166; end

class ISO3166::Country
  Data = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'data', 'countries.yaml')) || {}
  Names = Data.map {|k,v| [v['name'],k]}.sort
  NameIndex = Hash[*Names.flatten]

  AttrReaders = [
    :number,
    :alpha2,
    :alpha3,
    :currency,
    :name,
    :names,
    :latitude,
    :longitude,
    :region,
    :subregion,
    :country_code,
    :national_destination_code_lengths,
    :national_number_lengths,
    :international_prefix,
    :national_prefix,
    :address_format
  ]

  AttrReaders.each do |meth|
    define_method meth do
      @data[meth.to_s]
    end
  end

  attr_reader :data

  def initialize(country_data)
    @data = country_data.is_a?(Hash) ? country_data : Data[country_data]
  end
  
  def valid?
    !!@data
  end

  def currency
    ISO4217::Currency.from_code(@data['currency'])
  end

  def subdivisions
    @subdivisions ||= subdivisions? ? YAML.load_file(File.join(File.dirname(__FILE__), '..', 'data', 'subdivisions', "#{alpha2}.yaml")) : {}
  end
  
  alias :states :subdivisions
  
  def subdivisions?
    File.exist?(File.join(File.dirname(__FILE__), '..', 'data', 'subdivisions', "#{alpha2}.yaml"))
  end
  
  class << self
    def all
      Data.map { |country,data| [data['name'],country] }
    end
    
    alias :countries :all

    def search(query)
      country = self.new(query.to_s.upcase)
      country.valid? ? country : false
    end

    def [](query)
      self.search(query)
    end
    
    def method_missing(*m)
      if m.first.to_s.match /^find_(country_)?by_(.+)/
        country = self.find_all_by($~[2].downcase, m[1].to_s.downcase).first
        $~[1].nil? ? country : self.new(country.last) if country
      elsif m.first.to_s.match /^find_all_(countries_)?by_(.+)/
        self.find_all_by($~[2].downcase, m[1].to_s.downcase).inject([]) do |list, c|
          list << ($~[1].nil? ? c : self.new(c.last)) if c
          list
        end
      else
        super
      end
    end
    
    def find_all_by(attribute, val)
      raise "Invalid attribute name '#{attribute}'" unless AttrReaders.include?(attribute.to_sym)
      attribute = ['name', 'names'] if attribute == 'name'
      Data.select do |k,v|
        Array(attribute).map do |attr|
          if v[attr].kind_of?(Enumerable)
            v[attr].map{ |n| n.downcase }.include?(val)
          else
            v[attr] && v[attr].downcase == val
          end
        end.uniq.include?(true) 
      end
    end
  end
end