address_concern 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG +3 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +19 -0
- data/Readme.md +231 -0
- data/VERSION +1 -0
- data/address_concern.gemspec +33 -0
- data/bin/console +12 -0
- data/config/locale/overlay/en/id.yml +8 -0
- data/lib/address_concern/address.rb +306 -0
- data/lib/address_concern/address_associations.rb +66 -0
- data/lib/address_concern/attribute_normalizer.rb +9 -0
- data/lib/address_concern/version.rb +5 -0
- data/lib/address_concern.rb +11 -0
- data/lib/generators/address_concern/install_generator.rb +23 -0
- data/lib/generators/address_concern/templates/configurable.yml +33 -0
- data/lib/generators/address_concern/templates/migration.rb +37 -0
- data/spec/models/address_spec.rb +303 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/support/database.mysql2.yml +3 -0
- data/spec/support/database.sqlite3.yml +2 -0
- data/spec/support/models/address.rb +3 -0
- data/spec/support/models/application_record.rb +3 -0
- data/spec/support/models/child.rb +4 -0
- data/spec/support/models/company.rb +3 -0
- data/spec/support/models/employee.rb +4 -0
- data/spec/support/models/user.rb +3 -0
- data/spec/support/schema.rb +19 -0
- data/tmp/.gitkeep +0 -0
- metadata +232 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 54db12d5f83e9438e61c47552eba316511be4cbc69200bee3e51568a10c2c41e
|
4
|
+
data.tar.gz: 73a84002eeafb49d0536d2bf5de94391410b3b4d873633ba62f2fd3e42df8ab5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac7f541ac00b56b64c9a6e8f6cc7a49ee423cce621c90a13173ddc7e4c852883d5f5a2ad14bca967b2d7c7fcc9678a7b8062bb580c30baa43bb656e681b7fed9
|
7
|
+
data.tar.gz: 305160aabeca32990a9aa74aa2519b6b58b54c7354985ccbb1677fc1ec370b5c0f104319541834c33865f86079f10d4f45b31342be2d2656e90ade6eb42a85fa
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.7.4
|
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Paul Campbell
|
2
|
+
Copyright (c) 2011-2018 Tyler Rick
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
#---------------------------------------------------------------------------------------------------
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Start a console with this version of library loaded"
|
11
|
+
task :console do
|
12
|
+
require 'bundler/setup'
|
13
|
+
require 'address_concern'
|
14
|
+
require 'irb'
|
15
|
+
ARGV.clear
|
16
|
+
IRB.start
|
17
|
+
end
|
18
|
+
|
19
|
+
task :default => :spec
|
data/Readme.md
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
# Address Concern
|
2
|
+
|
3
|
+
A reusable polymorphic `Address` model concern for your Rails apps.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
Add `address_concern` to your `Gemfile`:
|
8
|
+
|
9
|
+
gem 'address_concern'
|
10
|
+
|
11
|
+
Then run the generator to create your addresses table:
|
12
|
+
|
13
|
+
rails generate address_concern:install
|
14
|
+
rake db:migrate
|
15
|
+
|
16
|
+
You now have an `Address` model that you can use in your app just as if it were in your `app/models` directory.
|
17
|
+
|
18
|
+
# Usage
|
19
|
+
|
20
|
+
## Base usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
class Address < ApplicationRecord
|
24
|
+
include AddressConcern::Address
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
## `belongs_to_address`
|
29
|
+
|
30
|
+
`AddressConcern::AddressAssociations` is automatically included into `ActiveRecord::Base` and
|
31
|
+
provides a few macros for defining associations with your app's Address model.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class Person < ApplicationRecord
|
35
|
+
belongs_to_address
|
36
|
+
end
|
37
|
+
|
38
|
+
person = Person.new
|
39
|
+
person.build_address(address: '...')
|
40
|
+
```
|
41
|
+
|
42
|
+
## Multiple addresses on same model
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class User < ApplicationRecord
|
46
|
+
belongs_to_address :shipping_address
|
47
|
+
belongs_to_address :billing_address
|
48
|
+
end
|
49
|
+
|
50
|
+
user = User.new
|
51
|
+
shipping_address = user.build_shipping_address(address: '...')
|
52
|
+
billing_address = user.build_billing_address( address: '...')
|
53
|
+
```
|
54
|
+
|
55
|
+
See "Adding an `address` association to your ActiveRecord models" section for more examples of
|
56
|
+
configuration your associations.
|
57
|
+
|
58
|
+
|
59
|
+
# Adding an `address` association to your ActiveRecord models
|
60
|
+
|
61
|
+
You can add an address association (or multiple) to any model that has an address.
|
62
|
+
|
63
|
+
You can associate with the address via a `belongs_to`, `has_one`, or `has_many` — whichever makes
|
64
|
+
the most sense for your use case.
|
65
|
+
|
66
|
+
You can either use standard ActiveRecord association macros, like this:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class Person < ApplicationRecord
|
70
|
+
belongs_to :address
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
... or use the provided macros:
|
75
|
+
|
76
|
+
## `belongs_to_address`
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class Person < ApplicationRecord
|
80
|
+
belongs_to_address
|
81
|
+
end
|
82
|
+
|
83
|
+
person = Person.new
|
84
|
+
person.build_address(address: '...')
|
85
|
+
```
|
86
|
+
|
87
|
+
If needed, you can pass a name as well as options for the `belongs_to` and (optional) inverse `has_one`
|
88
|
+
associations.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class Child < ApplicationRecord
|
92
|
+
belongs_to_address inverse: false
|
93
|
+
belongs_to_address :secret_hideout, inverse: {name: :child_for_secret_hideout}
|
94
|
+
end
|
95
|
+
|
96
|
+
child = Child.new
|
97
|
+
child.build_secret_hideout(address: '...')
|
98
|
+
```
|
99
|
+
|
100
|
+
## `has_address`
|
101
|
+
|
102
|
+
`has_address` creates a `has_one :address` association:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class Company < ApplicationRecord
|
106
|
+
has_address
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
```
|
111
|
+
company = company.new
|
112
|
+
address = company.build_address(address: '...')
|
113
|
+
```
|
114
|
+
|
115
|
+
This also adds a polymorphic `addressable` association on the Address model (not available if you're
|
116
|
+
using `belongs_to :address` on your addressable models):
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
belongs_to :addressable, :polymorphic => true
|
120
|
+
```
|
121
|
+
|
122
|
+
## `has_addresses`
|
123
|
+
|
124
|
+
`has_addresses` creates a `has_many :addresses` association:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
class User < ApplicationRecord
|
128
|
+
has_addresses
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
If you want to have several *individually accessible* addresses associated with a single model (such
|
133
|
+
as a separate shipping and billing address), you can do something like this:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
class User < ApplicationRecord
|
137
|
+
has_addresses :types => [:physical, :shipping, :billing]
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
Then you can refer to them by name, like this:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
shipping_address = user.build_shipping_address(address: 'Some address')
|
145
|
+
user.shipping_address # => shipping_address
|
146
|
+
```
|
147
|
+
|
148
|
+
Note that you aren't *limited* to only the address types you specifically list in your
|
149
|
+
`has_addresses` declaration; you can still add and retrieve other addresses using the `has_many
|
150
|
+
:addresses` association:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
vacation_address = user.addresses.build(address: 'Vacation', :address_type => 'Vacation')
|
154
|
+
user.addresses # => [shipping_address, vacation_address]
|
155
|
+
```
|
156
|
+
|
157
|
+
# Country/state database
|
158
|
+
|
159
|
+
Country/state data comes from the [`carmen`](https://github.com/carmen-ruby/carmen) gem.
|
160
|
+
|
161
|
+
- You can set the country either by using the `country=` writer (if you want to use a country name
|
162
|
+
as input in your frontend) or the `country_code=` writer (if you want to use a country code as
|
163
|
+
input). It will automatically update the other column for you and keep both of them up-to-date.
|
164
|
+
- The country name is stored in the `country` attribute (most common use case).
|
165
|
+
- Country codes can be optionally get/set via the `country_code2` (ISO 3166-1 alpha-2 codes)
|
166
|
+
(aliased as `country_code`) or `country_code3` attributes.
|
167
|
+
- Be aware that if the country you entered isn't recognized (in Carmen's database), it will be
|
168
|
+
rejected and the country field reset to nil. This should probably be considered a bug and be fixed
|
169
|
+
in a later release (using validations instead).
|
170
|
+
|
171
|
+
Other notes regarding country:
|
172
|
+
|
173
|
+
- Added some special handling of UK countries, since Carmen doesn't recognize 'England', etc. as
|
174
|
+
countries but we want to allow those country names to be stored since they may be a part of the
|
175
|
+
address you want to preserve.
|
176
|
+
|
177
|
+
# View helpers
|
178
|
+
|
179
|
+
Because this gem depends on [`carmen`](https://github.com/carmen-ruby/carmen), you have access to
|
180
|
+
its `country_select` and `state_select` helpers.
|
181
|
+
|
182
|
+
# Related Projects
|
183
|
+
|
184
|
+
(Along with some feature/API ideas that we may want to incorporate (pull requests welcome!).)
|
185
|
+
|
186
|
+
- https://github.com/ankane/mainstreet — A standard US address model for Rails
|
187
|
+
- Use `alias_attribute` to map existing field names
|
188
|
+
- Add new fields like `original_attributes` and `verification_info`
|
189
|
+
- Uses `SmartyStreets` to verify addresses (`valid?` returns false).
|
190
|
+
- [`acts_as_address` association macro](https://github.com/ankane/mainstreet/blob/master/lib/mainstreet.rb)
|
191
|
+
- [`Address` model generator](https://github.com/ankane/mainstreet/blob/master/lib/generators/mainstreet/address_generator.rb)
|
192
|
+
- `acts_as_address` could potentially be included into our `Address` model and both gems used together
|
193
|
+
|
194
|
+
- https://github.com/yrgoldteeth/whereabouts — A simple rails plugin that adds a polymorphic address model
|
195
|
+
- `has_whereabouts :location, {:geocode => true}`
|
196
|
+
- `has_whereabouts :location, {:validate => [:city, :state, :zip]}`
|
197
|
+
- [`has_whereabouts` association macro](https://github.com/yrgoldteeth/whereabouts/blob/master/lib/whereabouts_methods.rb)
|
198
|
+
- [`Address` model generator](https://github.com/yrgoldteeth/whereabouts/blob/master/lib/generators/address/templates/address.rb)
|
199
|
+
|
200
|
+
- https://github.com/wilbert/addresses — An Address engine to use Country, State, City and Neighborhood models
|
201
|
+
- Allows you use these models: `Country`, `State` (belongs to country), `City` (belongs to State), `Neighborhood` (belongs to city), `Address` (Belongs to `Neighborhood` and `City`, because neighborhood is not required)
|
202
|
+
- `address.city = Address::City.find(city_id)`
|
203
|
+
- `mount Addresses::Engine => "/addresses"`
|
204
|
+
- [`Address` model](https://github.com/wilbert/addresses/blob/master/app/models/addresses/address.rb)
|
205
|
+
|
206
|
+
- https://github.com/huerlisi/has_vcards — Rails plugin providing VCard like contact and address models and helpers
|
207
|
+
- [`Address` model](https://github.com/huerlisi/has_vcards/blob/master/app/models/has_vcards/address.rb)
|
208
|
+
|
209
|
+
Not maintained for 3+ years:
|
210
|
+
- https://github.com/mobilityhouse/acts_as_addressable — Make your models addressable
|
211
|
+
- `acts_as_addressable :postal, :billing`
|
212
|
+
- [`acts_as_addressable` association macro](https://github.com/mobilityhouse/acts_as_addressable/blob/master/lib/acts_as_addressable/addressable.rb)
|
213
|
+
- [`Address` model generator](https://github.com/mobilityhouse/acts_as_addressable/blob/master/lib/generators/acts_as_addressable/templates/address.rb)
|
214
|
+
- https://github.com/mariusz360/postally_addressable — Add postal addresses to your models
|
215
|
+
- [`has_postal_address` association macro](https://github.com/mariusz360/postally_addressable/blob/master/lib/postally_addressable/has_postal_address.rb)
|
216
|
+
- [`PostalAddress` model](https://github.com/mariusz360/postally_addressable/blob/master/app/models/postal_address.rb)
|
217
|
+
- `alias_attribute :state, :province`
|
218
|
+
- https://github.com/nybblr/somewhere — Serialized address class for use with Rails models. Like it should be.
|
219
|
+
- `address :billing, :postal_code => :zip, :include_prefix => false`
|
220
|
+
- `address.to_hash :exclude => [:country]`
|
221
|
+
- `address.to_s :country => false`
|
222
|
+
- [`Address` model](https://github.com/nybblr/somewhere/blob/master/lib/address.rb)
|
223
|
+
- [`address` association macro](https://github.com/nybblr/somewhere/blob/master/lib/somewhere.rb)
|
224
|
+
- https://github.com/rumblelabs/is_addressable
|
225
|
+
|
226
|
+
# License
|
227
|
+
|
228
|
+
Licensed under the MIT License.
|
229
|
+
|
230
|
+
See LICENSE.txt for further details.
|
231
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "address_concern/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "address_concern"
|
7
|
+
s.version = AddressConcern.version
|
8
|
+
s.authors = ["Paul Campbell", "Tyler Rick"]
|
9
|
+
s.email = ["paul@rslw.com", "tyler@tylerrick.com"]
|
10
|
+
s.homepage = %q{http://github.com/TylerRick/address_concern}
|
11
|
+
s.summary = %q{A reusable Address model for your Rails apps}
|
12
|
+
s.description = s.summary
|
13
|
+
s.licenses = ["MIT"]
|
14
|
+
|
15
|
+
s.add_dependency "rake"
|
16
|
+
s.add_dependency "cucumber"
|
17
|
+
s.add_dependency "rails", ">= 4.0"
|
18
|
+
s.add_dependency "activerecord", ">= 4.0"
|
19
|
+
s.add_dependency "activesupport", ">= 4.0"
|
20
|
+
s.add_dependency "carmen", '>= 1.1.1'
|
21
|
+
s.add_dependency "attribute_normalizer"
|
22
|
+
s.add_dependency "active_record_ignored_attributes"
|
23
|
+
s.add_dependency "facets"
|
24
|
+
|
25
|
+
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'sqlite3'
|
27
|
+
#s.add_development_dependency 'mysql2', '~>0.2.11'
|
28
|
+
|
29
|
+
s.files = `git ls-files`.split("\n")
|
30
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
31
|
+
#s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
32
|
+
s.require_paths = ["lib"]
|
33
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'address_concern'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require_relative "../spec/spec_helper"
|
11
|
+
require "pry"
|
12
|
+
Pry.start
|
@@ -0,0 +1,306 @@
|
|
1
|
+
module AddressConcern::Address
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
included do
|
4
|
+
|
5
|
+
#validates_presence_of :name
|
6
|
+
#validates_presence_of :address
|
7
|
+
#validates_presence_of :state, :if => :state_required?
|
8
|
+
#validates_presence_of :country
|
9
|
+
#validates_format_of :phone, :with => /^[0-9\-\+ ]*$/
|
10
|
+
#validates_format_of :email, :with => /^[^@]*@.*\.[^\.]*$/, :message => 'is invalid. Please enter an address in the format of you@company.com'
|
11
|
+
#validates_presence_of :phone, :message => ' is required.'
|
12
|
+
|
13
|
+
#-------------------------------------------------------------------------------------------------
|
14
|
+
normalize_attributes :name, :city, :state, :postal_code, :country
|
15
|
+
normalize_attribute :address, :with => [:cleanlines, :strip]
|
16
|
+
|
17
|
+
#-------------------------------------------------------------------------------------------------
|
18
|
+
# Country code
|
19
|
+
|
20
|
+
def country_alpha2=(code)
|
21
|
+
if code.blank?
|
22
|
+
write_attribute(:country, nil)
|
23
|
+
write_attribute(:country_alpha2, nil)
|
24
|
+
write_attribute(:country_alpha3, nil)
|
25
|
+
|
26
|
+
elsif (country = Carmen::Country.alpha_2_coded(code))
|
27
|
+
# Only set it if it's a recognized country code
|
28
|
+
write_attribute(:country, country.name)
|
29
|
+
write_attribute(:country_alpha2, code)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Aliases
|
34
|
+
def country_code
|
35
|
+
country_alpha2
|
36
|
+
end
|
37
|
+
def country_code=(code)
|
38
|
+
self.country_alpha2 = code
|
39
|
+
end
|
40
|
+
def state_code
|
41
|
+
state
|
42
|
+
end
|
43
|
+
|
44
|
+
def carmen_country
|
45
|
+
Carmen::Country.alpha_2_coded(country_alpha2)
|
46
|
+
end
|
47
|
+
|
48
|
+
def carmen_state
|
49
|
+
if (country = carmen_country)
|
50
|
+
Address.states_for_country(country).coded(state_code)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#-------------------------------------------------------------------------------------------------
|
55
|
+
# Country name
|
56
|
+
|
57
|
+
def country=(name)
|
58
|
+
if name.blank?
|
59
|
+
write_attribute(:country, nil)
|
60
|
+
write_attribute(:country_alpha2, nil)
|
61
|
+
write_attribute(:country_alpha3, nil)
|
62
|
+
else
|
63
|
+
name = recognize_country_name_alias(name)
|
64
|
+
if (country = Carmen::Country.named(name))
|
65
|
+
write_attribute(:country, country.name)
|
66
|
+
write_attribute(:country_alpha2, country.alpha_2_code)
|
67
|
+
write_attribute(:country_alpha3, country.alpha_3_code)
|
68
|
+
else
|
69
|
+
write_attribute(:country, nil)
|
70
|
+
write_attribute(:country_alpha2, nil)
|
71
|
+
write_attribute(:country_alpha3, nil)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def recognize_country_name_alias(name)
|
77
|
+
name = case name
|
78
|
+
when 'USA'
|
79
|
+
when 'The Democratic Republic of the Congo', 'Democratic Republic of the Congo'
|
80
|
+
'Congo, the Democratic Republic of the'
|
81
|
+
when 'Republic of Macedonia', 'Macedonia, Republic of', 'Macedonia'
|
82
|
+
'Macedonia, Republic of'
|
83
|
+
else
|
84
|
+
name
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# This should not be different from the value stored in the country attribute, but allows you to
|
89
|
+
# look it up just to make sure they match (or to update country field to match this).
|
90
|
+
def country_name_from_code
|
91
|
+
if (country = Carmen::Country.alpha_2_coded(country_alpha2))
|
92
|
+
country.name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Aliases
|
97
|
+
def country_name
|
98
|
+
country
|
99
|
+
end
|
100
|
+
def country_name=(name)
|
101
|
+
self.country = name
|
102
|
+
end
|
103
|
+
|
104
|
+
#════════════════════════════════════════════════════════════════════════════════════════════════════
|
105
|
+
# State/province options for country
|
106
|
+
|
107
|
+
# This is useful if want to list the state options allowed for a country in a select box and
|
108
|
+
# restrict entry to only officially listed state options.
|
109
|
+
# It is not required in the postal address for all countries, however. If you only want to show it
|
110
|
+
# if it's required in the postal address, you can make it conditional based on
|
111
|
+
# state_included_in_postal_address?.
|
112
|
+
def self.states_for_country(country)
|
113
|
+
return [] unless country
|
114
|
+
raise ArgumentError.new('expected a Carmen::Country') unless country.is_a? Carmen::Country
|
115
|
+
Carmen::RegionCollection.new(
|
116
|
+
if country.name == 'Kenya'
|
117
|
+
# https://github.com/jim/carmen/issues/227
|
118
|
+
# https://en.wikipedia.org/wiki/Provinces_of_Kenya
|
119
|
+
# Kenya's provinces were replaced by a system of counties in 2013.
|
120
|
+
# https://en.wikipedia.org/wiki/ISO_3166-2:KE confirms that they are "former" provinces.
|
121
|
+
# At the time of this writing, however, it doesn't look like Carmen has been updated to
|
122
|
+
# include the 47 counties listed under https://en.wikipedia.org/wiki/ISO_3166-2:KE.
|
123
|
+
country.subregions.typed('county')
|
124
|
+
elsif country.name == 'France'
|
125
|
+
# https://github.com/jim/carmen/issues/228
|
126
|
+
# https://en.wikipedia.org/wiki/Regions_of_France
|
127
|
+
# In 2016 what had been 27 regions was reduced to 18.
|
128
|
+
# France is divided into 18 administrative regions, including 13 metropolitan regions and 5 overseas regions.
|
129
|
+
# https://en.wikipedia.org/wiki/ISO_3166-2:FR
|
130
|
+
[]
|
131
|
+
else # Needed for New Zealand, Philippines, Indonesia, and possibly others
|
132
|
+
country.subregions.map {|_| _.subregions.any? ? _.subregions : _ }.flatten
|
133
|
+
end
|
134
|
+
)
|
135
|
+
end
|
136
|
+
def states_for_country
|
137
|
+
self.class.states_for_country(carmen_country)
|
138
|
+
end
|
139
|
+
alias_method :state_options, :states_for_country
|
140
|
+
|
141
|
+
def country_with_states?
|
142
|
+
states_for_country.any?
|
143
|
+
end
|
144
|
+
|
145
|
+
# Is the state/province required in a postal address?
|
146
|
+
# If no, perhaps you want to collect it for other reasons (like seeing which people/things are in
|
147
|
+
# the same region). Or for countries where it *may* be included in a postal address but is not
|
148
|
+
# required to be included.
|
149
|
+
def state_required_in_postal_address?
|
150
|
+
[
|
151
|
+
'Australia',
|
152
|
+
'Brazil',
|
153
|
+
'Canada',
|
154
|
+
'Mexico',
|
155
|
+
'United States',
|
156
|
+
'Italy',
|
157
|
+
'Venezuela',
|
158
|
+
].include? country_name
|
159
|
+
end
|
160
|
+
def state_possibly_included_in_postal_address?
|
161
|
+
# https://ux.stackexchange.com/questions/64665/address-form-field-for-region
|
162
|
+
# http://www.bitboost.com/ref/international-address-formats/denmark/
|
163
|
+
# http://www.bitboost.com/ref/international-address-formats/poland/
|
164
|
+
return true if state_required_in_postal_address?
|
165
|
+
return false if [
|
166
|
+
'Algeria',
|
167
|
+
'Argentina',
|
168
|
+
'Austria',
|
169
|
+
'Denmark',
|
170
|
+
'France',
|
171
|
+
'Germany',
|
172
|
+
'Indonesia',
|
173
|
+
'Ireland',
|
174
|
+
'Israel',
|
175
|
+
'Netherlands',
|
176
|
+
'New Zealand',
|
177
|
+
'Poland',
|
178
|
+
'Sweden',
|
179
|
+
'United Kingdom',
|
180
|
+
].include? country_name
|
181
|
+
# Default:
|
182
|
+
country_with_states?
|
183
|
+
end
|
184
|
+
|
185
|
+
# It's not called a "State" in all countries.
|
186
|
+
# In some countries, it could technically be multiple different types of regions:
|
187
|
+
# - In United States, it could be a state or an outlying region or a district or an APO
|
188
|
+
# - In Canada, it could be a province or a territory.
|
189
|
+
# This attempts to return the most common, expected name for this field.
|
190
|
+
# See also: https://ux.stackexchange.com/questions/64665/address-form-field-for-region
|
191
|
+
#
|
192
|
+
# To see what it should be called in all countries known to Carmen:
|
193
|
+
# Country.countries_with_states.map {|country| [country.name, Address.new(country_name: country.name).state_label] }.to_h
|
194
|
+
# => {"Afghanistan"=>"Province",
|
195
|
+
# "Armenia"=>"Province",
|
196
|
+
# "Angola"=>"Province",
|
197
|
+
# "Argentina"=>"Province",
|
198
|
+
# "Austria"=>"State",
|
199
|
+
# "Australia"=>"State",
|
200
|
+
# ...
|
201
|
+
def state_label
|
202
|
+
# In UK, it looks like they (optionally) include the *county* in their addresses. They don't actually have "states" per se.
|
203
|
+
# Reference: http://bitboost.com/ref/international-address-formats/united-kingdom/
|
204
|
+
# Could also limit to Countries (England, Scotland, Wales) and Provinces (Northern Ireland).
|
205
|
+
# Who knows. The UK's subregions are a mess.
|
206
|
+
# If allowing the full list of subregions from https://en.wikipedia.org/wiki/ISO_3166-2:GB,
|
207
|
+
# perhaps Region is a better, more inclusive term.
|
208
|
+
if country_name.in? ['United Kingdom']
|
209
|
+
'Region'
|
210
|
+
elsif state_options.any?
|
211
|
+
state_options[0].type.capitalize
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def state_name
|
216
|
+
carmen_state ? carmen_state.name : state
|
217
|
+
end
|
218
|
+
|
219
|
+
#════════════════════════════════════════════════════════════════════════════════════════════════════
|
220
|
+
|
221
|
+
def empty?
|
222
|
+
[:address, :city, :state, :postal_code, :country].all? {|_|
|
223
|
+
!self[_].present?
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
def started_filling_out?
|
228
|
+
[:address, :city, :state, :postal_code, :country].any? {|_|
|
229
|
+
self[_].present?
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
#════════════════════════════════════════════════════════════════════════════════════════════════════
|
234
|
+
# Formatting for humans
|
235
|
+
|
236
|
+
# Lines of a postal address
|
237
|
+
def lines
|
238
|
+
[
|
239
|
+
name,
|
240
|
+
address.to_s.lines.to_a,
|
241
|
+
city_line,
|
242
|
+
country_name,
|
243
|
+
].flatten.reject(&:blank?)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Used by #lines
|
247
|
+
#
|
248
|
+
# Instead of using `state` method (which is really state_code). That's fine for some countries
|
249
|
+
# like US, Canada, Australia but not other countries (presumably).
|
250
|
+
#
|
251
|
+
# TODO: Put postal code and city in a different order, as that countries conventions dictate.
|
252
|
+
# See http://bitboost.com/ref/international-address-formats/new-zealand/
|
253
|
+
#
|
254
|
+
def city_line
|
255
|
+
[
|
256
|
+
#[city, state].reject(&:blank?).join(', '),
|
257
|
+
[city, state_for_postal_address].reject(&:blank?).join(', '),
|
258
|
+
postal_code,
|
259
|
+
].reject(&:blank?).join(' ')
|
260
|
+
end
|
261
|
+
|
262
|
+
def city_state_code
|
263
|
+
[city, state].reject(&:blank?).join(', ')
|
264
|
+
end
|
265
|
+
|
266
|
+
def city_state_name
|
267
|
+
[city, state_name].reject(&:blank?).join(', ')
|
268
|
+
end
|
269
|
+
|
270
|
+
def city_state_country
|
271
|
+
[city_state_name, country_name].join(', ')
|
272
|
+
end
|
273
|
+
|
274
|
+
def state_for_postal_address
|
275
|
+
# Possibly others use a code? But seems safer to default to a name until confirmed that they use
|
276
|
+
# a code.
|
277
|
+
if country_name.in? ['United States', 'Canada', 'Australia']
|
278
|
+
state_code
|
279
|
+
elsif state_possibly_included_in_postal_address?
|
280
|
+
state_name
|
281
|
+
else
|
282
|
+
''
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
#════════════════════════════════════════════════════════════════════════════════════════════════════
|
287
|
+
# Misc. output
|
288
|
+
|
289
|
+
def parts
|
290
|
+
[
|
291
|
+
name,
|
292
|
+
address.to_s.lines.to_a,
|
293
|
+
city,
|
294
|
+
state_name,
|
295
|
+
postal_code,
|
296
|
+
country_name,
|
297
|
+
].flatten.reject(&:blank?)
|
298
|
+
end
|
299
|
+
|
300
|
+
def inspect
|
301
|
+
inspect_with([:id, :name, :address, :city, :state, :postal_code, :country], ['{', '}'])
|
302
|
+
end
|
303
|
+
|
304
|
+
#-------------------------------------------------------------------------------------------------
|
305
|
+
end
|
306
|
+
end
|