atlasq 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"978":["eur"],"eur":["eur"],"€":["eur"],"euro":["eur"],"784":["aed"],"aed":["aed"],"د.ا":["aed","jod"],"united":["aed","usd"],"arab":["aed"],"emirates":["aed"],"dirham":["aed","mad"],"971":["afn"],"afn":["afn"],"؋":["afn"],"afghan":["afn"],"afghani":["afn"],"951":["xcd"],"xcd":["xcd"],"$":["xcd","usd","ars","aud","bbd","bmd","bnd","bsd","bzd","cad","nzd","clp","cop","cup","cve","dop","fjd","gyd","hkd","jmd","kyd","lrd","mxn","nad","sbd","sgd","srd","ttd","twd"],"east":["xcd"],"caribbean":["xcd"],"dollar":["xcd","usd","aud","bbd","bmd","bnd","bsd","bzd","cad","nzd","fjd","gyd","hkd","jmd","kyd","lrd","nad","sbd","sgd","srd","ttd","twd"],"008":["all"],"all":["all"],"l":["all","hnl","lsl","mdl"],"albanian":["all"],"lek":["all"],"051":["amd"],"amd":["amd"],"դր.":["amd"],"armenian":["amd"],"dram":["amd"],"973":["aoa"],"aoa":["aoa"],"kz":["aoa"],"angolan":["aoa"],"kwanza":["aoa"],"840":["usd"],"usd":["usd"],"states":["usd"],"032":["ars"],"ars":["ars"],"argentine":["ars"],"peso":["ars","clp","cop","cup","dop","mxn","php","uyu"],"036":["aud"],"aud":["aud"],"australian":["aud"],"533":["awg"],"awg":["awg"],"f":["awg","ang"],"aruban":["awg"],"florin":["awg"],"944":["azn"],"azn":["azn"],"₼":["azn"],"azerbaijani":["azn"],"manat":["azn","tmt"],"977":["bam"],"bam":["bam"],"км":["bam"],"bosnia":["bam"],"and":["bam","std","ttd"],"herzegovina":["bam"],"convertible":["bam"],"mark":["bam"],"052":["bbd"],"bbd":["bbd"],"barbadian":["bbd"],"050":["bdt"],"bdt":["bdt"],"৳":["bdt"],"bangladeshi":["bdt"],"taka":["bdt"],"952":["xof"],"xof":["xof"],"fr":["xof","bif","cdf","gnf","kmf","xpf"],"west":["xof"],"african":["xof","xaf","zar"],"cfa":["xof","xaf"],"franc":["xof","bif","cdf","xaf","chf","djf","gnf","kmf","xpf","rwf"],"975":["bgn"],"bgn":["bgn"],"лв.":["bgn"],"bulgarian":["bgn"],"lev":["bgn"],"048":["bhd"],"bhd":["bhd"],"د.ب":["bhd"],"bahraini":["bhd"],"dinar":["bhd","dzd","iqd","jod","kwd","lyd","rsd","tnd"],"108":["bif"],"bif":["bif"],"burundian":["bif"],"060":["bmd"],"bmd":["bmd"],"bermudian":["bmd"],"096":["bnd"],"bnd":["bnd"],"brunei":["bnd"],"068":["bob"],"bob":["bob"],"bs.":["bob"],"bolivian":["bob"],"boliviano":["bob"],"986":["brl"],"brl":["brl"],"r$":["brl"],"brazilian":["brl"],"real":["brl"],"044":["bsd"],"bsd":["bsd"],"bahamian":["bsd"],"064":["btn"],"btn":["btn"],"nu.":["btn"],"bhutanese":["btn"],"ngultrum":["btn"],"578":["nok"],"nok":["nok"],"kr":["nok","sek"],"norwegian":["nok"],"krone":["nok","dkk"],"072":["bwp"],"bwp":["bwp"],"p":["bwp","mop"],"botswana":["bwp"],"pula":["bwp"],"933":["byn"],"byn":["byn"],"br":["byn","etb"],"belarusian":["byn"],"ruble":["byn","rub"],"084":["bzd"],"bzd":["bzd"],"belize":["bzd"],"124":["cad"],"cad":["cad"],"canadian":["cad"],"976":["cdf"],"cdf":["cdf"],"congolese":["cdf"],"950":["xaf"],"xaf":["xaf"],"central":["xaf"],"756":["chf"],"chf":["chf"],"swiss":["chf"],"554":["nzd"],"nzd":["nzd"],"new":["nzd","ils","pgk","twd"],"zealand":["nzd"],"152":["clp"],"clp":["clp"],"chilean":["clp"],"156":["cny"],"cny":["cny"],"¥":["cny","jpy"],"chinese":["cny"],"renminbi":["cny"],"yuan":["cny"],"170":["cop"],"cop":["cop"],"colombian":["cop"],"188":["crc"],"crc":["crc"],"₡":["crc"],"costa":["crc"],"rican":["crc"],"colon":["crc"],"192":["cup"],"cup":["cup"],"cuban":["cup"],"132":["cve"],"cve":["cve"],"cape":["cve"],"verdean":["cve"],"escudo":["cve"],"532":["ang"],"ang":["ang"],"netherlands":["ang"],"antillean":["ang"],"gulden":["ang"],"203":["czk"],"czk":["czk"],"kc":["czk"],"czech":["czk"],"koruna":["czk"],"262":["djf"],"djf":["djf"],"fdj":["djf"],"djiboutian":["djf"],"208":["dkk"],"dkk":["dkk"],"kr.":["dkk","isk"],"danish":["dkk"],"214":["dop"],"dop":["dop"],"dominican":["dop"],"012":["dzd"],"dzd":["dzd"],"د.ج":["dzd"],"algerian":["dzd"],"818":["egp"],"egp":["egp"],"ج.م":["egp"],"egyptian":["egp"],"pound":["egp","fkp","gbp","gip","lbp","sdg","shp","ssp","syp"],"504":["mad"],"mad":["mad"],"د.م.":["mad"],"moroccan":["mad"],"230":["etb"],"etb":["etb"],"ethiopian":["etb"],"birr":["etb"],"242":["fjd"],"fjd":["fjd"],"fijian":["fjd"],"238":["fkp"],"fkp":["fkp"],"£":["fkp","gbp","gip","sdg","shp","ssp"],"falkland":["fkp"],"826":["gbp"],"gbp":["gbp"],"british":["gbp"],"981":["gel"],"gel":["gel"],"ლ":["gel"],"georgian":["gel"],"lari":["gel"],"936":["ghs"],"ghs":["ghs"],"₵":["ghs"],"ghanaian":["ghs"],"cedi":["ghs"],"292":["gip"],"gip":["gip"],"gibraltar":["gip"],"270":["gmd"],"gmd":["gmd"],"d":["gmd"],"gambian":["gmd"],"dalasi":["gmd"],"324":["gnf"],"gnf":["gnf"],"guinean":["gnf","pgk"],"320":["gtq"],"gtq":["gtq"],"q":["gtq"],"guatemalan":["gtq"],"quetzal":["gtq"],"328":["gyd"],"gyd":["gyd"],"guyanese":["gyd"],"344":["hkd"],"hkd":["hkd"],"hong":["hkd"],"kong":["hkd"],"340":["hnl"],"hnl":["hnl"],"honduran":["hnl"],"lempira":["hnl"],"332":["htg"],"htg":["htg"],"g":["htg"],"haitian":["htg"],"gourde":["htg"],"348":["huf"],"huf":["huf"],"ft":["huf"],"hungarian":["huf"],"forint":["huf"],"360":["idr"],"idr":["idr"],"rp":["idr"],"indonesian":["idr"],"rupiah":["idr"],"376":["ils"],"ils":["ils"],"₪":["ils"],"israeli":["ils"],"sheqel":["ils"],"356":["inr"],"inr":["inr"],"₹":["inr"],"indian":["inr"],"rupee":["inr","lkr","mur","npr","pkr","scr"],"368":["iqd"],"iqd":["iqd"],"ع.د":["iqd"],"iraqi":["iqd"],"364":["irr"],"irr":["irr"],"ریال":["irr","yer"],"iranian":["irr"],"rial":["irr","omr","yer"],"352":["isk"],"isk":["isk"],"icelandic":["isk"],"krona":["isk","sek"],"388":["jmd"],"jmd":["jmd"],"jamaican":["jmd"],"400":["jod"],"jod":["jod"],"jordanian":["jod"],"392":["jpy"],"jpy":["jpy"],"japanese":["jpy"],"yen":["jpy"],"404":["kes"],"kes":["kes"],"ksh":["kes"],"kenyan":["kes"],"shilling":["kes","sos","tzs","ugx"],"417":["kgs"],"kgs":["kgs"],"som":["kgs","uzs"],"kyrgyzstani":["kgs"],"116":["khr"],"khr":["khr"],"៛":["khr"],"cambodian":["khr"],"riel":["khr"],"174":["kmf"],"kmf":["kmf"],"comorian":["kmf"],"408":["kpw"],"kpw":["kpw"],"₩":["kpw","krw"],"north":["kpw"],"korean":["kpw","krw"],"won":["kpw","krw"],"410":["krw"],"krw":["krw"],"south":["krw","ssp","zar"],"414":["kwd"],"kwd":["kwd"],"د.ك":["kwd"],"kuwaiti":["kwd"],"136":["kyd"],"kyd":["kyd"],"cayman":["kyd"],"islands":["kyd","sbd"],"398":["kzt"],"kzt":["kzt"],"₸":["kzt"],"kazakhstani":["kzt"],"tenge":["kzt"],"418":["lak"],"lak":["lak"],"₭":["lak"],"lao":["lak"],"kip":["lak"],"422":["lbp"],"lbp":["lbp"],"ل.ل":["lbp"],"lebanese":["lbp"],"144":["lkr"],"lkr":["lkr"],"Rs":["lkr","mur","pkr","scr"],"sri":["lkr"],"lankan":["lkr"],"430":["lrd"],"lrd":["lrd"],"liberian":["lrd"],"426":["lsl"],"lsl":["lsl"],"lesotho":["lsl"],"loti":["lsl"],"434":["lyd"],"lyd":["lyd"],"ل.د":["lyd"],"libyan":["lyd"],"498":["mdl"],"mdl":["mdl"],"moldovan":["mdl"],"leu":["mdl","ron"],"969":["mga"],"mga":["mga"],"ar":["mga"],"malagasy":["mga"],"ariary":["mga"],"807":["mkd"],"mkd":["mkd"],"ден":["mkd"],"macedonian":["mkd"],"denar":["mkd"],"104":["mmk"],"mmk":["mmk"],"k":["mmk","pgk","zmw"],"myanmar":["mmk"],"kyat":["mmk"],"496":["mnt"],"mnt":["mnt"],"₮":["mnt"],"mongolian":["mnt"],"togrog":["mnt"],"446":["mop"],"mop":["mop"],"macanese":["mop"],"pataca":["mop"],"929":["mru"],"mru":["mru"],"um":["mru"],"mauritanian":["mru"],"ouguiya":["mru"],"480":["mur"],"mur":["mur"],"mauritian":["mur"],"462":["mvr"],"mvr":["mvr"],"maldivian":["mvr"],"rufiyaa":["mvr"],"454":["mwk"],"mwk":["mwk"],"mk":["mwk"],"malawian":["mwk"],"kwacha":["mwk","zmw"],"484":["mxn"],"mxn":["mxn"],"mexican":["mxn"],"458":["myr"],"myr":["myr"],"rm":["myr"],"malaysian":["myr"],"ringgit":["myr"],"943":["mzn"],"mzn":["mzn"],"mtn":["mzn"],"mozambican":["mzn"],"metical":["mzn"],"516":["nad"],"nad":["nad"],"namibian":["nad"],"953":["xpf"],"xpf":["xpf"],"cfp":["xpf"],"566":["ngn"],"ngn":["ngn"],"₦":["ngn"],"nigerian":["ngn"],"naira":["ngn"],"558":["nio"],"nio":["nio"],"c$":["nio"],"nicaraguan":["nio"],"cordoba":["nio"],"524":["npr"],"npr":["npr"],"rs.":["npr"],"nepalese":["npr"],"512":["omr"],"omr":["omr"],"ر.ع.":["omr"],"omani":["omr"],"590":["pab"],"pab":["pab"],"b/.":["pab"],"panamanian":["pab"],"balboa":["pab"],"604":["pen"],"pen":["pen"],"s/":["pen"],"peruvian":["pen"],"sol":["pen"],"598":["pgk"],"pgk":["pgk"],"papua":["pgk"],"kina":["pgk"],"608":["php"],"php":["php"],"₱":["php"],"philippine":["php"],"586":["pkr"],"pkr":["pkr"],"pakistani":["pkr"],"985":["pln"],"pln":["pln"],"zl":["pln"],"polish":["pln"],"zloty":["pln"],"600":["pyg"],"pyg":["pyg"],"₲":["pyg"],"paraguayan":["pyg"],"guarani":["pyg"],"634":["qar"],"qar":["qar"],"ر.ق":["qar"],"qatari":["qar"],"riyal":["qar","sar"],"946":["ron"],"ron":["ron"],"lei":["ron"],"romanian":["ron"],"941":["rsd"],"rsd":["rsd"],"рсд":["rsd"],"serbian":["rsd"],"643":["rub"],"rub":["rub"],"₽":["rub"],"russian":["rub"],"646":["rwf"],"rwf":["rwf"],"frw":["rwf"],"rwandan":["rwf"],"682":["sar"],"sar":["sar"],"ر.س":["sar"],"saudi":["sar"],"090":["sbd"],"sbd":["sbd"],"solomon":["sbd"],"690":["scr"],"scr":["scr"],"seychellois":["scr"],"938":["sdg"],"sdg":["sdg"],"sudanese":["sdg","ssp"],"752":["sek"],"sek":["sek"],"swedish":["sek"],"702":["sgd"],"sgd":["sgd"],"singapore":["sgd"],"654":["shp"],"shp":["shp"],"saint":["shp"],"helenian":["shp"],"694":["sll"],"sll":["sll"],"le":["sll"],"sierra":["sll"],"leonean":["sll"],"leone":["sll"],"706":["sos"],"sos":["sos"],"sh":["sos","tzs"],"somali":["sos"],"968":["srd"],"srd":["srd"],"surinamese":["srd"],"728":["ssp"],"ssp":["ssp"],"678":["std"],"std":["std"],"db":["std"],"sao":["std"],"tome":["std"],"principe":["std"],"dobra":["std"],"760":["syp"],"syp":["syp"],"£s":["syp"],"syrian":["syp"],"748":["szl"],"szl":["szl"],"e":["szl"],"swazi":["szl"],"lilangeni":["szl"],"764":["thb"],"thb":["thb"],"฿":["thb"],"thai":["thb"],"baht":["thb"],"972":["tjs"],"tjs":["tjs"],"ѕм":["tjs"],"tajikistani":["tjs"],"somoni":["tjs"],"934":["tmt"],"tmt":["tmt"],"t":["tmt","wst"],"turkmenistani":["tmt"],"788":["tnd"],"tnd":["tnd"],"د.ت":["tnd"],"tunisian":["tnd"],"776":["top"],"top":["top"],"t$":["top"],"tongan":["top"],"paʻanga":["top"],"949":["try"],"try":["try"],"₺":["try"],"turkish":["try"],"lira":["try"],"780":["ttd"],"ttd":["ttd"],"trinidad":["ttd"],"tobago":["ttd"],"901":["twd"],"twd":["twd"],"taiwan":["twd"],"834":["tzs"],"tzs":["tzs"],"tanzanian":["tzs"],"980":["uah"],"uah":["uah"],"₴":["uah"],"ukrainian":["uah"],"hryvnia":["uah"],"800":["ugx"],"ugx":["ugx"],"ush":["ugx"],"ugandan":["ugx"],"858":["uyu"],"uyu":["uyu"],"$u":["uyu"],"uruguayan":["uyu"],"860":["uzs"],"uzs":["uzs"],"so'm":["uzs"],"uzbekistan":["uzs"],"928":["ves"],"ves":["ves"],"bs":["ves"],"venezuelan":["ves"],"bolivar":["ves"],"soberano":["ves"],"704":["vnd"],"vnd":["vnd"],"₫":["vnd"],"vietnamese":["vnd"],"dong":["vnd"],"548":["vuv"],"vuv":["vuv"],"vt":["vuv"],"vanuatu":["vuv"],"vatu":["vuv"],"882":["wst"],"wst":["wst"],"samoan":["wst"],"tala":["wst"],"886":["yer"],"yer":["yer"],"yemeni":["yer"],"710":["zar"],"zar":["zar"],"r":["zar"],"rand":["zar"],"967":["zmw"],"zmw":["zmw"],"zambian":["zmw"]}
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Atlasq
6
+ module Cache
7
+ CACHE_DIR = File.expand_path("../../cache", __dir__).freeze
8
+ private_constant :CACHE_DIR
9
+
10
+ # @param full_name [String] namespace + file name (ex. "info/countries.json")
11
+ # @return file
12
+ def self.get(full_name)
13
+ @get ||= {}
14
+ @get.fetch(full_name) do
15
+ path = "#{CACHE_DIR}/#{full_name}"
16
+ content = File.read(path)
17
+
18
+ case full_name
19
+ when /\.json$/
20
+ JSON.parse(content)
21
+ else
22
+ content
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -7,11 +7,13 @@ module Atlasq
7
7
  class Any < Base
8
8
  def content
9
9
  search_terms.map do |term|
10
- data = Data.any(term)
11
- empty = data.empty? if data.respond_to?(:empty?)
12
-
13
- if data && !empty
14
- Format.any(data, term)
10
+ if (country = Data.country(term))
11
+ Format.country(country)
12
+ elsif (country_codes = Data.countries_by_region(term))
13
+ region_name = Util::String.titleize(term)
14
+ Format.countries(country_codes, title: "Region: #{region_name}")
15
+ elsif (currencies = Data.countries_by_currencies(term))
16
+ Format.currencies(currencies)
15
17
  else
16
18
  Atlasq.failed!
17
19
  "Unknown search term: #{term}"
@@ -18,7 +18,7 @@ module Atlasq
18
18
 
19
19
  # @param search_terms [Array<String>]
20
20
  def initialize(search_terms)
21
- @search_terms = search_terms
21
+ @search_terms = search_terms.map(&Util::String.method(:normalize))
22
22
  end
23
23
 
24
24
  # @return [String]
@@ -8,14 +8,14 @@ module Atlasq
8
8
  # @return [String]
9
9
  def content
10
10
  if search_terms.empty?
11
- countries = Data.all_countries
12
- Format.countries(countries, title: "All Countries")
11
+ country_codes = Data.all_countries
12
+ Format.countries(country_codes, title: "All Countries")
13
13
  else
14
14
  search_terms.map do |term|
15
- country = Data.country(term)
16
-
17
- if country
18
- Format.country(country, term)
15
+ if (country_code = Data.country(term))
16
+ Format.country(country_code)
17
+ elsif (country_codes = PartialMatch.countries(term)).any?
18
+ Format.countries(country_codes, title: "Countries (Partial Match)")
19
19
  else
20
20
  Atlasq.failed!
21
21
  "Unknown country: #{term}"
@@ -41,6 +41,7 @@ module Atlasq
41
41
  2. alpha3 (ISO3166 standard 3 letter code)
42
42
  3. number (ISO3166 standard 3 digit code)
43
43
  4. name (common, localized, unofficial)
44
+ 5. partial match on name
44
45
 
45
46
  -r/--region
46
47
  : Display all countries by subregion
@@ -58,6 +59,7 @@ module Atlasq
58
59
  1. code (ISO4127 standard 3 letter code)
59
60
  2. name (ISO4127 standard name)
60
61
  3. symbol
62
+ 4. partial match on name
61
63
 
62
64
  -h/--help
63
65
  : Display this page
@@ -11,13 +11,14 @@ module Atlasq
11
11
  Format.currencies(currencies)
12
12
  else
13
13
  search_terms.map do |term|
14
- currencies = Data.currencies(term)
15
-
16
- if currencies.empty?
14
+ if (currencies = Data.countries_by_currencies(term))
15
+ Format.currencies(currencies)
16
+ elsif (currency_codes = PartialMatch.currencies(term)).any?
17
+ currencies = Data.countries_by_currencies(currency_codes)
18
+ Format.currencies(currencies, partial_match: true)
19
+ else
17
20
  Atlasq.failed!
18
21
  "Unknown currency: #{term}"
19
- else
20
- Format.currencies(currencies)
21
22
  end
22
23
  end.join("\n\n")
23
24
  end
@@ -11,10 +11,9 @@ module Atlasq
11
11
  Format.subregions(subregions)
12
12
  else
13
13
  search_terms.map do |term|
14
- region = Data.region(term)
15
-
16
- if region
17
- Format.region(region)
14
+ if (country_codes = Data.countries_by_region(term))
15
+ region_name = Util::String.titleize(term)
16
+ Format.countries(country_codes, title: "Region: #{region_name}")
18
17
  else
19
18
  Atlasq.failed!
20
19
  "Unknown region: #{term}"
@@ -17,6 +17,7 @@ module Atlasq
17
17
  def self.parse(args)
18
18
  command = parse_command(args.first)
19
19
  args.shift unless command.to_s == "Atlasq::Command::Any"
20
+ args.map!(&:strip)
20
21
 
21
22
  Options.new(command: command, args: args).freeze
22
23
  end
data/lib/atlasq/data.rb CHANGED
@@ -1,113 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "countries"
4
- require "iso-639"
5
- require "money"
6
- require "money-heuristics"
7
-
8
- ISO3166.configure do |config|
9
- # Needed to allow us to access the `ISO3166::Country#currency`
10
- # object which ends up being an instance of `Money::Currency`.
11
- config.enable_currency_extension!
12
-
13
- # Needed to allow us to search by localized country name.
14
- config.locales = %i[
15
- af am ar as az be bg bn br bs
16
- ca cs cy da de dz el en eo es
17
- et eu fa fi fo fr ga gl gu he
18
- hi hr hu hy ia id is it ja ka
19
- kk km kn ko ku lt lv mi mk ml
20
- mn mr ms mt nb ne nl nn oc or
21
- pa pl ps pt ro ru rw si sk sl
22
- so sq sr sv sw ta te th ti tk
23
- tl tr tt ug uk ve vi wa wo xh
24
- zh zu
25
- ]
26
- end
27
-
28
3
  module Atlasq
29
4
  module Data
30
- autoload :Currency, "atlasq/data/currency"
31
- autoload :Region, "atlasq/data/region"
32
-
33
- # @param term [String]
34
- # @return [ISO3166::Country, Atlasq::Data::Region, Atlasq::Data::Currency, nil]
35
- def self.any(term)
36
- Data.country(term) ||
37
- Data.region(term) ||
38
- Data.currencies(term)
5
+ # @param term [String] search term
6
+ # @return [String, nil] ISO3166 2 letter country code
7
+ def self.country(term)
8
+ Cache
9
+ .get("search_index/direct_match_country.json")
10
+ .dig(term)
39
11
  end
40
12
 
41
- # @param term [String]
42
- # @return [ISO3166::Country|nil]
43
- def self.country(term)
44
- ISO3166::Country.find_country_by_alpha2(term) ||
45
- ISO3166::Country.find_country_by_alpha3(term) ||
46
- ISO3166::Country.find_country_by_number(term) ||
47
- ISO3166::Country.find_country_by_any_name(term)
13
+ # @param term [String] search term
14
+ # @return [Array<String>, nil] ISO3166 2 letter country codes
15
+ def self.countries_by_region(term)
16
+ Cache
17
+ .get("search_index/countries_by_region.json")
18
+ .dig(term)
48
19
  end
49
20
 
50
- # @return [Array<ISO3166::Country>]
21
+ # @return [Array<String>] ISO3166 2 letter country codes
51
22
  def self.all_countries
52
- @all_countries ||= ISO3166::Country.all
23
+ Cache.get("list/all_countries.json")
53
24
  end
54
25
 
55
- # Region types for querying ISO3166::Country
56
- REGION_TYPES = %i[region subregion world_region continent].freeze
57
-
58
- # @return [Atlasq::Data::Region|nil]
59
- def self.region(term)
60
- REGION_TYPES.each do |region_type|
61
- countries = ISO3166::Country.find_all_by(region_type, term)
62
- next if countries.empty?
26
+ # @return [Array<String>]
27
+ def self.all_subregions
28
+ subregions = Cache.get("list/all_subregions.json")
63
29
 
64
- return Region.new(countries: countries.values, type: region_type)
65
- end
66
- nil
30
+ Cache
31
+ .get("search_index/countries_by_region.json")
32
+ .slice(*subregions)
67
33
  end
68
34
 
69
- # @return [Hash<String, Array<ISO3166::Country>>] Ex. { "Central Asia" => [...], ... }
70
- def self.all_subregions
71
- all_countries
72
- .group_by(&:subregion)
73
- .tap do |subregions|
74
- # Multiple countries do not have a valid subregion so shouldn't be shown.
75
- # (010 | AQ | ATA | Antarctica)
76
- # (074 | BV | BVT | Bouvet Island)
77
- # (334 | HM | HMD | Heard Island and McDonald Islands)
78
- subregions.delete("")
79
- end
80
- end
35
+ # @param terms [String, Array<String>] search terms
36
+ # @return [Hash<String, Array<String>>] ISO4127 3 letter currency code to ISO3166 2 letter country codes
37
+ def self.countries_by_currencies(terms)
38
+ terms = Array(terms)
39
+ currency_codes = Cache
40
+ .get("search_index/direct_match_currency.json")
41
+ .values_at(*terms)
42
+ .compact
81
43
 
82
- # @param term [String]
83
- # @return [Array<Atlasq::Data::Currency>]
84
- def self.currencies(term)
85
- currency_codes = Money::Currency.analyze(term)
86
- currency_codes.filter_map do |currency_code|
87
- countries = ISO3166::Country.find_all_by(:currency_code, currency_code)
88
- next if countries.empty?
44
+ return if currency_codes.empty?
89
45
 
90
- Currency.new(countries: countries.values, currency_code: currency_code)
91
- end
46
+ Cache
47
+ .get("search_index/countries_by_currency.json")
48
+ .slice(*currency_codes)
92
49
  end
93
50
 
94
- # @return [Array<Atlasq::Data::Currency>]
51
+ # @return [Hash<String, Array<String>>] ISO4127 3 letter currency code to ISO3166 2 letter country codes
95
52
  def self.all_currencies
96
- all_countries
97
- .group_by(&:currency_code)
98
- .map do |currency_code, countries|
99
- Currency.new(countries: countries, currency_code: currency_code)
100
- end
101
- end
102
-
103
- # @param number [String] ISO3166-1 numeric country code
104
- # @return [String, nil]
105
- def self.emoji_flag(iso_number)
106
- @emoji_flag ||= all_countries
107
- .to_h { |country| [country.number, country.emoji_flag] }
108
- .freeze
109
-
110
- @emoji_flag[iso_number]
53
+ Cache.get("search_index/countries_by_currency.json")
111
54
  end
112
55
  end
113
56
  end
data/lib/atlasq/format.rb CHANGED
@@ -13,7 +13,7 @@ module Atlasq
13
13
  [
14
14
  "*",
15
15
  "* #{title}",
16
- "*#{" *" * ((title.size / 2) + 2)}"
16
+ "*#{" *" * ((title.size / 2) + 2)}",
17
17
  ].join("\n")
18
18
  end
19
19
 
@@ -56,168 +56,95 @@ module Atlasq
56
56
 
57
57
  [
58
58
  Format.title(title),
59
- *elements
59
+ *elements,
60
60
  ].join("\n")
61
61
  end
62
62
 
63
- # @example
64
- # *
65
- # * Title
66
- # * * * * *
67
- # (attr1 | attr2 | attr3)
68
- # | Info One: 1
69
- # | Info Two: 2
70
- # | Info Three: 3
71
- # |________________________________________
72
- #
63
+ # @param country_codes [Array<String>] ISO3166 2 letter country codes
73
64
  # @param title [String]
74
- # @param attributes [String]
75
- # @param info [Hash<String, String>]
76
- # @return [String]
77
- def self.verbose_template(title:, attributes:, info:)
78
- info_ladder = info.map.with_index do |(name, value), index|
79
- "#{" " * (index + 1)}| #{name}: #{value}"
80
- end
81
- info_ladder << "#{" " * (info_ladder.size + 1)}|#{"_" * 40}"
82
-
83
- [
84
- Format.title(title),
85
- attributes,
86
- *info_ladder
87
- ].join("\n")
88
- end
89
-
90
- # @param value
91
- # @param search_term [String]
92
- # @return [String]
93
- def self.any(value, search_term)
94
- case value
95
- in ISO3166::Country
96
- Format.country(value, search_term)
97
- in Atlasq::Data::Region
98
- Format.region(value)
99
- in [Atlasq::Data::Currency, *]
100
- Format.currencies(value)
101
- else
102
- raise Error, "Unknown format type: #{value.class}"
103
- end
104
- end
105
-
106
- # @param country [ISO3166::Country]
107
- # @param search_term [String]
108
- # @return [String]
109
- def self.country(country, search_term)
110
- Format.verbose_template(
111
- title: "Country: #{country.iso_long_name}",
112
- attributes: Format.one_line_country(country),
113
- info: {
114
- "Search Term" => search_term,
115
- "Languages" => Format.languages(country.languages),
116
- "Nationality" => country.nationality,
117
- "Region" => country.subregion,
118
- "Continent" => country.continent,
119
- "Currency" => "#{country.currency.symbol} #{country.currency.name}"
120
- }.reject do |_, value|
121
- # "countries" like Antarctica can have missing language, nationality,
122
- # and region data so we remove that missing data beforehand.
123
- value.nil? || value.empty?
124
- end.to_h
125
- )
126
- end
127
-
128
- # @example "English / Shona / Ndebele, North; North Ndebele"
129
- # @param language_codes [Array<String>] Ex. ["id"]
130
65
  # @return [String]
131
- def self.languages(language_codes)
132
- language_codes
133
- .take(4) # arbitrary limit to avoid long lines
134
- .map do |lang|
135
- ISO_639.find(lang).english_name
136
- end
137
- .join(" / ")
138
- end
139
-
140
- # @param countries [Array<ISO3166::Country|Hash>]
141
- # @param title [String]
142
- # @return [String]
143
- def self.countries(countries, title:)
66
+ def self.countries(country_codes, title:)
144
67
  Format.brief_template(
145
68
  title: title,
146
- elements: countries.map do |country|
69
+ elements: country_codes.map do |country|
147
70
  Format.one_line_country(country)
148
71
  end
149
72
  )
150
73
  end
151
74
 
152
- # @param country [ISO3166::Country|Hash]
75
+ # @param country_code [String] ISO3166 2 letter country code
153
76
  # @return [String]
154
- def self.one_line_country(country)
155
- case country
156
- when ISO3166::Country
157
- [
158
- country.emoji_flag,
159
- country.number,
160
- country.alpha2,
161
- country.alpha3,
162
- country.iso_short_name
163
- ]
164
- when Hash
165
- values = country.slice(
166
- "number",
167
- "alpha2",
168
- "alpha3",
169
- "iso_short_name"
170
- ).values
77
+ def self.country(country_code)
78
+ [
79
+ Format.country_title(country_code),
80
+ Format.one_line_country(country_code),
81
+ Format.multiline_country(country_code),
82
+ ].join("\n")
83
+ end
171
84
 
172
- [
173
- Data.emoji_flag(country.fetch("number")),
174
- *values
175
- ]
176
- else
177
- raise Error, "Unknown country type: #{country.class}"
178
- end.then do |country_values|
179
- "(#{country_values.compact.join(" | ")})"
180
- end
85
+ # @param country_code [String] ISO3166 2 letter country code
86
+ # @return [String]
87
+ def self.country_title(country_code)
88
+ Cache
89
+ .get("formatted_output/country_title.json")
90
+ .fetch(country_code)
91
+ .then(&Format.method(:title))
181
92
  end
182
93
 
183
- # @param region [Atlasq::Data::Region]
94
+ # @param country_code [String] ISO3166 2 letter country code
184
95
  # @return [String]
185
- def self.region(region)
186
- type = Util::String.titleize(region.type)
187
- title = "#{type}: #{region.name}"
96
+ def self.one_line_country(country_code)
97
+ Cache
98
+ .get("formatted_output/one_line_country.json")
99
+ .fetch(country_code)
100
+ end
188
101
 
189
- Format.countries(region.countries, title: title)
102
+ # @param country_code [String] ISO3166 2 letter country code
103
+ # @return [String]
104
+ def self.multiline_country(country_code)
105
+ Cache
106
+ .get("formatted_output/multiline_country.json")
107
+ .fetch(country_code)
190
108
  end
191
109
 
192
- # @param subregions [Hash<String, Array<ISO3166::Country>>]
110
+ # @param subregions [Hash<String, Array<String>>] region name to ISO3166 2 letter country codes
193
111
  # @return [String]
194
112
  def self.subregions(subregions)
195
- subregions.transform_values! do |countries|
196
- countries.map(&Format.method(:one_line_country))
113
+ subregions = subregions.to_h do |region, countries|
114
+ [
115
+ Util::String.titleize(region),
116
+ countries.map(&Format.method(:one_line_country)),
117
+ ]
197
118
  end
198
119
 
199
120
  Format.brief_template(title: "All Subregions", elements: subregions)
200
121
  end
201
122
 
202
- # @param currencies [Array<Atlasq::Data::Currencies]
123
+ # @param currencies [Hash<String, Array<String>>] 3 letter currency code to ISO3166 2 letter country codes
124
+ # @param partial_match [Boolean] defaults to false
203
125
  # @return [String]
204
- def self.currencies(currencies)
205
- currencies = currencies.to_h do |currency_class|
206
- currency = Money::Currency.new(currency_class.currency_code)
207
-
126
+ def self.currencies(currencies, partial_match: false)
127
+ currencies = currencies.to_h do |currency, countries|
208
128
  [
209
- "[#{currency.iso_code}] #{currency.symbol} #{currency.name}",
210
- currency_class.countries.map(&Format.method(:one_line_country))
129
+ Format.one_line_currency(currency),
130
+ countries.map(&Format.method(:one_line_country)),
211
131
  ]
212
132
  end
213
133
 
214
- if currencies.size == 1
134
+ if !partial_match && currencies.size == 1
215
135
  title, elements = currencies.first
216
136
  title = "Currency: #{title}"
217
137
  Format.brief_template(title: title, elements: elements)
218
138
  else
219
- Format.brief_template(title: "Currencies", elements: currencies)
139
+ title = partial_match ? "Currencies (Partial Match)" : "Currencies"
140
+ Format.brief_template(title: title, elements: currencies)
220
141
  end
221
142
  end
143
+
144
+ def self.one_line_currency(currency_code)
145
+ Cache
146
+ .get("formatted_output/one_line_currency.json")
147
+ .fetch(currency_code)
148
+ end
222
149
  end
223
150
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atlasq
4
+ module PartialMatch
5
+ # @param term [String]
6
+ # @return [Array<String>] 3 digit country codes (ISO3166)
7
+ def self.countries(term)
8
+ @countries ||= Util::WordMap.new(index: Cache.get("search_index/partial_match_country.json"))
9
+ @countries.search(term)
10
+ end
11
+
12
+ # @param term [String]
13
+ # @return [Array<String>] 3 letter currency codes (ISO4217)
14
+ def self.currencies(term)
15
+ @currencies ||= Util::WordMap.new(index: Cache.get("search_index/partial_match_currency.json"))
16
+ @currencies.search(term)
17
+ end
18
+ end
19
+ end
@@ -1,16 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "unaccent"
4
+
3
5
  module Atlasq
4
6
  module Util
5
7
  module String
6
- # @param string [#to_s]
8
+ ABBREVIATIONS = {
9
+ "AMER" => "North, Central and South America",
10
+ "APAC" => "Asia-Pacific",
11
+ "EMEA" => "Europe, Middle East, and Africa",
12
+ }.freeze
13
+
14
+ # @param string [String]
7
15
  # @return [String]
8
16
  def self.titleize(string)
9
- string
10
- .to_s
11
- .split(/[-_ \t]+/)
12
- .map(&:capitalize)
13
- .join(" ")
17
+ abbreviation = string.upcase
18
+
19
+ if (full_name = ABBREVIATIONS[abbreviation])
20
+ "#{full_name} (#{abbreviation})"
21
+ else
22
+ words = string.split(/[-_ \t]+/)
23
+ words.map! do |word|
24
+ if word.match?(/^(?:of|the|and)$/i)
25
+ word.downcase
26
+ else
27
+ word.capitalize
28
+ end
29
+ end
30
+ words.join(" ")
31
+ end
32
+ end
33
+
34
+ # Make string lowercase and remove accents.
35
+ #
36
+ # @param string [String]
37
+ # @return [String]
38
+ def self.normalize(string)
39
+ @normalize ||= {}
40
+ @normalize[string] ||= Unaccent.unaccent(string.downcase)
41
+ end
42
+
43
+ # Split on spaces, tabs and punctuation separators.
44
+ # Note: Some punctuation can be connectors or separators based on the language.
45
+ #
46
+ # @param sentence [String]
47
+ # @return [Array<String>]
48
+ def self.word_split(sentence)
49
+ sentence.split(/[ \t,;:()]+/).reject(&:empty?)
14
50
  end
15
51
  end
16
52
  end