Module: Worldwide::Zip
Instance Method Summary collapse
-
#find_country(country_code: nil, zip:, min_confidence: 0) ⇒ Region
Suggest a country for a given postal code.
-
#gb_style?(country_code:) ⇒ Boolean
The United Kingdom has an unusual style of postcode.
-
#normalize(country_code:, zip:, allow_autofill: true, strip_extraneous_characters: false) ⇒ String
Normalizes the postal code into the format expected by the national postal authority.
-
#numeric_only_zip?(country_code:) ⇒ Boolean
We want to show numeric keypad on mobile view for countries with only numeric postal codes Spaces or dashes are allowed.
-
#outcode(country_code:, zip:) ⇒ Object
Returns the “outcode” (first portion) of a postcode for a country that uses the UK style.
-
#pure_numeric_only_zip?(country_code:) ⇒ Boolean
We want to show numeric keypad on mobile view for countries with only numeric postal codes Spaces or dashes aren’t allowed.
- #strip_optional_country_prefix(country_code:, zip:) ⇒ Object
Instance Method Details
#find_country(country_code: nil, zip:, min_confidence: 0) ⇒ Region
Suggest a country for a given postal code.
Some countries have an ambiguous dual state. For example, some consider Jersey to be a top-level country in its own right, while others consider it to be part of the United Kingdom. This leads to frustrated buyers receiving a validation error when they enter their address and the postal code isn’t valid for the selected “country”.
Also, in some cases, users are simply confused, and use a postal code that is obviously inappropriate for the selected country. Some examples:
- entering a postal code for Georgia, United States, but selecting the country Georgia
- using a USA FPO address, but selecting the non-US physical location of the US base as the country
This method attempts to heuristically suggest a country in some common cases where we see a lot of confusion. There’s no way to solve this problem for all cases, and we will often not have a suggestion (in which case this method will return ‘nil`).
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/worldwide/zip.rb', line 52 def find_country(country_code: nil, zip:, min_confidence: 0) return nil unless zip.present? country = Worldwide.region(code: country_code) unless country_code.nil? return country if country&.valid_zip?(zip) adjusted_zip = zip.strip.upcase # Try to match based on the alleged country suggestion, confidence = find_country_using_alleged_country(country_code, adjusted_zip) return suggestion unless suggestion.nil? || confidence.nil? || confidence < min_confidence # If our postal code is wholly numeric, we can't make an intelligent suggestion without an alleged country. return nil unless adjusted_zip.match?(/[A-Z]/) # Try a broader-ranging match without considering the alleged country # We'll see if we have only a single suggestion and, if so, return it. # In cases where there's more than one possible match, we'll return nil. suggestions = find_country_using_zip_alone(adjusted_zip) suggestion = suggestions.first[0] unless suggestions.blank? confidence = suggestions.first[1] unless suggestions.blank? return suggestion if suggestions.length == 1 && confidence && confidence >= min_confidence nil end |
#gb_style?(country_code:) ⇒ Boolean
The United Kingdom has an unusual style of postcode. It consists of two halves (outcode and incode), separated by a space. The incode is always 3 characters, with 1 digit followed by 2 letters. The outcode varies from 2 to 4 characters, following certain patterns. A handful of other countries also allocate their codes within the UK “namespace”.
28 29 30 |
# File 'lib/worldwide/zip.rb', line 28 def gb_style?(country_code:) GB_STYLE_ZIP_COUNTRIES.include?(country_code.to_s.upcase) end |
#normalize(country_code:, zip:, allow_autofill: true, strip_extraneous_characters: false) ⇒ String
Normalizes the postal code into the format expected by the national postal authority.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/worldwide/zip.rb', line 85 def normalize(country_code:, zip:, allow_autofill: true, strip_extraneous_characters: false) input = zip # preserve the original zip, in case we need to fall back to it country = Worldwide.region(code: country_code) return zip if country.nil? || NORMALIZATION_DISABLED_COUNTRIES.include?(country.iso_code) if allow_autofill autofill = country.autofill_zip return autofill if autofill.present? end return nil if zip.nil? # Convert to uppercase # Convert numeric and romaji full-width to half-width # Strip hyphens, dashes and the Japanese postcode marker zip = zip.upcase.tr("0-9a-zA-Z", "0-9a-zA-Z") .delete("〒\u058a\u05be\u1806\u1b60\u200b\u2010\u2011\u2012\u2013\u2014\u2015\u2053\u2e17\u2e3a\u2e3b\u2212\u30fb\u30fc\ufe58\ufe63\uff0d\uff65(),./_~-") zip = add_prefix_if_required(country_code: country_code, zip: zip) if strip_extraneous_characters zip = strip_extraneous_characters(zip: zip, country_code: country_code) end result = if gb_style?(country_code: country.iso_code) normalize_for_gb(zip: zip) else # Remove both normal-width and double-width spaces zip.delete!(" ") zip = replace_letters_and_numbers(country_code: country.iso_code, zip: zip) if "BD" == country.iso_code normalize_for_bd(zip: zip) elsif "FO" == country.iso_code normalize_for_fo(zip: zip) elsif "GH" == country.iso_code normalize_for_gh(zip: zip) elsif "HT" == country.iso_code normalize_for_ht(zip: zip) elsif "LK" == country.iso_code normalize_for_lk(zip: zip) elsif "MD" == country.iso_code normalize_for_md(zip: zip) elsif "MG" == country.iso_code normalize_for_mg(zip: zip) elsif "NG" == country.iso_code normalize_for_ng(zip: zip) elsif "SG" == country.iso_code normalize_for_sg(zip: zip) elsif "MA" == country.iso_code normalize_for_ma(zip: zip) elsif "XK" == country.iso_code normalize_for_xk(zip: zip) elsif "BR" == country.iso_code || "JP" == country.iso_code insert_spaces_and_hyphens_for_partial_code(country_code: country.iso_code, zip: zip) else insert_spaces_and_hyphens(country_code: country.iso_code, zip: zip) end end if country.send(:valid_normalized_zip?, result) result elsif country.send(:valid_normalized_zip?, result, partial_match: true) result else input # fall back to the original input, because we don't seem to have generated anything sensible end end |
#numeric_only_zip?(country_code:) ⇒ Boolean
We want to show numeric keypad on mobile view for countries with only numeric postal codes Spaces or dashes are allowed.
10 11 12 |
# File 'lib/worldwide/zip.rb', line 10 def numeric_only_zip?(country_code:) NUMERIC_ONLY_ZIP_COUNTRIES.include?(country_code.to_s.upcase) end |
#outcode(country_code:, zip:) ⇒ Object
Returns the “outcode” (first portion) of a postcode for a country that uses the UK style. Returns the “forward sortation area” (first portion) of a postal code for Canada. Returns the “routing key” (first portion) of a postal code for Ireland. Otherwise, returns the full zip.
157 158 159 160 161 162 163 |
# File 'lib/worldwide/zip.rb', line 157 def outcode(country_code:, zip:) @split_code_countries ||= Set.new(GB_STYLE_ZIP_COUNTRIES).add("CA").add("IE") return zip unless @split_code_countries.include?(country_code.to_s.upcase) normalize(country_code: country_code, zip: zip)&.split(" ")&.first end |
#pure_numeric_only_zip?(country_code:) ⇒ Boolean
We want to show numeric keypad on mobile view for countries with only numeric postal codes Spaces or dashes aren’t allowed.
17 18 19 20 21 |
# File 'lib/worldwide/zip.rb', line 17 def pure_numeric_only_zip?(country_code:) return false if SPACES_AND_HYPHENS.key?(country_code.to_s.upcase.to_sym) numeric_only_zip?(country_code: country_code) end |
#strip_optional_country_prefix(country_code:, zip:) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/worldwide/zip.rb', line 165 def strip_optional_country_prefix(country_code:, zip:) return zip if zip.blank? unless OPTIONAL_PREFIX_COUNTRIES.include?(country_code&.to_sym) return zip end vehicle_code = OPTIONAL_PREFIX_COUNTRIES[country_code&.to_sym] stripped = zip.strip upcased = stripped.upcase if upcased.start_with?(country_code&.to_s) stripped = stripped[country_code&.to_s&.length..-1] elsif upcased.start_with?(vehicle_code) stripped = stripped[vehicle_code.length..-1] end if stripped.start_with?("-") stripped = stripped[1..-1] end m = stripped.match(/^\d/) if m.nil? zip else stripped end end |