Class: Worldwide::Region
- Inherits:
-
Object
- Object
- Worldwide::Region
- Defined in:
- lib/worldwide/region.rb
Constant Summary collapse
- REQUIRED =
When faced with the question, “Is a postal code required in this country?”, we treat the answer as a tri-state configuration item. “Recommended” means that we recommend that it be provided, but you may still leave the zip field blank.
"required"- RECOMMENDED =
"recommended"- OPTIONAL =
"optional"- INSPECTION_FIELDS =
The default ‘.inspect` isn’t a good fit for Region, because it can end up dumping a lot of info as it walks the hierarchy of descendants. So, instead, we provide our own ‘.inspect` that only shows a restricted subset of the object’s fields.
[ :alpha_three, :building_number_required, :currency, :example_city, :flag, :format, :group, :group_name, :cldr_code, :iso_code, :languages, :neighbours, :numeric_three, :week_start_day, :unit_system, :zip_autofill_enabled, :zip_example, :zip_regex, :zip_requirement, ]
Instance Attribute Summary collapse
-
#alpha_three ⇒ Object
readonly
ISO-3166 three-letter code for this region, if there is one.
-
#building_number_required ⇒ Object
In some countries, every address must have a building number.
-
#cldr_code ⇒ Object
readonly
The CLDR code for this region.
-
#code_alternates ⇒ Object
Alternate codes which may be used to designate this region.
-
#currency ⇒ Object
The suggested currency for use in this region.
-
#example_city ⇒ Object
A major city in the given region that can be used as an example.
-
#flag ⇒ Object
Unicode codepoints for this region’s flag emoji.
-
#format ⇒ Object
Hash of strings denoting how to format an address in this region.
-
#group ⇒ Object
The string that results from appending “ Countries” to the adjectival form of the #group_name.
-
#group_name ⇒ Object
The continent that this region is part of.
-
#hide_provinces_from_addresses ⇒ Object
If this flag is set, then we support provinces “under the hood” for this country, but we do not show them as part of a formatted address.
-
#iso_code ⇒ Object
readonly
The ISO-3166-2 code for this region (e.g. “CA”, “CA-ON”) or, if there is no alpha-2 code defined for this region, a numeric code (e.g. “001”).
-
#languages ⇒ Object
Languages that are commonly used in this region.
-
#legacy_code ⇒ Object
readonly
The code used by the legacy Shopify ecosystem for this region.
-
#legacy_name ⇒ Object
readonly
The name used by the legacy Shopify ecosystem for this region.
-
#neighbours ⇒ Object
iso_code values of regions (subdivisions) within the same country that border this region.
-
#numeric_three ⇒ Object
readonly
The ISO-3166-1 three-digit code for this region (returned as a string to preserve leading zeroes), e.g., “003”.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#partial_zip_regex ⇒ Object
Some countries have a multi-part postal code, and we may in some cases encounter only the first part.
-
#phone_number_prefix ⇒ Object
The telephone country dialing code for this region.
-
#province_optional ⇒ Object
If true, then the province is optional for addresses in this region.
-
#tags ⇒ Object
tags that help us group the region, e.g.
-
#tax_name ⇒ Object
readonly
Value Added Tax (Sales Tax) name Note that this should really be translated; showing this untranslated name to users is a bad idea.
-
#tax_rate ⇒ Object
readonly
“generic” VAT tax rate on “most” goods.
-
#timezone ⇒ Object
If the region is within a single timezone, its Olson name will be given here.
-
#timezones ⇒ Object
If the region spans multiple timezones (and it has postal codes), then this attribute will contain a hash table mapping from timezone name to a list of postal code prefixes.
-
#unit_system ⇒ Object
The measurement system in use in this region.
-
#use_zone_code_as_short_name ⇒ Object
true iff zone.iso_code should be returned as the .short_name for zones of this region.
-
#week_start_day ⇒ Object
Day of the week (English language string) on which the week is considered to start in this region.
-
#zip_autofill_enabled ⇒ Object
Some regions have only a single postal code value.
-
#zip_example ⇒ Object
An example of a valid postal code for this region.
-
#zip_prefixes ⇒ Object
A list of character sequences with which a postal code in this region may start.
-
#zip_regex ⇒ Object
A regular expression which postal codes in this region must match.
-
#zip_requirement ⇒ Object
Is a zip value required in this region? (Possible values: “optional”, “recommended”, “required”).
-
#zips_crossing_provinces ⇒ Object
Hash of zips that are valid for more than one province.
-
#zones ⇒ Object
readonly
Regions that are sub-regions of this region.
Instance Method Summary collapse
-
#add_zone(region) ⇒ Object
Relationships.
-
#autofill_zip ⇒ Object
The value with which to autofill the zip, if this region has zip autofill active; otherwise, nil.
-
#city_required? ⇒ Boolean
Does this region require cities to be specified?.
-
#continent? ⇒ Boolean
Is this Region a continent?.
-
#country? ⇒ Boolean
Is this Region considered a “country” (top-level political entity “country or region”) in the view of the legacy Shopify ecosystem?.
- #deprecated? ⇒ Boolean
-
#field(key:) ⇒ Object
An Worldwide::Field that can be used to ask about the field, including labels, error messages, and an autofill value if there is one.
-
#full_name(locale: I18n.locale) ⇒ Object
A user-facing name in the currently-active locale’s language.
-
#has_zip? ⇒ Boolean
Does this region have postal codes?.
-
#initialize(alpha_three: nil, continent: false, country: false, deprecated: false, cldr_code: nil, iso_code: nil, legacy_code: nil, legacy_name: nil, numeric_three: nil, province: false, short_name: nil, tax_name: nil, tax_rate: 0.0, use_zone_code_as_short_name: false) ⇒ Region
constructor
A new instance of Region.
- #inspect ⇒ Object
-
#province? ⇒ Boolean
Is this Region considered a “province” (political subdivision of a “country”)?.
-
#province_optional? ⇒ Boolean
are zones optional for this region?.
-
#short_name ⇒ Object
A short-form name for this region, if there is a conventional short form.
-
#valid_zip?(zip, partial_match: false) ⇒ Boolean
is the given postal code value valid for this region?.
-
#zip_autofill ⇒ Object
If the Region has an autofill zip, return the value that will be autofilled Otherwise, return nil.
-
#zip_required? ⇒ Boolean
is a postal code required for this region?.
-
#zone(code: nil, name: nil, zip: nil) ⇒ Object
returns a Region that is a child of this Region.
Constructor Details
#initialize(alpha_three: nil, continent: false, country: false, deprecated: false, cldr_code: nil, iso_code: nil, legacy_code: nil, legacy_name: nil, numeric_three: nil, province: false, short_name: nil, tax_name: nil, tax_rate: 0.0, use_zone_code_as_short_name: false) ⇒ Region
Returns a new instance of Region.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/worldwide/region.rb', line 184 def initialize( alpha_three: nil, continent: false, country: false, deprecated: false, cldr_code: nil, iso_code: nil, legacy_code: nil, legacy_name: nil, numeric_three: nil, province: false, short_name: nil, tax_name: nil, tax_rate: 0.0, use_zone_code_as_short_name: false ) if iso_code.nil? && numeric_three.nil? raise ArgumentError, "At least one of iso_code: and numeric_three: must be provided" end @alpha_three = alpha_three&.to_s&.upcase @continent = continent @country = country @deprecated = deprecated @cldr_code = cldr_code @iso_code = iso_code&.to_s&.upcase @legacy_code = legacy_code @legacy_name = legacy_name @numeric_three = numeric_three&.to_s @province = province @short_name = short_name @tax_name = tax_name @tax_rate = tax_rate @use_zone_code_as_short_name = use_zone_code_as_short_name @building_number_required = false @currency = nil @flag = nil @format = {} @group = nil @group_name = nil @languages = [] @neighbours = [] @partial_zip_regex = nil @phone_number_prefix = nil @tags = [] @timezone = nil @timezones = {} @unit_system = nil @week_start_day = nil @zip_autofill_enabled = false @zip_example = nil @zip_prefixes = [] @zip_regex = nil @parent = nil @zones = [] end |
Instance Attribute Details
#alpha_three ⇒ Object (readonly)
ISO-3166 three-letter code for this region, if there is one. Otherwise, nil.
41 42 43 |
# File 'lib/worldwide/region.rb', line 41 def alpha_three @alpha_three end |
#building_number_required ⇒ Object
In some countries, every address must have a building number. In others (e.g., GB), some addresses rely on just a building name, not a number. If we require a building number in an address, then this will be true.
46 47 48 |
# File 'lib/worldwide/region.rb', line 46 def building_number_required @building_number_required end |
#cldr_code ⇒ Object (readonly)
The CLDR code for this region.
84 85 86 |
# File 'lib/worldwide/region.rb', line 84 def cldr_code @cldr_code end |
#code_alternates ⇒ Object
Alternate codes which may be used to designate this region
49 50 51 |
# File 'lib/worldwide/region.rb', line 49 def code_alternates @code_alternates end |
#currency ⇒ Object
The suggested currency for use in this region. Note that this may not always be the official currency. E.g., we return USD for VE, not VED.
54 55 56 |
# File 'lib/worldwide/region.rb', line 54 def currency @currency end |
#example_city ⇒ Object
A major city in the given region that can be used as an example
57 58 59 |
# File 'lib/worldwide/region.rb', line 57 def example_city @example_city end |
#flag ⇒ Object
Unicode codepoints for this region’s flag emoji
60 61 62 |
# File 'lib/worldwide/region.rb', line 60 def flag @flag end |
#format ⇒ Object
Hash of strings denoting how to format an address in this region. The format is described in shopify.engineering/handling-addresses-from-all-around-the-world
- address1: a street address (address line 1, with a buliding nmuber and street name)
- address1_with_unit: address line 1 including a subpremise (unit, apartment, etc.)
- edit: the fields to present on an address input form
- show: how to arrange the fields when formatting an address for display
68 69 70 |
# File 'lib/worldwide/region.rb', line 68 def format @format end |
#group ⇒ Object
The string that results from appending “ Countries” to the adjectival form of the #group_name
73 74 75 |
# File 'lib/worldwide/region.rb', line 73 def group @group end |
#group_name ⇒ Object
The continent that this region is part of.
76 77 78 |
# File 'lib/worldwide/region.rb', line 76 def group_name @group_name end |
#hide_provinces_from_addresses ⇒ Object
If this flag is set, then we support provinces “under the hood” for this country, but we do not show them as part of a formatted address. If the province is missing, we will auto-infer it based on the zip (note that this auto-inference may be wrong for some addresses near a border).
81 82 83 |
# File 'lib/worldwide/region.rb', line 81 def hide_provinces_from_addresses @hide_provinces_from_addresses end |
#iso_code ⇒ Object (readonly)
The ISO-3166-2 code for this region (e.g. “CA”, “CA-ON”) or, if there is no alpha-2 code defined for this region, a numeric code (e.g. “001”).
88 89 90 |
# File 'lib/worldwide/region.rb', line 88 def iso_code @iso_code end |
#languages ⇒ Object
Languages that are commonly used in this region. Note that this may not be the same as the languages that are officially recognized there. We present them in alphabetical order by language code.
93 94 95 |
# File 'lib/worldwide/region.rb', line 93 def languages @languages end |
#legacy_code ⇒ Object (readonly)
The code used by the legacy Shopify ecosystem for this region. E.g., for MX-CMX it will return “DF”. This code should never be shown in the user interface. This is the code that was traditionally returned by “country_db”.
99 100 101 |
# File 'lib/worldwide/region.rb', line 99 def legacy_code @legacy_code end |
#legacy_name ⇒ Object (readonly)
The name used by the legacy Shopify ecosystem for this region. E.g., “Sao Tome And Principe” for “ST”. This name should never be shown in the user interface. This name is the name that was traditionally returned by “country_db”.
105 106 107 |
# File 'lib/worldwide/region.rb', line 105 def legacy_name @legacy_name end |
#neighbours ⇒ Object
iso_code values of regions (subdivisions) within the same country that border this region. E.g., for CA-ON, the neighbouring zones are CA-MB, CA-NU and CA-QC.
109 110 111 |
# File 'lib/worldwide/region.rb', line 109 def neighbours @neighbours end |
#numeric_three ⇒ Object (readonly)
The ISO-3166-1 three-digit code for this region (returned as a string to preserve leading zeroes), e.g., “003”.
113 114 115 |
# File 'lib/worldwide/region.rb', line 113 def numeric_three @numeric_three end |
#parent ⇒ Object
Returns the value of attribute parent.
37 38 39 |
# File 'lib/worldwide/region.rb', line 37 def parent @parent end |
#partial_zip_regex ⇒ Object
Some countries have a multi-part postal code, and we may in some cases encounter only the first part. E.g., the GB code ‘SW1A 1AA` has a first part (outward code) of `SW1A`. When validating such a partial postal code, it must match this regular expression.
118 119 120 |
# File 'lib/worldwide/region.rb', line 118 def partial_zip_regex @partial_zip_regex end |
#phone_number_prefix ⇒ Object
The telephone country dialing code for this region
121 122 123 |
# File 'lib/worldwide/region.rb', line 121 def phone_number_prefix @phone_number_prefix end |
#province_optional ⇒ Object
If true, then the province is optional for addresses in this region.
182 183 184 |
# File 'lib/worldwide/region.rb', line 182 def province_optional @province_optional end |
#tags ⇒ Object
tags that help us group the region, e.g. “EU-member”
139 140 141 |
# File 'lib/worldwide/region.rb', line 139 def @tags end |
#tax_name ⇒ Object (readonly)
Value Added Tax (Sales Tax) name Note that this should really be translated; showing this untranslated name to users is a bad idea.
133 134 135 |
# File 'lib/worldwide/region.rb', line 133 def tax_name @tax_name end |
#tax_rate ⇒ Object (readonly)
“generic” VAT tax rate on “most” goods
136 137 138 |
# File 'lib/worldwide/region.rb', line 136 def tax_rate @tax_rate end |
#timezone ⇒ Object
If the region is within a single timezone, its Olson name will be given here.
142 143 144 |
# File 'lib/worldwide/region.rb', line 142 def timezone @timezone end |
#timezones ⇒ Object
If the region spans multiple timezones (and it has postal codes), then this attribute will contain a hash table mapping from timezone name to a list of postal code prefixes. We can use this information to determine the timezone for a given postal code.
147 148 149 |
# File 'lib/worldwide/region.rb', line 147 def timezones @timezones end |
#unit_system ⇒ Object
The measurement system in use in this region.
154 155 156 |
# File 'lib/worldwide/region.rb', line 154 def unit_system @unit_system end |
#use_zone_code_as_short_name ⇒ Object
true iff zone.iso_code should be returned as the .short_name for zones of this region
157 158 159 |
# File 'lib/worldwide/region.rb', line 157 def use_zone_code_as_short_name @use_zone_code_as_short_name end |
#week_start_day ⇒ Object
Day of the week (English language string) on which the week is considered to start in this region. E.g., “sunday”
151 152 153 |
# File 'lib/worldwide/region.rb', line 151 def week_start_day @week_start_day end |
#zip_autofill_enabled ⇒ Object
Some regions have only a single postal code value. In such cases, we can autofill the zip field with the value from zip_example.
161 162 163 |
# File 'lib/worldwide/region.rb', line 161 def zip_autofill_enabled @zip_autofill_enabled end |
#zip_example ⇒ Object
An example of a valid postal code for this region
164 165 166 |
# File 'lib/worldwide/region.rb', line 164 def zip_example @zip_example end |
#zip_prefixes ⇒ Object
A list of character sequences with which a postal code in this region may start.
170 171 172 |
# File 'lib/worldwide/region.rb', line 170 def zip_prefixes @zip_prefixes end |
#zip_regex ⇒ Object
A regular expression which postal codes in this region must match.
173 174 175 |
# File 'lib/worldwide/region.rb', line 173 def zip_regex @zip_regex end |
#zip_requirement ⇒ Object
Is a zip value required in this region? (Possible values: “optional”, “recommended”, “required”)
167 168 169 |
# File 'lib/worldwide/region.rb', line 167 def zip_requirement @zip_requirement end |
#zips_crossing_provinces ⇒ Object
Hash of zips that are valid for more than one province
176 177 178 |
# File 'lib/worldwide/region.rb', line 176 def zips_crossing_provinces @zips_crossing_provinces end |
#zones ⇒ Object (readonly)
Regions that are sub-regions of this region.
179 180 181 |
# File 'lib/worldwide/region.rb', line 179 def zones @zones end |
Instance Method Details
#add_zone(region) ⇒ Object
Relationships
249 250 251 252 253 254 |
# File 'lib/worldwide/region.rb', line 249 def add_zone(region) return if @zones.include?(region) region.parent = self @zones.append(region) end |
#autofill_zip ⇒ Object
The value with which to autofill the zip, if this region has zip autofill active; otherwise, nil.
260 261 262 |
# File 'lib/worldwide/region.rb', line 260 def autofill_zip zip_example if @zip_autofill_enabled end |
#city_required? ⇒ Boolean
Does this region require cities to be specified?
265 266 267 |
# File 'lib/worldwide/region.rb', line 265 def city_required? field(key: :city).autofill(locale: :en).nil? end |
#continent? ⇒ Boolean
Is this Region a continent?
270 271 272 |
# File 'lib/worldwide/region.rb', line 270 def continent? @continent end |
#country? ⇒ Boolean
Is this Region considered a “country” (top-level political entity “country or region”) in the view of the legacy Shopify ecosystem?
276 277 278 |
# File 'lib/worldwide/region.rb', line 276 def country? @country end |
#deprecated? ⇒ Boolean
280 281 282 |
# File 'lib/worldwide/region.rb', line 280 def deprecated? @deprecated end |
#field(key:) ⇒ Object
An Worldwide::Field that can be used to ask about the field, including labels, error messages, and an autofill value if there is one.
286 287 288 289 290 |
# File 'lib/worldwide/region.rb', line 286 def field(key:) return nil unless country? Worldwide::Fields.field(country_code: iso_code, field_key: key) end |
#full_name(locale: I18n.locale) ⇒ Object
A user-facing name in the currently-active locale’s language.
293 294 295 296 297 298 299 300 |
# File 'lib/worldwide/region.rb', line 293 def full_name(locale: I18n.locale) lookup_code = cldr_code if /^[0-9]+$/.match?(lookup_code) || lookup_code.length < 3 Worldwide::Cldr.t("territories.#{lookup_code}", locale: locale, default: legacy_name) else Worldwide::Cldr.t("subdivisions.#{lookup_code}", locale: locale, default: legacy_name) end end |
#has_zip? ⇒ Boolean
Does this region have postal codes?
303 304 305 |
# File 'lib/worldwide/region.rb', line 303 def has_zip? !!format["show"]&.include?("{zip}") end |
#inspect ⇒ Object
243 244 245 |
# File 'lib/worldwide/region.rb', line 243 def inspect "#<#{self.class.name}:#{object_id} #{inspected_fields}>" end |
#province? ⇒ Boolean
Is this Region considered a “province” (political subdivision of a “country”)?
308 309 310 |
# File 'lib/worldwide/region.rb', line 308 def province? @province end |
#province_optional? ⇒ Boolean
are zones optional for this region?
381 382 383 |
# File 'lib/worldwide/region.rb', line 381 def province_optional? province_optional || !@zones&.any?(&:province?) end |
#short_name ⇒ Object
A short-form name for this region, if there is a conventional short form. E.g., returns “ON” for “CA-ON”, but “Tokyo” for “JP-13”.
314 315 316 |
# File 'lib/worldwide/region.rb', line 314 def short_name @short_name || full_name end |
#valid_zip?(zip, partial_match: false) ⇒ Boolean
is the given postal code value valid for this region?
372 373 374 375 376 377 378 |
# File 'lib/worldwide/region.rb', line 372 def valid_zip?(zip, partial_match: false) normalized = Zip.normalize( country_code: province? && parent.iso_code ? parent.iso_code : iso_code, zip: zip, ) valid_normalized_zip?(normalized, partial_match: partial_match) end |
#zip_autofill ⇒ Object
If the Region has an autofill zip, return the value that will be autofilled Otherwise, return nil
358 359 360 |
# File 'lib/worldwide/region.rb', line 358 def zip_autofill return zip_example if zip_autofill_enabled end |
#zip_required? ⇒ Boolean
is a postal code required for this region?
363 364 365 366 367 368 369 |
# File 'lib/worldwide/region.rb', line 363 def zip_required? if zip_requirement.nil? !zip_regex.nil? else REQUIRED == zip_requirement end end |
#zone(code: nil, name: nil, zip: nil) ⇒ Object
returns a Region that is a child of this Region
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/worldwide/region.rb', line 319 def zone(code: nil, name: nil, zip: nil) count = 0 count += 1 unless code.nil? count += 1 unless name.nil? count += 1 unless zip.nil? unless count == 1 # More than one of code, name, or zip was given raise ArgumentError, "Must specify exactly one of code:, name: or zip:." end if Worldwide::Util.present?(code) search_code = code.to_s.upcase alt_search_code = "#{search_code[0..1]}-#{search_code[2..-1]}" zones.find do |region| [search_code, alt_search_code].any? do |candidate| candidate == region.alpha_three || candidate == region.iso_code || candidate == region.legacy_code || candidate == region.numeric_three || region&.code_alternates&.any?(candidate) end end elsif Worldwide::Util.present?(name) search_name = name.upcase zones.find do |region| search_name == region.legacy_name.upcase || search_name == region.full_name.upcase || search_name == I18n.with_locale(:en) { region.full_name.upcase } end else # Worldwide::Util.present?(zip) zone_by_normalized_zip(Zip.normalize(country_code: iso_code, zip: zip)) end || Worldwide.unknown_region end |