address_concern 2.1.0 → 3.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 205541c7ef9aedeb7e807bb946274c5a379edc7162592bbb2b1fd1015ce681b5
4
- data.tar.gz: 2dacfdb3da70d8ef88989ea2966e407179af9d536b8e3ae5df0826abe7ab1571
3
+ metadata.gz: bcde48cb5114abd702acfe50d80c0f9d1a384faa94b750627a0b5fe17a6d1fc4
4
+ data.tar.gz: 5b049ab69ee5882d7747b5de0a6de40ef41aca78b3a5c565e2328c0bac87fac9
5
5
  SHA512:
6
- metadata.gz: 15f9277026adf1a62dff7561386dae79d021f11eeb3f3b4c3c13ff76bb991c7893861402ad120ec7ff8a4e714fd389cb644580a1a7f2a2fedc4c273018b77c38
7
- data.tar.gz: a5ab51814edaee41313de51bf2fc7fa75b683b79d490dc26af1401b3770c292c4ffd49b0452b3a7f02b1fd49b1d48fa2e052087d8b8e9ae43126dfa6b6fc5fe6
6
+ metadata.gz: 35e52d1a95952bb73d9a6267ff4e1a59ca10b311f8e69a4906110eb09a4c21b4cd2e4c5d3c5e08899d843ab5f919a77960dcb2775f01ae729bcc8b473752f643
7
+ data.tar.gz: ac471eda04dc70ad516b9e6f79179a17b31f54ede686e504266d1c56e1da1014bb21877d27ea3c789675f6bb6ad7bef4102b5b52c3d71d0498de5bf9b0596622
data/.tool-versions ADDED
@@ -0,0 +1,2 @@
1
+ # ruby 2.7.4
2
+ ruby 3.1.2
data/Changelog.md ADDED
@@ -0,0 +1,27 @@
1
+ ## 3.0.0
2
+
3
+ Breaking changes:
4
+
5
+ - Rename `.address_attributes`, `#address_lines`, … to something less ambiguous so it's clear whether we're talking about street address or all address attributes
6
+ - Rename .address_attr_names -> .street_address_attr_names
7
+ - Rename #address_lines -> #street_address_lines
8
+
9
+ It was confusing having .address_attributes and #address_attributes referring to completely
10
+ different sets of attributes.
11
+
12
+ - Change the default value for `street_address.attributes` config to a more strict Regex pattern to avoid
13
+ matching `'foo_address'`: `column_names.grep(/^address$|^address_\d$/)`
14
+
15
+ Fixes:
16
+
17
+ - Fix `.states_for_country` to always return a `Carmen::RegionCollection` rather than sometimes a
18
+ - plain Array `[]`, so that we won't get an error if we try to call `coded` on the returned collection.
19
+
20
+ - Fix error with rails 5.2
21
+
22
+ Added:
23
+
24
+ - Add experimental support for `:on_unknown` config and state validation (added
25
+ `validate_state_for_country`)
26
+
27
+
@@ -18,9 +18,11 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency "activesupport", ">= 4.0"
19
19
  s.add_dependency "carmen", '>= 1.1.1'
20
20
  #s.add_dependency "attribute_normalizer"
21
+ s.add_dependency "zeitwerk"
21
22
 
22
23
  s.add_development_dependency 'active_record_ignored_attributes' # for be_same_as
23
24
  s.add_development_dependency 'rspec'
25
+ s.add_development_dependency 'shoulda-matchers'
24
26
  s.add_development_dependency 'sqlite3'
25
27
  #s.add_development_dependency 'mysql2', '~>0.2.11'
26
28
 
@@ -0,0 +1,6 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ # state_not_in_list: "must be one of the valid options for %{country_name}: %{states_for_country}"
6
+ state_not_in_list: "is not a valid option for %{country_name}"
File without changes
@@ -1,7 +1,7 @@
1
- require_relative '../../../lib/core_extensions/hash/reorder'
1
+ require_relative '../core_extensions/hash/reorder'
2
2
  using Hash::Reorder
3
3
 
4
- require_relative '../../../lib/core_extensions/string/cleanlines'
4
+ require_relative '../core_extensions/string/cleanlines'
5
5
  using String::Cleanlines
6
6
 
7
7
  require_relative 'inspect_base'
@@ -24,19 +24,6 @@ module Address
24
24
  }
25
25
  options = options.deep_symbolize_keys
26
26
  default_config = {
27
- state: {
28
- #normalize: false,
29
- #validate: false,
30
-
31
- code_attribute: column_for_attribute(:state_code).yield_self(&not_null)&.name ||
32
- (column_for_attribute(:state).yield_self(&not_null)&.name unless options.dig(:state, :name_attribute).to_s == 'state'),
33
-
34
- name_attribute: column_for_attribute(:state_name).yield_self(&not_null)&.name ||
35
- (column_for_attribute(:state).yield_self(&not_null)&.name unless options.dig(:state, :code_attribute).to_s == 'state'),
36
-
37
- on_unknown: ->(value, name_or_code) { },
38
- },
39
-
40
27
  country: {
41
28
  #normalize: false,
42
29
  #validate: false,
@@ -50,15 +37,29 @@ module Address
50
37
  name_attribute: column_for_attribute(:country_name).yield_self(&not_null)&.name ||
51
38
  (column_for_attribute(:country).yield_self(&not_null)&.name unless options.dig(:country, :code_attribute).to_s == 'country'),
52
39
 
53
- on_unknown: ->(value, name_or_code) { },
40
+ on_unknown: ->(record, name_or_code, value) { },
54
41
  },
55
42
 
56
- address: {
43
+ state: {
44
+ #normalize: false,
45
+ #validate: false,
46
+
47
+ code_attribute: column_for_attribute(:state_code).yield_self(&not_null)&.name ||
48
+ (column_for_attribute(:state).yield_self(&not_null)&.name unless options.dig(:state, :name_attribute).to_s == 'state'),
49
+
50
+ name_attribute: column_for_attribute(:state_name).yield_self(&not_null)&.name ||
51
+ (column_for_attribute(:state).yield_self(&not_null)&.name unless options.dig(:state, :code_attribute).to_s == 'state'),
52
+
53
+ on_unknown: ->(record, name_or_code, carmen_country, value) { },
54
+ debug_unknown: false
55
+ },
56
+
57
+ street_address: {
57
58
  #normalize: false,
58
59
  #validate: false,
59
60
 
60
61
  # Try to auto-detect address columns
61
- attributes: column_names.grep(/address$|^address_\d$/),
62
+ attributes: column_names.grep(/^address$|^address_\d$/),
62
63
  }
63
64
  }
64
65
  @acts_as_address_config = config = {
@@ -141,24 +142,25 @@ module Address
141
142
 
142
143
  #─────────────────────────────────────────────────────────────────────────────────────────────
143
144
 
144
- def address_attr_config
145
- @acts_as_address_config[:address] || {}
145
+ def street_address_attr_config
146
+ if @acts_as_address_config[:address]
147
+ raise "The :address config key has been renamed to :street_address"
148
+ end
149
+ @acts_as_address_config[:street_address] || {}
146
150
  end
147
151
 
148
- # TODO: rename to something different than the same name as #address_attributes, like
149
- # street_address_attr_names
150
- def address_attributes
151
- Array(address_attr_config[:attributes]).map(&:to_sym)
152
+ def street_address_attr_names
153
+ Array(street_address_attr_config[:attributes]).map(&:to_sym)
152
154
  end
153
155
 
154
156
  # Address line 1
155
- def address_attribute
156
- address_attributes[0]
157
+ def street_address_attribute
158
+ street_address_attr_names[0]
157
159
  end
158
160
 
159
- def multi_line_address?
160
- address_attributes.size == 1 && (
161
- column = column_for_attribute(address_attribute)
161
+ def multi_line_street_address?
162
+ street_address_attr_names.size == 1 && (
163
+ column = column_for_attribute(street_address_attribute)
162
164
  column.type == :text
163
165
  )
164
166
  end
@@ -168,7 +170,7 @@ module Address
168
170
  # AKA configured_address_attributes
169
171
  def address_attr_names
170
172
  [
171
- *address_attributes,
173
+ *street_address_attr_names,
172
174
  :city,
173
175
  state_name_attribute,
174
176
  state_code_attribute,
@@ -180,18 +182,22 @@ module Address
180
182
  end
181
183
 
182
184
  #═════════════════════════════════════════════════════════════════════════════════════════════════
183
- # Customizable validation (to add?)
185
+ # Customizable validation (part 1 of 2)
186
+ # TODO: Finish adding some optional reasonable default validations
184
187
 
185
188
  #validates_presence_of :address
186
189
  #validates_presence_of :state, if: :state_required?
187
190
  #validates_presence_of :country
188
191
 
192
+ validate :validate_state_for_country, if: -> { state_config[:validate_code] }
193
+
189
194
  #═════════════════════════════════════════════════════════════════════════════════════════════════
190
195
  # Attributes
191
196
 
192
197
  def _assign_attributes(attributes)
193
198
  attributes = attributes.symbolize_keys
194
199
  attributes = reorder_language_attributes(attributes)
200
+ attributes = attributes.stringify_keys
195
201
  super(attributes)
196
202
  end
197
203
 
@@ -215,7 +221,7 @@ module Address
215
221
 
216
222
  # TODO: automatically normalize if attribute_normalizer/normalizy gem is loaded? add a config option to opt out?
217
223
  #normalize_attributes :city, :state, :postal_code, :country
218
- #normalize_attribute *address_attributes, with: [:cleanlines, :strip]
224
+ #normalize_attribute *street_address_attr_names, with: [:cleanlines, :strip]
219
225
 
220
226
  #═════════════════════════════════════════════════════════════════════════════════════════════════
221
227
  # Country & State (Carmen + custom)
@@ -386,7 +392,7 @@ module Address
386
392
  if (country = self.class.find_carmen_country_by_code(value))
387
393
  set_country_from_carmen_country(country)
388
394
  else
389
- country_config[:on_unknown].(value, :code)
395
+ on_unknown = country_config[:on_unknown]&.(self, :code, value)
390
396
  write_attribute(self.class.country_code_attribute, value) if self.class.country_code_attribute
391
397
  end
392
398
  end
@@ -413,7 +419,7 @@ module Address
413
419
  if (country = self.class.find_carmen_country_by_name(value))
414
420
  set_country_from_carmen_country(country)
415
421
  else
416
- country_config[:on_unknown].(value, :name)
422
+ on_unknown = country_config[:on_unknown]&.(self, :name, value)
417
423
  write_attribute(self.class.country_name_attribute, value) if self.class.country_name_attribute
418
424
  end
419
425
  end
@@ -458,9 +464,15 @@ module Address
458
464
  if carmen_country && (state = self.class.find_carmen_state_by_code(carmen_country, value))
459
465
  set_state_from_carmen_state(state)
460
466
  else
461
- #puts carmen_country ? "unknown state code '#{value}'" : "can't find state without country"
462
- state_config[:on_unknown].(value, :code)
463
- write_attribute(self.class.state_code_attribute, value) if self.class.state_code_attribute
467
+ if state_config[:debug_unknown]
468
+ puts carmen_country ? "unknown state code '#{value}'. Valid options: #{states_for_country_str}" : "can't find state without country"
469
+ end
470
+ on_unknown = state_config[:on_unknown]&.(self, :code, carmen_country, value)
471
+ if on_unknown == :find_by_name && carmen_country && (state = self.class.find_carmen_state_by_name(carmen_country, value))
472
+ set_state_from_carmen_state(state)
473
+ else
474
+ write_attribute(self.class.state_code_attribute, value) if self.class.state_code_attribute
475
+ end
464
476
  end
465
477
  end
466
478
  end
@@ -491,7 +503,7 @@ module Address
491
503
  set_state_from_carmen_state(state)
492
504
  else
493
505
  #puts carmen_country ? "unknown state name '#{name}'" : "can't find state without country"
494
- state_config[:on_unknown].(value, :name)
506
+ on_unknown = state_config[:on_unknown]&.(self, :name, carmen_country, value)
495
507
  write_attribute(self.class.state_name_attribute, value) if self.class.state_name_attribute
496
508
  end
497
509
  end
@@ -515,8 +527,11 @@ module Address
515
527
  # It is not required in the postal address for all countries, however. If you only want to show it
516
528
  # if it's required in the postal address, you can make it conditional based on
517
529
  # state_included_in_postal_address?.
530
+ # @return [Carmen::RegionCollection]
518
531
  def self.states_for_country(country)
519
- return [] unless country
532
+ empty_set = Carmen::RegionCollection.new([])
533
+ return empty_set unless country
534
+
520
535
  country = find_carmen_country!(country)
521
536
 
522
537
  has_states_at_level_1 = country.subregions.any? { |region|
@@ -540,7 +555,7 @@ module Address
540
555
  # # In 2016 what had been 27 regions was reduced to 18.
541
556
  # # France is divided into 18 administrative regions, including 13 metropolitan regions and 5 overseas regions.
542
557
  # # https://en.wikipedia.org/wiki/ISO_3166-2:FR
543
- # []
558
+ # empty_set
544
559
  elsif has_states_at_level_1
545
560
  country.subregions
546
561
  else
@@ -669,18 +684,24 @@ module Address
669
684
  #════════════════════════════════════════════════════════════════════════════════════════════════════
670
685
  # Street address / Address lines
671
686
 
687
+ def street_address_attributes
688
+ attributes_slice(
689
+ *self.class.street_address_attr_names
690
+ )
691
+ end
692
+
672
693
  # Attribute alias for street address line 1
673
- #if address_attribute
674
- # unless :address == address_attribute
675
- # alias_attribute :address, :"#{address_attribute}"
694
+ #if street_address_attribute
695
+ # unless :street_address == street_address_attribute
696
+ # alias_attribute :street_address, :"#{street_address_attribute}"
676
697
  # end
677
698
  #end
678
699
 
679
- def address_lines
680
- if self.class.multi_line_address?
700
+ def street_address_lines
701
+ if self.class.multi_line_street_address?
681
702
  address.to_s.cleanlines.to_a
682
703
  else
683
- self.class.address_attributes.map do |attr_name|
704
+ self.class.street_address_attr_names.map do |attr_name|
684
705
  send attr_name
685
706
  end
686
707
  end
@@ -693,7 +714,7 @@ module Address
693
714
  def lines
694
715
  [
695
716
  #name,
696
- *address_lines,
717
+ *street_address_lines,
697
718
  city_line,
698
719
  country_name,
699
720
  ].reject(&:blank?)
@@ -746,7 +767,7 @@ module Address
746
767
  def parts
747
768
  [
748
769
  #name,
749
- *address_lines,
770
+ *street_address_lines,
750
771
  city,
751
772
  state_name,
752
773
  postal_code,
@@ -775,6 +796,33 @@ module Address
775
796
  end
776
797
 
777
798
  #─────────────────────────────────────────────────────────────────────────────────────────────────
799
+ end # included do
800
+
801
+ #═════════════════════════════════════════════════════════════════════════════════════════════════
802
+ # Customizable validation (part 2 of 2)
803
+ # Defining here rather than in included block, so that it is actually defined on the module, which
804
+ # gives the consumer more flexibility on how to reuse the validation code. You can, for example,
805
+ # do this:
806
+ #
807
+ # validate \
808
+ # def validate_state_for_country
809
+ # return unless addressable.is_a?(User)
810
+ #
811
+ # super
812
+ # end
813
+
814
+ def validate_state_for_country
815
+ return unless country_with_states?
816
+ return unless state_code
817
+ return if states_for_country.map(&:code).include? state_code
818
+
819
+ errors.add self.class.state_code_attribute, :state_not_in_list, country_name: country_name, states_for_country: states_for_country_str
820
+ # puts %(errors.messages=\n#{(errors.messages).pretty_inspect.indent(4)})
821
+ end
822
+
823
+ def states_for_country_str
824
+ return unless country_with_states?
825
+ states_for_country.map(&:code).join(', ')
778
826
  end
779
827
  end
780
828
  end
@@ -4,15 +4,9 @@ module AddressConcern
4
4
  class Engine < ::Rails::Engine
5
5
  engine_name 'address_concern'
6
6
 
7
- config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
8
- config.eager_load_paths += Dir["#{config.root}/app/models/concerns"]
9
-
10
7
  initializer 'address_concern.active_record' do |app|
11
8
  ActiveSupport.on_load :active_record do
12
- # These are currently required from lib/address_concern.rb
13
- AddressConcern::Address::Base
14
- AddressConcern::AddressAssociations
15
- # ActiveRecord::Base.extend(AddressConcern::Address::Base)
9
+ #
16
10
  end
17
11
  end
18
12
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AddressConcern
4
+
4
5
  module InspectBase
5
6
  def inspect_base(*_items, class: true, id: true)
6
7
  items = _items.map { |item|
@@ -1,5 +1,5 @@
1
1
  module AddressConcern
2
2
  def self.version
3
- "2.1.0"
3
+ "3.0.0"
4
4
  end
5
5
  end
@@ -2,11 +2,33 @@ require 'rails'
2
2
  require 'carmen'
3
3
  require 'active_record'
4
4
 
5
- Carmen.i18n_backend.append_locale_path File.join(File.dirname(__FILE__), '../config/locale/overlay/en')
5
+ Carmen.i18n_backend.append_locale_path File.join(File.dirname(__FILE__), '../config/locales/overlay/en')
6
+
7
+ require 'zeitwerk'
8
+ loader = Zeitwerk::Loader.for_gem
9
+ # Note: Because this gem has/is an engine, its "engine files" (under app) are autoload managed by
10
+ # the parent app. And you can't have more than one loader managing the same root. Therefore we can't
11
+ # do this:
12
+ # loader.push_dir("#{__dir__}/../app/models")
13
+ # That is one reason, the models/concerns have been moved to lib, where it won't conflict with Rails
14
+ # app's loader.
15
+ loader.ignore("#{__dir__}/address_concern/attribute_normalizer.rb")
16
+ loader.ignore("#{__dir__}/address_concern/version.rb")
17
+ loader.ignore("#{__dir__}/core_extensions")
18
+ loader.ignore("#{__dir__}/generators")
19
+ loader.setup
6
20
 
7
21
  require 'address_concern/version'
8
- require 'address_concern/attribute_normalizer'
9
- require 'address_concern/engine'
22
+ #pp loader.autoloads
23
+ loader.eager_load
24
+ #require_relative '../app/models/address_concern/address'
10
25
 
11
- require_relative '../app/models/concerns/address'
12
- require_relative '../app/models/concerns/address_associations'
26
+ # When used in a Rails app, this isn't needed because the engine will add its locale load paths, but
27
+ # when not using Rails, including from our tests, the engine isn't loaded.
28
+ I18n.load_path.unshift(
29
+ *Dir.glob(
30
+ x=File.expand_path(
31
+ File.join(%w[.. config locales *.yml]), File.dirname(__FILE__)
32
+ )
33
+ )
34
+ )
@@ -50,15 +50,15 @@ describe 'acts_as_address' do
50
50
  describe 'address lines' do
51
51
  describe Address do
52
52
  it do
53
- expect(klass.multi_line_address?).to eq true
54
- expect(klass.address_attributes).to eq [:address]
53
+ expect(klass.multi_line_street_address?).to eq true
54
+ expect(klass.street_address_attr_names).to eq [:address]
55
55
  end
56
56
  end
57
57
 
58
58
  describe AddressWithSeparateAddressColumns do
59
59
  it do
60
- expect(klass.multi_line_address?).to eq false
61
- expect(klass.address_attributes).to eq [:address_1, :address_2, :address_3]
60
+ expect(klass.multi_line_street_address?).to eq false
61
+ expect(klass.street_address_attr_names).to eq [:address_1, :address_2, :address_3]
62
62
  end
63
63
  end
64
64
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Address do
3
+ describe Address, type: :model do
4
4
  def klass
5
5
  described_class
6
6
  end
@@ -213,16 +213,74 @@ describe Address do
213
213
 
214
214
  #═════════════════════════════════════════════════════════════════════════════════════════════════
215
215
 
216
- describe 'setting state by name' do
217
- context "simple" do
218
- subject { Address.new(state: 'FL', country_name: 'United States') }
219
- it { expect(subject.state_code).to eq('FL') }
216
+ describe 'setting state' do
217
+ # Uses find_carmen_state, which finds by name, falling back to finding by code.
218
+ describe 'setting state_name_attribute' do
219
+ before {
220
+ expect(Address.state_code_attribute).to eq :state_code
221
+ expect(Address.state_name_attribute).to eq :state
222
+ }
223
+
224
+ context "setting state_name_attribute to code" do
225
+ subject { Address.new(state: 'FL', country_name: 'United States') }
226
+ it { expect(subject.state_code).to eq('FL') }
227
+ end
228
+
229
+ context "setting state_name_attribute to name" do
230
+ subject { Address.new(state: 'Florida', country_name: 'United States') }
231
+ it { expect(subject.state_code).to eq('FL') }
232
+ end
220
233
  end
221
234
 
222
- context "setting to name instead of code" do
223
- subject { Address.new(state: 'Florida', country_name: 'United States') }
224
- # Uses find_carmen_state, which finds by name, falling back to finding by code.
225
- it { expect(subject.state_code).to eq('FL') }
235
+ # Unlike {state_name_attribute}=, which falls back to finding by code, {state_code_attribute}=
236
+ # _only_ looks up by code by default.
237
+ describe 'setting state_code_attribute' do
238
+ subject { Address.new(state_code: input, country_name: 'United States') }
239
+
240
+ before {
241
+ expect(Address.state_code_attribute).to eq :state_code
242
+ expect(Address.state_name_attribute).to eq :state
243
+
244
+ }
245
+ around(:example) { |example|
246
+ validate_code = Address.state_config[:validate_code]
247
+ Address.state_config[:validate_code] = true
248
+ example.run
249
+ Address.state_config[:validate_code] = validate_code
250
+ }
251
+
252
+ context "setting state_code_attribute to code" do
253
+ let(:input) { 'FL' }
254
+ it { expect(subject.state_code).to eq(input) }
255
+ it { expect(subject.carmen_state&.code).to eq('FL') }
256
+ it { expect(subject).to allow_values(input).for(:state_code) }
257
+ end
258
+
259
+ context "setting state_code_attribute to name: doesn't find by default" do
260
+ let(:input) { 'Florida' }
261
+ subject { Address.new(state_code: input, country_name: 'United States') }
262
+ it { expect(subject.state_code).to eq(input) }
263
+ it { expect(subject.carmen_state&.code).to eq(nil) }
264
+ it { expect(subject).to_not allow_values(input).for(:state_code).with_message('is not a valid option for United States') }
265
+
266
+ context "when state_config[:on_unknown] returns :find_by_name" do
267
+ before {
268
+ @orig = Address.state_config[:on_unknown]
269
+ Address.state_config[:on_unknown] = Proc.new { :find_by_name }
270
+ }
271
+ after { Address.state_config[:on_unknown] = @orig }
272
+ it { expect(subject.carmen_state&.code).to eq('FL') }
273
+ it { expect(subject.state_code).to eq('FL') }
274
+ it { expect(subject).to allow_values(input).for(:state_code) }
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ #═════════════════════════════════════════════════════════════════════════════════════════════════
281
+ describe 'validations' do
282
+ it do
283
+ expect(AddressConcern::Address.instance_method(:validate_state_for_country)).to be_a UnboundMethod
226
284
  end
227
285
  end
228
286
 
@@ -254,14 +312,14 @@ describe Address do
254
312
  describe 'address, address_lines' do
255
313
  describe Address do
256
314
  it do
257
- expect(klass.multi_line_address?).to eq true
315
+ expect(klass.multi_line_street_address?).to eq true
258
316
 
259
317
  address = klass.new(address: str = 'Line 1')
260
318
  expect(address.address).to eq str
261
319
 
262
320
  address = klass.new(address: str = "Line 1\nLine 2\nLine 3")
263
321
  expect(address.address).to eq str
264
- expect(address.address_lines).to eq [
322
+ expect(address.street_address_lines).to eq [
265
323
  'Line 1',
266
324
  'Line 2',
267
325
  'Line 3',
@@ -271,7 +329,7 @@ describe Address do
271
329
 
272
330
  describe AddressWithSeparateAddressColumns do
273
331
  it do
274
- expect(klass.multi_line_address?).to eq false
332
+ expect(klass.multi_line_street_address?).to eq false
275
333
 
276
334
  address = klass.new(
277
335
  address_1: 'Line 1',
@@ -281,7 +339,7 @@ describe Address do
281
339
  expect(address.address_1).to eq 'Line 1'
282
340
  expect(address.address_2).to eq 'Line 2'
283
341
  expect(address.address_3).to eq 'Line 3'
284
- expect(address.address_lines).to eq [
342
+ expect(address.street_address_lines).to eq [
285
343
  'Line 1',
286
344
  'Line 2',
287
345
  'Line 3',
@@ -368,6 +426,7 @@ describe Address do
368
426
  end
369
427
  context "when address has a state name instead of code entered for state_name, and state is for different country" do
370
428
  let(:user) { User.create }
429
+ # Internally, it sees: unknown state code 'FL'
371
430
  subject { user.build_physical_address(address: '123', city: 'Ocala', state_code: 'FL', country_name: 'Denmark') }
372
431
  it do
373
432
  expect(subject.state_code). to eq('FL')
@@ -412,6 +471,7 @@ describe Address do
412
471
  it { expect(Address.new(country: 'United States').states_for_country.map(&:name)).to include 'Puerto Rico' }
413
472
  it { expect(Address.new(country: 'South Africa').states_for_country.map(&:name)).to include 'Mpumalanga' }
414
473
  it { expect(Address.new(country: 'Kenya').states_for_country.typed('province')).to be_empty }
474
+ it { expect(Address.new(country: 'Kenya').states_for_country.typed('province')).to be_empty }
415
475
  # At the time of this writing, it doesn't look like Carmen has been updated to
416
476
  # include the 47 counties listed under https://en.wikipedia.org/wiki/ISO_3166-2:KE.
417
477
  #it { Address.new(country: 'Kenya').states_for_country.map(&:name).should include 'Nyeri' }
@@ -448,6 +508,26 @@ describe Address do
448
508
  it { expect(Address.new(country_name: 'France').state_options.map(&:name)).to include 'Auvergne-Rhône-Alpes' }
449
509
  #it { Address.new(country: 'France').state_options.size.should eq 18 }
450
510
  #it { Address.new(country: 'France').state_options.map(&:name).should include 'Auvergne-Rhône-Alpes' }
511
+
512
+ context 'for a country without states data (Aruba)' do
513
+ subject { Address.new(country_name: 'Aruba') }
514
+ it do
515
+ expect(subject.states_for_country).to be_empty
516
+ expect(subject.states_for_country).to be_a Carmen::RegionCollection
517
+ expect(subject.states_for_country.coded('Roo')).to eq nil
518
+ expect(subject.country_with_states?).to eq false
519
+ end
520
+ end
521
+
522
+ context 'with an invalid country code (ZZ)' do
523
+ subject { Address.new(country_code: 'ZZ') }
524
+ it do
525
+ expect(subject.states_for_country).to be_empty
526
+ expect(subject.states_for_country).to be_a Carmen::RegionCollection
527
+ expect(subject.states_for_country.coded('Roo')).to eq nil
528
+ expect(subject.country_with_states?).to eq false
529
+ end
530
+ end
451
531
  end
452
532
 
453
533
  #═════════════════════════════════════════════════════════════════════════════════════════════════
@@ -0,0 +1,8 @@
1
+ Shoulda::Matchers.configure do |config|
2
+ config.integrate do |with|
3
+ with.test_framework :rspec
4
+
5
+ with.library :active_record
6
+ with.library :active_model
7
+ end
8
+ 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.1.0
4
+ version: 3.0.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-04-20 00:00:00.000000000 Z
12
+ date: 2022-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -81,6 +81,20 @@ dependencies:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: 1.1.1
84
+ - !ruby/object:Gem::Dependency
85
+ name: zeitwerk
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'
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: active_record_ignored_attributes
86
100
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +123,20 @@ dependencies:
109
123
  - - ">="
110
124
  - !ruby/object:Gem::Version
111
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: shoulda-matchers
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
112
140
  - !ruby/object:Gem::Dependency
113
141
  name: sqlite3
114
142
  requirement: !ruby/object:Gem::Requirement
@@ -134,24 +162,26 @@ files:
134
162
  - ".document"
135
163
  - ".gitignore"
136
164
  - ".rspec"
137
- - ".ruby-version"
165
+ - ".tool-versions"
138
166
  - CHANGELOG
167
+ - Changelog.md
139
168
  - Gemfile
140
169
  - LICENSE.txt
141
170
  - Rakefile
142
171
  - Readme.md
143
172
  - VERSION
144
173
  - 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
149
174
  - bin/console
150
175
  - config/address_concern.rb
151
- - config/locale/overlay/en/id.yml
176
+ - config/locales/en.yml
177
+ - config/locales/overlay/en/id.yml
152
178
  - lib/address_concern.rb
179
+ - lib/address_concern/address.rb
180
+ - lib/address_concern/address_associations.rb
153
181
  - lib/address_concern/attribute_normalizer.rb
182
+ - lib/address_concern/attributes_slice.rb
154
183
  - lib/address_concern/engine.rb
184
+ - lib/address_concern/inspect_base.rb
155
185
  - lib/address_concern/version.rb
156
186
  - lib/core_extensions/hash/reorder.rb
157
187
  - lib/core_extensions/string/cleanlines.rb
@@ -174,6 +204,7 @@ files:
174
204
  - spec/support/models/employee.rb
175
205
  - spec/support/models/user.rb
176
206
  - spec/support/schema.rb
207
+ - spec/support/shoulda_matchers.rb
177
208
  - tmp/.gitkeep
178
209
  homepage: http://github.com/TylerRick/address_concern
179
210
  licenses:
@@ -194,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
225
  - !ruby/object:Gem::Version
195
226
  version: '0'
196
227
  requirements: []
197
- rubygems_version: 3.3.3
228
+ rubygems_version: 3.3.7
198
229
  signing_key:
199
230
  specification_version: 4
200
231
  summary: A reusable Address model for your Rails apps
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- ruby-2.7.4