address_concern 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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