Skip to content

Commit

Permalink
Split ComparisonHelper into FieldComparison classes for each fields
Browse files Browse the repository at this point in the history
  • Loading branch information
lloydsal committed Feb 1, 2024
1 parent 438cf72 commit 90bc359
Show file tree
Hide file tree
Showing 16 changed files with 538 additions and 356 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- Address TestCase#fixture_path deprecation [#94](https://github.com/Shopify/atlas_engine/pull/94)
- Address parser test cleanup [#92](https://github.com/Shopify/atlas_engine/pull/92)
- Split field comparison logic into their respective classes [#93](https://github.com/Shopify/atlas_engine/pull/93)
- Improved validation suggestions for Luxembourg [#82](https://github.com/Shopify/atlas_engine/pull/82)
- Store a duplicate of Result in ConcernRecord [#85](https://github.com/Shopify/atlas_engine/pull/85)
- Introduce parser and city exclusion for South Korea [#79](https://github.com/Shopify/atlas_engine/pull/79)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def apply?(session, candidate, address_comparison)
address_comparison.building_comparison.nil? ||
address_comparison.building_comparison.candidate_ranges.empty?

!address_comparison.street_comparison.match? || !address_comparison.building_comparison.match?
!T.must(address_comparison.street_comparison).match? ||
!T.must(address_comparison.building_comparison).match?
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@ class AddressComparison
extend T::Sig
include Comparable

attr_reader :comparison_helper

delegate :street_comparison,
:city_comparison,
:province_code_comparison,
:zip_comparison,
:building_comparison,
to: :comparison_helper

sig { params(address: AbstractAddress, candidate: Candidate, datastore: DatastoreBase).void }
def initialize(address:, candidate:, datastore:)
@comparison_helper = ComparisonHelper.new(address:, candidate:, datastore:)
@street_comparison = StreetComparison.new(address: address, candidate: candidate, datastore: datastore)
@city_comparison = CityComparison.new(address: address, candidate: candidate, datastore: datastore)
@province_code_comparison = ProvinceCodeComparison.new(
address: address,
candidate: candidate,
datastore: datastore,
)
@zip_comparison = ZipComparison.new(address: address, candidate: candidate, datastore: datastore)
@building_comparison = BuildingComparison.new(address: address, candidate: candidate, datastore: datastore)
end

sig { params(other: AddressComparison).returns(Integer) }
Expand All @@ -45,6 +44,31 @@ def potential_match?
street_comparison.nil? || T.must(street_comparison).potential_match?
end

sig { returns(T.nilable(Token::Sequence::Comparison)) }
def zip_comparison
@zip_comparison.compare
end

sig { returns(T.nilable(Token::Sequence::Comparison)) }
def street_comparison
@street_comparison.compare
end

sig { returns(T.nilable(Token::Sequence::Comparison)) }
def city_comparison
@city_comparison.compare
end

sig { returns(T.nilable(Token::Sequence::Comparison)) }
def province_code_comparison
@province_code_comparison.compare
end

sig { returns(NumberComparison) }
def building_comparison
@building_comparison.compare
end

protected

sig do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# typed: true
# frozen_string_literal: true

module AtlasEngine
module AddressValidation
module Validators
module FullAddress
class BuildingComparison < FieldComparisonBase
extend T::Sig

sig { override.returns(T.nilable(NumberComparison)) }
def compare
@building_comparison ||= NumberComparison.new(
numbers: datastore.parsings.potential_building_numbers,
candidate_ranges: building_ranges_from_candidate(candidate),
)
end

private

sig { params(candidate: Candidate).returns(T::Array[AddressNumberRange]) }
def building_ranges_from_candidate(candidate)
building_and_unit_ranges = candidate.component(:building_and_unit_ranges)&.value
return [] if building_and_unit_ranges.blank?

building_ranges = JSON.parse(building_and_unit_ranges).keys
building_ranges.map { |building_range| AddressNumberRange.new(range_string: building_range) }
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# typed: true
# frozen_string_literal: true

module AtlasEngine
module AddressValidation
module Validators
module FullAddress
class CityComparison < FieldComparisonBase
extend T::Sig

sig { override.returns(T.nilable(Token::Sequence::Comparison)) }
def compare
return @city_comparison if defined?(@city_comparison)

@city_comparison = best_comparison(
datastore.fetch_city_sequence,
T.must(candidate.component(:city)).sequences,
field_policy(:city),
)
end
end
end
end
end
end

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# typed: true
# frozen_string_literal: true

module AtlasEngine
module AddressValidation
module Validators
module FullAddress
class FieldComparisonBase
extend T::Sig
extend T::Helpers

abstract!

sig { params(address: AbstractAddress, candidate: Candidate, datastore: DatastoreBase).void }
def initialize(address:, candidate:, datastore:)
@address = address
@datastore = datastore
@candidate = candidate
end

sig { abstract.returns(T.any(T.nilable(Token::Sequence::Comparison), T.nilable(NumberComparison))) }
def compare; end

private

sig { returns(AbstractAddress) }
attr_reader :address

sig { returns(DatastoreBase) }
attr_reader :datastore

sig { returns(Candidate) }
attr_reader :candidate

sig do
params(
sequence: Token::Sequence,
component_sequences: T::Array[Token::Sequence],
comparison_policy: Token::Sequence::ComparisonPolicy,
).returns(T.nilable(Token::Sequence::Comparison))
end
def best_comparison(
sequence,
component_sequences,
comparison_policy = Token::Sequence::ComparisonPolicy::DEFAULT_POLICY
)
component_sequences.map do |component_sequence|
Token::Sequence::Comparator.new(
left_sequence: sequence,
right_sequence: component_sequence,
comparison_policy:,
).compare
end.min_by.with_index do |comparison, index|
# ruby's `min` and `sort` methods are not stable
# so we need to prefer the leftmost comparison when two comparisons are equivalent
[comparison, index]
end
end

sig { params(field: Symbol).returns(Token::Sequence::ComparisonPolicy) }
def field_policy(field)
datastore.country_profile.validation.comparison_policy(field)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# typed: true
# frozen_string_literal: true

module AtlasEngine
module AddressValidation
module Validators
module FullAddress
class ProvinceCodeComparison < FieldComparisonBase
extend T::Sig

sig { override.returns(T.nilable(Token::Sequence::Comparison)) }
def compare
return @province_code_comparison if defined?(@province_code_comparison)

normalized_session_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
country_code: address.country_code,
province_code: address.province_code,
)
normalized_candidate_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
country_code: T.must(candidate.component(:country_code)).value,
province_code: T.must(candidate.component(:province_code)).value,
)

@province_code_comparison = best_comparison(
Token::Sequence.from_string(normalized_session_province_code),
[Token::Sequence.from_string(normalized_candidate_province_code)],
field_policy(:province_code),
)
end
end
end
end
end
end

0 comments on commit 90bc359

Please sign in to comment.