address_concern 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/spec/spec_helper.rb CHANGED
@@ -30,12 +30,8 @@ CreateAddresses.up
30
30
 
31
31
  require 'rspec'
32
32
 
33
- require 'active_record_ignored_attributes/matchers'
34
-
35
-
36
33
  RSpec.configure do |config|
37
- config.include AttributeNormalizer::RSpecMatcher #, :type => :models
38
- config.expect_with(:rspec) { |c| c.syntax = :should }
34
+ #config.include AttributeNormalizer::RSpecMatcher #, :type => :models
39
35
  config.example_status_persistence_file_path = "tmp/rspec_status.txt"
40
36
  end
41
37
 
@@ -1,3 +1,4 @@
1
1
  class Address < ApplicationRecord
2
- include AddressConcern::Address
2
+ #normalize_attributes :name
3
+ acts_as_address
3
4
  end
@@ -0,0 +1,11 @@
1
+ class AddressCustomAttrNames < ApplicationRecord
2
+ self.table_name = 'addresses'
3
+
4
+ acts_as_address(
5
+ country: {
6
+ name_attribute: 'country',
7
+ code_attribute: 'country_code',
8
+ carmen_code: 'code', # same as 'alpha_2_code'
9
+ }
10
+ )
11
+ end
@@ -0,0 +1,12 @@
1
+ class AddressWithCodeOnly < ApplicationRecord
2
+ self.table_name = 'address_with_code_or_name_only'
3
+
4
+ acts_as_address(
5
+ state: {
6
+ code_attribute: 'state',
7
+ },
8
+ country: {
9
+ code_attribute: 'country'
10
+ },
11
+ )
12
+ end
@@ -0,0 +1,5 @@
1
+ class AddressWithNameOnly < ApplicationRecord
2
+ self.table_name = 'address_with_code_or_name_only'
3
+
4
+ acts_as_address
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddressWithSeparateAddressColumns < ApplicationRecord
2
+ self.table_name = 'address_with_separate_address_columns'
3
+
4
+ acts_as_address
5
+ end
@@ -1,3 +1,8 @@
1
1
  class User < ApplicationRecord
2
- has_addresses :types => [:physical, :shipping, :billing]
2
+ # Could also do:
3
+ # has_addresses [:physical, :shipping, :billing]
4
+ has_addresses
5
+ has_address :physical
6
+ has_address :shipping
7
+ has_address :billing
3
8
  end
@@ -1,4 +1,26 @@
1
1
  ActiveRecord::Schema.define do
2
+ # In order to avoid duplication, and to ensure that the template migration is valid, the schema for :addresses table can be found in lib/generators/address_concern/templates/migration.rb
3
+
4
+ create_table :address_with_code_or_name_only do |t|
5
+ t.text :address
6
+ t.string :city
7
+ t.string :state
8
+ t.string :postal_code
9
+ t.string :country
10
+ t.timestamps
11
+ end
12
+
13
+ create_table :address_with_separate_address_columns do |t|
14
+ t.string :address_1
15
+ t.string :address_2
16
+ t.string :address_3
17
+ t.string :city
18
+ t.string :state
19
+ t.string :postal_code
20
+ t.string :country
21
+ t.timestamps
22
+ end
23
+
2
24
  create_table :users, :force => true do |t|
3
25
  t.string :name
4
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: address_concern
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Campbell
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-03-23 00:00:00.000000000 Z
12
+ date: 2022-04-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -81,20 +81,6 @@ dependencies:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: 1.1.1
84
- - !ruby/object:Gem::Dependency
85
- name: attribute_normalizer
86
- requirement: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: '0'
91
- type: :runtime
92
- prerelease: false
93
- version_requirements: !ruby/object:Gem::Requirement
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- version: '0'
98
84
  - !ruby/object:Gem::Dependency
99
85
  name: active_record_ignored_attributes
100
86
  requirement: !ruby/object:Gem::Requirement
@@ -102,21 +88,7 @@ dependencies:
102
88
  - - ">="
103
89
  - !ruby/object:Gem::Version
104
90
  version: '0'
105
- type: :runtime
106
- prerelease: false
107
- version_requirements: !ruby/object:Gem::Requirement
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- version: '0'
112
- - !ruby/object:Gem::Dependency
113
- name: facets
114
- requirement: !ruby/object:Gem::Requirement
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- version: '0'
119
- type: :runtime
91
+ type: :development
120
92
  prerelease: false
121
93
  version_requirements: !ruby/object:Gem::Requirement
122
94
  requirements:
@@ -170,21 +142,32 @@ files:
170
142
  - Readme.md
171
143
  - VERSION
172
144
  - address_concern.gemspec
145
+ - app/models/concerns/address.rb
146
+ - app/models/concerns/address_associations.rb
147
+ - app/models/concerns/attributes_slice.rb
148
+ - app/models/concerns/inspect_base.rb
173
149
  - bin/console
150
+ - config/address_concern.rb
174
151
  - config/locale/overlay/en/id.yml
175
152
  - lib/address_concern.rb
176
- - lib/address_concern/address.rb
177
- - lib/address_concern/address_associations.rb
178
153
  - lib/address_concern/attribute_normalizer.rb
154
+ - lib/address_concern/engine.rb
179
155
  - lib/address_concern/version.rb
156
+ - lib/core_extensions/hash/reorder.rb
157
+ - lib/core_extensions/string/cleanlines.rb
180
158
  - lib/generators/address_concern/install_generator.rb
181
159
  - lib/generators/address_concern/templates/configurable.yml
182
160
  - lib/generators/address_concern/templates/migration.rb
161
+ - spec/models/acts_as_address_spec.rb
183
162
  - spec/models/address_spec.rb
184
163
  - spec/spec_helper.rb
185
164
  - spec/support/database.mysql2.yml
186
165
  - spec/support/database.sqlite3.yml
187
166
  - spec/support/models/address.rb
167
+ - spec/support/models/address_custom_attr_names.rb
168
+ - spec/support/models/address_with_code_only.rb
169
+ - spec/support/models/address_with_name_only.rb
170
+ - spec/support/models/address_with_separate_address_columns.rb
188
171
  - spec/support/models/application_record.rb
189
172
  - spec/support/models/child.rb
190
173
  - spec/support/models/company.rb
@@ -211,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
194
  - !ruby/object:Gem::Version
212
195
  version: '0'
213
196
  requirements: []
214
- rubygems_version: 3.1.6
197
+ rubygems_version: 3.3.3
215
198
  signing_key:
216
199
  specification_version: 4
217
200
  summary: A reusable Address model for your Rails apps
@@ -1,306 +0,0 @@
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