module Uncharted class Country def subdivisions @subdivisions ||= [] end def territories @territories ||= find_by_type(:territory) end def states @states ||= find_by_type(:state) end def districts @districts ||= find_by_type(:district) end def self.subdivisions Territory.data end def find_by_type(type) subdivisions.select {|t| t.type == type} end end class Territory attr_reader :abbr, :code, :country, :country_code, :type TYPES = [:administration, :atol, :authority, :borough, :canton, :capital, :city, :collectivity, :commune, :council, :county, :department, :dependency, :district, :emirate, :entity, :governorate, :island, :metropolis, :municipality, :parish, :part, :prefecture, :province, :region, :republic, :sector, :state, :territory, :unit, :voivodship, :zone] def initialize(code, name, type) @code = code @name = name @country_code, @abbr = code.split('-') @country = Country.find(@country_code) Territory.data[code] = self @country.subdivisions << self if @country @type = TYPES.detect {|t| /#{t}/ =~ type} || :territory end def name(options = {}) I18n.t("territories.#{@country_code}#{@abbr}", {locale: options[:locale] || I18n.locale}, default: @name) end def names @names ||= Country.split_name(@name) end def to_s @abbr end def inspect "#{@code} - #{@name} (#{@type} - #{@country.inspect})" end def self.find(object) territory = object && object.is_a?(Territory) ? object : data[object] end def self.data @data ||= {} end def self.find_by_name(name) closest = [nil, 0] @data.each do |code, territory| return territory if territory.name == name intersection = territory.names & Country.split_name(name) size = intersection.size return territory if size == territory.names.size closest = [territory, size] if closest.last < size end return closest.first end def self.search(object) find(object) || find_by_name(object) end # Columns are expected in this order # 1. Territory ISO 3166-2 code # 2. Territory name # 3. Territory level name (state, territory, parish, etc) def self.load(path = "data/territories.csv", options = {:encoding => "UTF-8", :locale => 'pt-BR'}) CSV.foreach(path, :encoding => options[:encoding]) do |row| code, name, type = row.collect {|c| c && c.strip} type && type.downcase! # puts "Importing: #{code} :: #{name} :: #{type}" new(code, name, type) if code =~ /^[A-Z]{2}-[A-Z0-9]{1,3}$/ && name && name.size > 0 end #Territory.data.each {|code, t| puts t.inspect} end end end