atlasq 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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