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_may_be_in_address2 ⇒ Object
In some countries, an address may have the building number in address2.
-
#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.
-
#name_alternates ⇒ Object
Other names that may be used to refer to 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”.
-
#parents ⇒ Object
A region may have more than one 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.
- #associated_continent ⇒ Object
-
#associated_country ⇒ Object
Attributes.
-
#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.
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 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/worldwide/region.rb', line 194 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 @building_number_may_be_in_address2 = 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 @parents = [].to_set @zones = [] end |
Instance Attribute Details
#alpha_three ⇒ Object (readonly)
ISO-3166 three-letter code for this region, if there is one. Otherwise, nil.
43 44 45 |
# File 'lib/worldwide/region.rb', line 43 def alpha_three @alpha_three end |
#building_number_may_be_in_address2 ⇒ Object
In some countries, an address may have the building number in address2. If we are allowed to have a building number in address2, then this will be true.
52 53 54 |
# File 'lib/worldwide/region.rb', line 52 def building_number_may_be_in_address2 @building_number_may_be_in_address2 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.
48 49 50 |
# File 'lib/worldwide/region.rb', line 48 def building_number_required @building_number_required end |
#cldr_code ⇒ Object (readonly)
The CLDR code for this region.
90 91 92 |
# File 'lib/worldwide/region.rb', line 90 def cldr_code @cldr_code end |
#code_alternates ⇒ Object
Alternate codes which may be used to designate this region
55 56 57 |
# File 'lib/worldwide/region.rb', line 55 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.
60 61 62 |
# File 'lib/worldwide/region.rb', line 60 def currency @currency end |
#example_city ⇒ Object
A major city in the given region that can be used as an example
63 64 65 |
# File 'lib/worldwide/region.rb', line 63 def example_city @example_city end |
#flag ⇒ Object
Unicode codepoints for this region’s flag emoji
66 67 68 |
# File 'lib/worldwide/region.rb', line 66 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
74 75 76 |
# File 'lib/worldwide/region.rb', line 74 def format @format end |
#group ⇒ Object
The string that results from appending “ Countries” to the adjectival form of the #group_name
79 80 81 |
# File 'lib/worldwide/region.rb', line 79 def group @group end |
#group_name ⇒ Object
The continent that this region is part of.
82 83 84 |
# File 'lib/worldwide/region.rb', line 82 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).
87 88 89 |
# File 'lib/worldwide/region.rb', line 87 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”).
94 95 96 |
# File 'lib/worldwide/region.rb', line 94 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.
99 100 101 |
# File 'lib/worldwide/region.rb', line 99 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”.
105 106 107 |
# File 'lib/worldwide/region.rb', line 105 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”.
111 112 113 |
# File 'lib/worldwide/region.rb', line 111 def legacy_name @legacy_name end |
#name_alternates ⇒ Object
Other names that may be used to refer to this region. E.g., “Czech Republic” is also known as “Czechia”.
115 116 117 |
# File 'lib/worldwide/region.rb', line 115 def name_alternates @name_alternates 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.
119 120 121 |
# File 'lib/worldwide/region.rb', line 119 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”.
123 124 125 |
# File 'lib/worldwide/region.rb', line 123 def numeric_three @numeric_three end |
#parents ⇒ Object
A region may have more than one parent. For example, Puerto Rico (PR/US-PR) is associated with both the US and the Caribbean (029)
39 40 41 |
# File 'lib/worldwide/region.rb', line 39 def parents @parents 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.
128 129 130 |
# File 'lib/worldwide/region.rb', line 128 def partial_zip_regex @partial_zip_regex end |
#phone_number_prefix ⇒ Object
The telephone country dialing code for this region
131 132 133 |
# File 'lib/worldwide/region.rb', line 131 def phone_number_prefix @phone_number_prefix end |
#province_optional ⇒ Object
If true, then the province is optional for addresses in this region.
192 193 194 |
# File 'lib/worldwide/region.rb', line 192 def province_optional @province_optional end |
#tags ⇒ Object
tags that help us group the region, e.g. “EU-member”
149 150 151 |
# File 'lib/worldwide/region.rb', line 149 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.
143 144 145 |
# File 'lib/worldwide/region.rb', line 143 def tax_name @tax_name end |
#tax_rate ⇒ Object (readonly)
“generic” VAT tax rate on “most” goods
146 147 148 |
# File 'lib/worldwide/region.rb', line 146 def tax_rate @tax_rate end |
#timezone ⇒ Object
If the region is within a single timezone, its Olson name will be given here.
152 153 154 |
# File 'lib/worldwide/region.rb', line 152 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.
157 158 159 |
# File 'lib/worldwide/region.rb', line 157 def timezones @timezones end |
#unit_system ⇒ Object
The measurement system in use in this region.
164 165 166 |
# File 'lib/worldwide/region.rb', line 164 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
167 168 169 |
# File 'lib/worldwide/region.rb', line 167 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”
161 162 163 |
# File 'lib/worldwide/region.rb', line 161 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.
171 172 173 |
# File 'lib/worldwide/region.rb', line 171 def zip_autofill_enabled @zip_autofill_enabled end |
#zip_example ⇒ Object
An example of a valid postal code for this region
174 175 176 |
# File 'lib/worldwide/region.rb', line 174 def zip_example @zip_example end |
#zip_prefixes ⇒ Object
A list of character sequences with which a postal code in this region may start.
180 181 182 |
# File 'lib/worldwide/region.rb', line 180 def zip_prefixes @zip_prefixes end |
#zip_regex ⇒ Object
A regular expression which postal codes in this region must match.
183 184 185 |
# File 'lib/worldwide/region.rb', line 183 def zip_regex @zip_regex end |
#zip_requirement ⇒ Object
Is a zip value required in this region? (Possible values: “optional”, “recommended”, “required”)
177 178 179 |
# File 'lib/worldwide/region.rb', line 177 def zip_requirement @zip_requirement end |
#zips_crossing_provinces ⇒ Object
Hash of zips that are valid for more than one province
186 187 188 |
# File 'lib/worldwide/region.rb', line 186 def zips_crossing_provinces @zips_crossing_provinces end |
#zones ⇒ Object (readonly)
Regions that are sub-regions of this region.
189 190 191 |
# File 'lib/worldwide/region.rb', line 189 def zones @zones end |
Instance Method Details
#add_zone(region) ⇒ Object
Relationships
260 261 262 263 264 265 |
# File 'lib/worldwide/region.rb', line 260 def add_zone(region) return if @zones.include?(region) region.parents << self @zones.append(region) end |
#associated_continent ⇒ Object
275 276 277 278 279 280 281 282 283 284 |
# File 'lib/worldwide/region.rb', line 275 def associated_continent return self if continent? parents.each do |parent| candidate = parent.associated_continent return candidate unless candidate.nil? end nil end |
#associated_country ⇒ Object
Attributes
269 270 271 272 273 |
# File 'lib/worldwide/region.rb', line 269 def associated_country return self if country? parent_country end |
#autofill_zip ⇒ Object
The value with which to autofill the zip, if this region has zip autofill active; otherwise, nil.
288 289 290 |
# File 'lib/worldwide/region.rb', line 288 def autofill_zip zip_example if @zip_autofill_enabled end |
#city_required? ⇒ Boolean
Does this region require cities to be specified?
293 294 295 |
# File 'lib/worldwide/region.rb', line 293 def city_required? field(key: :city).autofill(locale: :en).nil? end |
#continent? ⇒ Boolean
Is this Region a continent?
298 299 300 |
# File 'lib/worldwide/region.rb', line 298 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?
304 305 306 |
# File 'lib/worldwide/region.rb', line 304 def country? @country end |
#deprecated? ⇒ Boolean
308 309 310 |
# File 'lib/worldwide/region.rb', line 308 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.
314 315 316 317 318 |
# File 'lib/worldwide/region.rb', line 314 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.
321 322 323 324 325 326 327 328 |
# File 'lib/worldwide/region.rb', line 321 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?
331 332 333 |
# File 'lib/worldwide/region.rb', line 331 def has_zip? !!format["show"]&.include?("{zip}") end |
#inspect ⇒ Object
254 255 256 |
# File 'lib/worldwide/region.rb', line 254 def inspect "#<#{self.class.name}:#{object_id} #{inspected_fields}>" end |
#province? ⇒ Boolean
Is this Region considered a “province” (political subdivision of a “country”)?
336 337 338 |
# File 'lib/worldwide/region.rb', line 336 def province? @province end |
#province_optional? ⇒ Boolean
are zones optional for this region?
410 411 412 |
# File 'lib/worldwide/region.rb', line 410 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”.
342 343 344 |
# File 'lib/worldwide/region.rb', line 342 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?
401 402 403 404 405 406 407 |
# File 'lib/worldwide/region.rb', line 401 def valid_zip?(zip, partial_match: false) normalized = Zip.normalize( country_code: province? && associated_country.iso_code ? associated_country.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
387 388 389 |
# File 'lib/worldwide/region.rb', line 387 def zip_autofill return zip_example if zip_autofill_enabled end |
#zip_required? ⇒ Boolean
is a postal code required for this region?
392 393 394 395 396 397 398 |
# File 'lib/worldwide/region.rb', line 392 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
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/worldwide/region.rb', line 347 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 == subdivision_code(region.iso_code) || 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 |