postman_paf 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/gem-push.yml +48 -0
- data/.github/workflows/gem-test.yml +22 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +36 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +174 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/postman_paf/converter.rb +65 -0
- data/lib/postman_paf/exceptions/exceptions.rb +29 -0
- data/lib/postman_paf/exceptions/last_part_exceptions.rb +36 -0
- data/lib/postman_paf/exceptions/rule_3_exceptions.rb +35 -0
- data/lib/postman_paf/exceptions/rule_5_and_7_exceptions.rb +24 -0
- data/lib/postman_paf/exceptions/rule_6_exceptions.rb +48 -0
- data/lib/postman_paf/exceptions/which_exception.rb +103 -0
- data/lib/postman_paf/printable_address.rb +35 -0
- data/lib/postman_paf/rules/address_builder.rb +30 -0
- data/lib/postman_paf/rules/building_number.rb +35 -0
- data/lib/postman_paf/rules/rule_1.rb +27 -0
- data/lib/postman_paf/rules/rule_2.rb +23 -0
- data/lib/postman_paf/rules/rule_3.rb +45 -0
- data/lib/postman_paf/rules/rule_4.rb +24 -0
- data/lib/postman_paf/rules/rule_5.rb +35 -0
- data/lib/postman_paf/rules/rule_6.rb +69 -0
- data/lib/postman_paf/rules/rule_7.rb +33 -0
- data/lib/postman_paf/rules/rules.rb +71 -0
- data/lib/postman_paf/rules/which_rule.rb +40 -0
- data/lib/postman_paf/validator.rb +65 -0
- data/lib/postman_paf/version.rb +5 -0
- data/lib/postman_paf.rb +32 -0
- data/postman_paf.gemspec +53 -0
- metadata +210 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Exceptions
|
5
|
+
module WhichException
|
6
|
+
RULE6_PREMISE_ELEMENTS = %w[buildingName subBuildingName].freeze
|
7
|
+
|
8
|
+
# Checks if the whole buildingName String contains an Exception I,
|
9
|
+
# Exception II or Exception III. Then checks if last part of the
|
10
|
+
# buildingName String (AKA numeric part) contains an exception.
|
11
|
+
# Finally checks if the whole subBuildingName String contains an
|
12
|
+
# Exception I, Exception II or Exception III.
|
13
|
+
# @param paf_address [Hash] to be converted.
|
14
|
+
# @return [Symbol] the Rule 6 conversion exception(s) to apply to
|
15
|
+
# the address based on criteria met.
|
16
|
+
def self.exception_to_apply_rule6(paf_address:)
|
17
|
+
RULE6_PREMISE_ELEMENTS.each_with_object([]) do |premise_element, exceptions|
|
18
|
+
exception = if contains_digit_as_first_and_last?(paf_address[premise_element])
|
19
|
+
:exception_i
|
20
|
+
elsif contains_digit_as_first_and_penultimate_and_alpha_as_last?(paf_address[premise_element])
|
21
|
+
:exception_ii
|
22
|
+
elsif contains_one_alpha_character?(paf_address[premise_element])
|
23
|
+
:exception_iii
|
24
|
+
elsif premise_element == BUILDING_NAME
|
25
|
+
# Checks buildingName ONLY for numeric part exception - no numeric part
|
26
|
+
# exceptions have been found for subBuildingName in test data.
|
27
|
+
building_name_parts = paf_address[BUILDING_NAME].split(' ')
|
28
|
+
if building_name_parts.size > 1
|
29
|
+
last_part = building_name_parts.last
|
30
|
+
if last_part.match?(/\d/)
|
31
|
+
PostmanPAF::Exceptions::WhichException.last_part_exception_to_apply(paf_address: paf_address, last_part: last_part)
|
32
|
+
else
|
33
|
+
:no_exception
|
34
|
+
end
|
35
|
+
else
|
36
|
+
:no_exception
|
37
|
+
end
|
38
|
+
else
|
39
|
+
:no_exception
|
40
|
+
end
|
41
|
+
|
42
|
+
premise_element_exception = "#{SimpleSymbolize.snakeize(premise_element)}_#{exception}".to_sym
|
43
|
+
exceptions << premise_element_exception
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Exceptions
|
5
|
+
module WhichException
|
6
|
+
# Exception I.
|
7
|
+
# @param address_data [String] buildingName or subBuildingName data.
|
8
|
+
# @return [Boolean] if first and last characters are numeric.
|
9
|
+
# @example
|
10
|
+
# contains_digit_as_first_and_last?("1") #=> true
|
11
|
+
# contains_digit_as_first_and_last?("1-2") #=> true
|
12
|
+
# contains_digit_as_first_and_last?("1/2") #=> true
|
13
|
+
# contains_digit_as_first_and_last?("1 Example House 2") #=> true
|
14
|
+
# contains_digit_as_first_and_last?("1 Example House") #=> false
|
15
|
+
def self.contains_digit_as_first_and_last?(address_data)
|
16
|
+
address_data.match?(/^\d(.*\d)?$/)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Exception II.
|
20
|
+
# @param address_data [String] buildingName or subBuildingName data.
|
21
|
+
# @return [Boolean] if first and penultimate characters are numeric and
|
22
|
+
# last character is alphabetic. Case insensitive.
|
23
|
+
# @example
|
24
|
+
# contains_digit_as_first_and_penultimate_and_alpha_as_last?("1A") #=> true
|
25
|
+
# contains_digit_as_first_and_penultimate_and_alpha_as_last?("12A") #=> true
|
26
|
+
# contains_digit_as_first_and_penultimate_and_alpha_as_last?("1 Example 2A") #=> true
|
27
|
+
# contains_digit_as_first_and_penultimate_and_alpha_as_last?("1") #=> false
|
28
|
+
# contains_digit_as_first_and_penultimate_and_alpha_as_last?("15 A2") #=> false
|
29
|
+
def self.contains_digit_as_first_and_penultimate_and_alpha_as_last?(address_data)
|
30
|
+
return false unless address_data.length >= 2
|
31
|
+
|
32
|
+
first, *middle, last = address_data.chars
|
33
|
+
|
34
|
+
first.match?(/[[:digit:]]/) &&
|
35
|
+
(middle.empty? || middle.last.match?(/[[:digit:]]/)) &&
|
36
|
+
last.match?(/[[:alpha:]]/)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Exception III.
|
40
|
+
# @param address_data [String] buildingName or subBuildingName data.
|
41
|
+
# @return [Boolean] if one alphabetic character only. Case insensitive.
|
42
|
+
# @example
|
43
|
+
# contains_one_alpha_character?("A") #=> true
|
44
|
+
# contains_one_alpha_character?("b") #=> true
|
45
|
+
# contains_one_alpha_character?("Z") #=> true
|
46
|
+
# contains_one_alpha_character?("1") #=> false
|
47
|
+
def self.contains_one_alpha_character?(address_data)
|
48
|
+
return false unless address_data.length.eql? 1
|
49
|
+
|
50
|
+
address_data.match?(/^[a-zA-Z]$/)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Exception to Rule.
|
54
|
+
# @param address_data [String] buildingName.
|
55
|
+
# @return [Boolean] if last part (i.e. numeric part) contains a number
|
56
|
+
# between 0 and 9999.
|
57
|
+
# @example
|
58
|
+
# contains_digit_between_0_and_9999?("Example 0") #=> true
|
59
|
+
# contains_digit_between_0_and_9999?("Example 14") #=> true
|
60
|
+
# contains_digit_between_0_and_9999?("Example 9999") #=> true
|
61
|
+
# contains_digit_between_0_and_9999?("Example 10000") #=> false
|
62
|
+
def self.contains_digit_between_0_and_9999?(address_data)
|
63
|
+
address_data.match?(/ \d{1,4}$/)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Exception IV.
|
67
|
+
# @param address_data [String] buildingName.
|
68
|
+
# @return [Boolean] if last part (i.e. numeric range or a numeric alpha
|
69
|
+
# suffix) is prefixed by an addressing keyword. Case insensitive.
|
70
|
+
# @example
|
71
|
+
# contains_address_keyword?("UNIT 1") #=> true
|
72
|
+
# contains_address_keyword?("BLOCKS 1-2") #=> true
|
73
|
+
# contains_address_keyword?("FLAT 12A") #=> true
|
74
|
+
# contains_address_keyword?("shops 2-4") #=> true
|
75
|
+
# contains_address_keyword?("STALLS") #=> false
|
76
|
+
def self.contains_address_keyword?(address_data)
|
77
|
+
last_part = address_data.split(' ').last
|
78
|
+
|
79
|
+
['BACK OF', 'BLOCK', 'BLOCKS', 'BUILDING', 'FLAT', 'MAISONETTE', 'MAISONETTES', 'REAR OF', 'SHOP', 'SHOPS', 'STALL',
|
80
|
+
'STALLS', 'SUITE', 'SUITES', 'UNIT', 'UNITS'].each do |keyword|
|
81
|
+
regex = "(^|\\s)#{keyword} #{last_part.upcase}$"
|
82
|
+
return true if address_data.upcase.match?(regex)
|
83
|
+
end
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
# Exception V.
|
88
|
+
# @param address_data [String] buildingName.
|
89
|
+
# @return [Boolean] if penultimate part (i.e. word in a buildingName) is
|
90
|
+
# followed by a whitespace and completed by numerics with decimals or
|
91
|
+
# forward slashes.
|
92
|
+
# @example
|
93
|
+
# contains_digits_with_decimals_or_forward_slashes?("Example 1.2") #=> true
|
94
|
+
# contains_digits_with_decimals_or_forward_slashes?("Example 85/1") #=> true
|
95
|
+
# contains_digits_with_decimals_or_forward_slashes?("Example 7:8") #=> false
|
96
|
+
def self.contains_digits_with_decimals_or_forward_slashes?(address_data)
|
97
|
+
last_part = address_data.split(' ').last
|
98
|
+
|
99
|
+
last_part.match?(%r{^\d+[.|/]\d+$})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
# Getter and setter class for variables of the converted address.
|
5
|
+
# @attr line1 [String] converted.
|
6
|
+
# @attr line2 [String] converted.
|
7
|
+
# @attr line3 [String] converted.
|
8
|
+
# @attr line4 [String] converted.
|
9
|
+
# @attr line5 [String] the postTown of an address. Not converted.
|
10
|
+
# @attr country [String] not converted.
|
11
|
+
# @attr postcode [String] not converted.
|
12
|
+
# @attr language [String] not converted.
|
13
|
+
# @attr dps [String] the delivery point suffix, a unique 2-digit
|
14
|
+
# code for each delivery point in a postcode. Not converted.
|
15
|
+
class PrintableAddress
|
16
|
+
attr_accessor :line1, :line2, :line3, :line4, :line5, :country, :postcode, :language, :dps
|
17
|
+
|
18
|
+
# Converts PrintableAddress object to JSON Hash representation
|
19
|
+
# without ActiveSupport.
|
20
|
+
# @return [Hash] converted address data as JSON Hash.
|
21
|
+
def as_json
|
22
|
+
{
|
23
|
+
'line1' => @line1,
|
24
|
+
'line2' => @line2,
|
25
|
+
'line3' => @line3,
|
26
|
+
'line4' => @line4,
|
27
|
+
'line5' => @line5,
|
28
|
+
'postcode' => @postcode,
|
29
|
+
'country' => @country,
|
30
|
+
'language' => @language,
|
31
|
+
'dps' => @dps
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module BuildAddress
|
6
|
+
# Builds PrintableAddress object and returns attributes as JSON.
|
7
|
+
# @param paf_address [Hash] to be converted.
|
8
|
+
# @param lines [Array<String>] the converted lines of address data.
|
9
|
+
# Lines with an Integer value are type converted to a String.
|
10
|
+
# Lines with a value of nil are removed before converted address
|
11
|
+
# is returned.
|
12
|
+
def self.build_printable_address(paf_address:, lines:)
|
13
|
+
printable_address = PrintableAddress.new
|
14
|
+
|
15
|
+
lines.each_with_index do |line, index|
|
16
|
+
line_number = index + 1
|
17
|
+
line = line.to_s if line.is_a?(Integer)
|
18
|
+
printable_address.send("line#{line_number}=", line) unless line_number.eql?(6) || line_number.eql?(7)
|
19
|
+
end
|
20
|
+
|
21
|
+
printable_address.postcode = paf_address[POSTCODE].slice(0..7)
|
22
|
+
printable_address.country = paf_address[COUNTRY]
|
23
|
+
printable_address.language = paf_address[LANGUAGE]
|
24
|
+
printable_address.dps = paf_address[DPS]
|
25
|
+
|
26
|
+
printable_address.as_json.compact
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Places buildingNumber in converted printable format address depending
|
7
|
+
# on thoroughfare and locality elements present in the PAF address.
|
8
|
+
# @param data [Array<String>] the converted lines of address data.
|
9
|
+
# @param paf_address [Hash] to be converted.
|
10
|
+
# @param number [String] the building number to be placed in the
|
11
|
+
# converted address.
|
12
|
+
# @return data [Array<String>] the updated converted lines of
|
13
|
+
# address data with the building number added.
|
14
|
+
def self.add_building_number(data:, paf_address:, number:)
|
15
|
+
if paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
16
|
+
data << "#{number} #{paf_address[DEPENDENT_THOROUGHFARE_NAME]}"
|
17
|
+
data << paf_address[THOROUGHFARE_NAME]
|
18
|
+
data << paf_address[DOUBLE_DEPENDENT_LOCALITY]
|
19
|
+
data << paf_address[DEPENDENT_LOCALITY]
|
20
|
+
elsif paf_address[THOROUGHFARE_NAME]
|
21
|
+
data << "#{number} #{paf_address[THOROUGHFARE_NAME]}"
|
22
|
+
data << paf_address[DOUBLE_DEPENDENT_LOCALITY]
|
23
|
+
data << paf_address[DEPENDENT_LOCALITY]
|
24
|
+
elsif paf_address[DOUBLE_DEPENDENT_LOCALITY]
|
25
|
+
data << "#{number} #{paf_address[DOUBLE_DEPENDENT_LOCALITY]}"
|
26
|
+
data << paf_address[DEPENDENT_LOCALITY]
|
27
|
+
elsif paf_address[DEPENDENT_LOCALITY]
|
28
|
+
data << "#{number} #{paf_address[DEPENDENT_LOCALITY]}"
|
29
|
+
else
|
30
|
+
data << number
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 1 to the PAF address.
|
7
|
+
# Rule 1 conversion criteria: organisationName.
|
8
|
+
# Will also apply as a default if organisationName,
|
9
|
+
# subBuildingName, buildingName + buildingNumber NOT
|
10
|
+
# present. Exceptions are not applicable.
|
11
|
+
# @param paf_address [Hash] to be converted.
|
12
|
+
# @return data [Array<String>] the converted lines of address data.
|
13
|
+
def self.apply_rule1(paf_address:)
|
14
|
+
data = []
|
15
|
+
|
16
|
+
data << paf_address[ORGANISATION_NAME]
|
17
|
+
data << paf_address[DEPARTMENT_NAME]
|
18
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
19
|
+
|
20
|
+
data << paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
21
|
+
data << paf_address[THOROUGHFARE_NAME]
|
22
|
+
data << paf_address[DOUBLE_DEPENDENT_LOCALITY]
|
23
|
+
data << paf_address[DEPENDENT_LOCALITY]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 2 to the PAF address.
|
7
|
+
# Rule 2 conversion criteria: buildingNumber OR
|
8
|
+
# organisationName + buildingNumber. Exceptions
|
9
|
+
# are not applicable.
|
10
|
+
# @param paf_address [Hash] to be converted.
|
11
|
+
# @return data [Array<String>] the converted lines of address data.
|
12
|
+
def self.apply_rule2(paf_address:)
|
13
|
+
data = []
|
14
|
+
|
15
|
+
data << paf_address[ORGANISATION_NAME]
|
16
|
+
data << paf_address[DEPARTMENT_NAME]
|
17
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
18
|
+
|
19
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: paf_address[BUILDING_NUMBER])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 3 to the PAF address. Rule 3 conversion
|
7
|
+
# criteria: buildingName OR organisationName + buildingName.
|
8
|
+
# Exceptions can apply to buildingName.
|
9
|
+
# @param paf_address [Hash] to be converted.
|
10
|
+
# @param exception [Symbol] the exception to be applied during
|
11
|
+
# conversion.
|
12
|
+
# @return data [Array<String>] the converted lines of address data.
|
13
|
+
def self.apply_rule3(paf_address:, exception:)
|
14
|
+
data = []
|
15
|
+
|
16
|
+
data << paf_address[ORGANISATION_NAME]
|
17
|
+
data << paf_address[DEPARTMENT_NAME]
|
18
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
19
|
+
|
20
|
+
case exception
|
21
|
+
when :exception_i, :exception_ii, :exception_iii
|
22
|
+
if paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
23
|
+
data << "#{paf_address[BUILDING_NAME]} #{paf_address[DEPENDENT_THOROUGHFARE_NAME]}"
|
24
|
+
data << paf_address[THOROUGHFARE_NAME]
|
25
|
+
else
|
26
|
+
data << "#{paf_address[BUILDING_NAME]} #{paf_address[THOROUGHFARE_NAME]}"
|
27
|
+
end
|
28
|
+
when :exception_i_numeric_part, :exception_ii_numeric_part
|
29
|
+
*name, number = paf_address[BUILDING_NAME].split(' ')
|
30
|
+
data << name.join(' ')
|
31
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: number)
|
32
|
+
else
|
33
|
+
# Applies if :exception_to_rule, :exception_iv, :exception_v or :no_exception i.e. buildingName is not split
|
34
|
+
data << paf_address[BUILDING_NAME]
|
35
|
+
data << paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
36
|
+
data << paf_address[THOROUGHFARE_NAME]
|
37
|
+
end
|
38
|
+
|
39
|
+
data << paf_address[DOUBLE_DEPENDENT_LOCALITY] unless data.include?(paf_address[DOUBLE_DEPENDENT_LOCALITY])
|
40
|
+
data << paf_address[DEPENDENT_LOCALITY] unless data.include?(paf_address[DEPENDENT_LOCALITY])
|
41
|
+
data
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 4 to the PAF address.
|
7
|
+
# Rule 4 conversion criteria: buildingName + buildingNumber OR
|
8
|
+
# organisationName + buildingName + buildingNumber. Exceptions
|
9
|
+
# are not applicable.
|
10
|
+
# @param paf_address [Hash] to be converted.
|
11
|
+
# @return data [Array<String>] the converted lines of address data.
|
12
|
+
def self.apply_rule4(paf_address:)
|
13
|
+
data = []
|
14
|
+
|
15
|
+
data << paf_address[ORGANISATION_NAME]
|
16
|
+
data << paf_address[DEPARTMENT_NAME]
|
17
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
18
|
+
|
19
|
+
data << paf_address[BUILDING_NAME]
|
20
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: paf_address[BUILDING_NUMBER])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 5 to the PAF address. Rule 5 conversion
|
7
|
+
# criteria: subBuildingName + buildingNumber OR organisationName
|
8
|
+
# + subBuildingName + buildingNumber. Exceptions can apply to
|
9
|
+
# subBuildingName ONLY.
|
10
|
+
# @param paf_address [Hash] to be converted.
|
11
|
+
# @param exception [Symbol] the exception to be applied during
|
12
|
+
# conversion.
|
13
|
+
# @return data [Array<String>] the converted lines of address data.
|
14
|
+
def self.apply_rule5(paf_address:, exception:)
|
15
|
+
data = []
|
16
|
+
|
17
|
+
data << paf_address[ORGANISATION_NAME]
|
18
|
+
data << paf_address[DEPARTMENT_NAME]
|
19
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
20
|
+
|
21
|
+
case exception
|
22
|
+
when :exception_i, :exception_ii
|
23
|
+
number = "#{paf_address[SUB_BUILDING_NAME]} #{paf_address[BUILDING_NUMBER]}"
|
24
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: number)
|
25
|
+
when :exception_iii
|
26
|
+
number = "#{paf_address[BUILDING_NUMBER]}#{paf_address[SUB_BUILDING_NAME]}"
|
27
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: number)
|
28
|
+
else
|
29
|
+
data << paf_address[SUB_BUILDING_NAME]
|
30
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: paf_address[BUILDING_NUMBER])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
SUB_BUILDING_NAME_EXCEPTIONS = %i[sub_building_name_exception_i sub_building_name_exception_ii sub_building_name_exception_iii].freeze
|
7
|
+
|
8
|
+
# Applies conversion Rule 6 to the PAF address. Rule 6 conversion
|
9
|
+
# criteria: subBuildingName + buildingName OR organisationName
|
10
|
+
# + subBuildingName + buildingName. Exceptions can apply to
|
11
|
+
# subBuildingName AND/OR buildingName.
|
12
|
+
# @param paf_address [Hash] to be converted.
|
13
|
+
# @param exceptions [Array<Symbol>] the exceptions to be applied
|
14
|
+
# during conversion.
|
15
|
+
# @return data [Array<String>] the converted lines of address data.
|
16
|
+
def self.apply_rule6(paf_address:, exceptions:)
|
17
|
+
building_name_exception = exceptions.first
|
18
|
+
sub_building_name_exception = exceptions.last
|
19
|
+
|
20
|
+
data = []
|
21
|
+
|
22
|
+
data << paf_address[ORGANISATION_NAME]
|
23
|
+
data << paf_address[DEPARTMENT_NAME]
|
24
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
25
|
+
|
26
|
+
case building_name_exception
|
27
|
+
when :building_name_exception_i, :building_name_exception_ii, :building_name_exception_iii
|
28
|
+
# Applies regardless of sub_building_name_exception.
|
29
|
+
if paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
30
|
+
data << paf_address[SUB_BUILDING_NAME]
|
31
|
+
data << "#{paf_address[BUILDING_NAME]} #{paf_address[DEPENDENT_THOROUGHFARE_NAME]}"
|
32
|
+
data << paf_address[THOROUGHFARE_NAME]
|
33
|
+
else
|
34
|
+
data << paf_address[SUB_BUILDING_NAME]
|
35
|
+
data << "#{paf_address[BUILDING_NAME]} #{paf_address[THOROUGHFARE_NAME]}"
|
36
|
+
end
|
37
|
+
when :building_name_exception_i_numeric_part, :building_name_exception_ii_numeric_part
|
38
|
+
if SUB_BUILDING_NAME_EXCEPTIONS.include?(sub_building_name_exception)
|
39
|
+
*name, number = paf_address[BUILDING_NAME].split(' ')
|
40
|
+
data << "#{paf_address[SUB_BUILDING_NAME]} #{name.join(" ")}"
|
41
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: number)
|
42
|
+
else
|
43
|
+
*name, number = paf_address[BUILDING_NAME].split(' ')
|
44
|
+
data << paf_address[SUB_BUILDING_NAME]
|
45
|
+
data << name.join(' ')
|
46
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: number)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
# Applies if :building_name_exception_to_rule, :building_name_exception_iv, :building_name_exception_v or :building_name_no_exception
|
50
|
+
# i.e. buildingName is not split
|
51
|
+
if SUB_BUILDING_NAME_EXCEPTIONS.include?(sub_building_name_exception)
|
52
|
+
data << "#{paf_address[SUB_BUILDING_NAME]} #{paf_address[BUILDING_NAME]}"
|
53
|
+
data << paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
54
|
+
data << paf_address[THOROUGHFARE_NAME]
|
55
|
+
else
|
56
|
+
data << paf_address[SUB_BUILDING_NAME]
|
57
|
+
data << paf_address[BUILDING_NAME]
|
58
|
+
data << paf_address[DEPENDENT_THOROUGHFARE_NAME]
|
59
|
+
data << paf_address[THOROUGHFARE_NAME]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
data << paf_address[DOUBLE_DEPENDENT_LOCALITY] unless data.include?(paf_address[DOUBLE_DEPENDENT_LOCALITY])
|
64
|
+
data << paf_address[DEPENDENT_LOCALITY] unless data.include?(paf_address[DEPENDENT_LOCALITY])
|
65
|
+
data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module ApplyRule
|
6
|
+
# Applies conversion Rule 7 to the PAF address. Rule 7 conversion
|
7
|
+
# criteria: subBuildingName + buildingName + buildingNumber OR
|
8
|
+
# organisationName + subBuildingName + buildingName +
|
9
|
+
# buildingNumber. Exceptions can apply to subBuildingName ONLY.
|
10
|
+
# @param paf_address [Hash] to be converted.
|
11
|
+
# @param exception [Symbol] the exception to be applied during
|
12
|
+
# conversion.
|
13
|
+
# @return data [Array<String>] the converted lines of address data.
|
14
|
+
def self.apply_rule7(paf_address:, exception:)
|
15
|
+
data = []
|
16
|
+
|
17
|
+
data << paf_address[ORGANISATION_NAME]
|
18
|
+
data << paf_address[DEPARTMENT_NAME]
|
19
|
+
data << "PO BOX #{paf_address[PO_BOX_NUMBER]}" if paf_address[PO_BOX_NUMBER]
|
20
|
+
|
21
|
+
case exception
|
22
|
+
when :exception_i, :exception_ii, :exception_iii
|
23
|
+
data << "#{paf_address[SUB_BUILDING_NAME]} #{paf_address[BUILDING_NAME]}"
|
24
|
+
else
|
25
|
+
data << paf_address[SUB_BUILDING_NAME]
|
26
|
+
data << paf_address[BUILDING_NAME]
|
27
|
+
end
|
28
|
+
|
29
|
+
PostmanPAF::Rules::ApplyRule.add_building_number(data: data, paf_address: paf_address, number: paf_address[BUILDING_NUMBER])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
# PAF address elements:
|
6
|
+
ORGANISATION_NAME = 'organisationName'
|
7
|
+
DEPARTMENT_NAME = 'departmentName'
|
8
|
+
PO_BOX_NUMBER = 'poBoxNumber'
|
9
|
+
BUILDING_NUMBER = 'buildingNumber'
|
10
|
+
BUILDING_NAME = 'buildingName'
|
11
|
+
SUB_BUILDING_NAME = 'subBuildingName'
|
12
|
+
THOROUGHFARE_NAME = 'thoroughfareName'
|
13
|
+
DEPENDENT_THOROUGHFARE_NAME = 'dependentThoroughfareName'
|
14
|
+
DEPENDENT_LOCALITY = 'dependentLocality'
|
15
|
+
DOUBLE_DEPENDENT_LOCALITY = 'doubleDependentLocality'
|
16
|
+
POST_TOWN = 'postTown'
|
17
|
+
POSTCODE = 'postcode'
|
18
|
+
COUNTRY = 'country'
|
19
|
+
LANGUAGE = 'language'
|
20
|
+
DPS = 'dps'
|
21
|
+
# Only the presence of subBuildingName, buildingName and
|
22
|
+
# buildingNumber determine which Rule applies.
|
23
|
+
ADDRESS_ELEMENTS_FOR_RULE = %w[subBuildingName buildingName buildingNumber].freeze
|
24
|
+
|
25
|
+
# Identifies address elements found in PAF address and returns
|
26
|
+
# applicable rule.
|
27
|
+
# @param paf_address [Hash] to be converted.
|
28
|
+
# @return [Symbol] the conversion rule to apply to the address
|
29
|
+
# based on address elements found.
|
30
|
+
def self.which_rule(paf_address:)
|
31
|
+
found_premise_elements = []
|
32
|
+
|
33
|
+
paf_address.each do |key, value|
|
34
|
+
# to_s is used to handle an Integer value in the PAF address.
|
35
|
+
found_premise_elements << key if ADDRESS_ELEMENTS_FOR_RULE.include?(key) && !value.to_s.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
PostmanPAF::Rules::WhichRule.rule_to_apply(premise_elements: found_premise_elements)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Applies the applicable rule and exception (if applicable) to
|
42
|
+
# create the converted printable address data. Rule 1
|
43
|
+
# applies for a PAF address containing an organisationName,
|
44
|
+
# but also applies when defaulting to Rule 1 for a PAF address
|
45
|
+
# that does not contain: organisationName, subBuildingName,
|
46
|
+
# buildingName or buildingNumber.
|
47
|
+
# @param rule [Symbol] the conversion rule to be applied to the
|
48
|
+
# address.
|
49
|
+
# @param paf_address [Hash] to be converted.
|
50
|
+
# @param exception [Symbol] applied during conversion.
|
51
|
+
# @return [Array] converted address data.
|
52
|
+
def self.apply_rule(rule:, paf_address:, exception:)
|
53
|
+
case rule
|
54
|
+
when :rule2
|
55
|
+
PostmanPAF::Rules::ApplyRule.apply_rule2(paf_address: paf_address)
|
56
|
+
when :rule3
|
57
|
+
PostmanPAF::Rules::ApplyRule.apply_rule3(paf_address: paf_address, exception: exception)
|
58
|
+
when :rule4
|
59
|
+
PostmanPAF::Rules::ApplyRule.apply_rule4(paf_address: paf_address)
|
60
|
+
when :rule5
|
61
|
+
PostmanPAF::Rules::ApplyRule.apply_rule5(paf_address: paf_address, exception: exception)
|
62
|
+
when :rule6
|
63
|
+
PostmanPAF::Rules::ApplyRule.apply_rule6(paf_address: paf_address, exceptions: exception)
|
64
|
+
when :rule7
|
65
|
+
PostmanPAF::Rules::ApplyRule.apply_rule7(paf_address: paf_address, exception: exception)
|
66
|
+
else
|
67
|
+
PostmanPAF::Rules::ApplyRule.apply_rule1(paf_address: paf_address)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PostmanPAF
|
4
|
+
module Rules
|
5
|
+
module WhichRule
|
6
|
+
RULE_02 = %w[buildingNumber].freeze
|
7
|
+
RULE_03 = %w[buildingName].freeze
|
8
|
+
RULE_04 = %w[buildingName buildingNumber].freeze
|
9
|
+
RULE_05 = %w[buildingNumber subBuildingName].freeze
|
10
|
+
RULE_06 = %w[buildingName subBuildingName].freeze
|
11
|
+
RULE_07 = %w[buildingName buildingNumber subBuildingName].freeze
|
12
|
+
|
13
|
+
# Matches address elements found in PAF address to criteria for
|
14
|
+
# applicable Royal Mail rule. Rule 1 is defaulted to if
|
15
|
+
# subBuildingName, buildingName and buildingNumber are NOT
|
16
|
+
# present.
|
17
|
+
# @param premise_elements [Array<String>] the premise elements
|
18
|
+
# found in the PAF address.
|
19
|
+
# @return [Symbol] the applicable Royal Mail conversion rule.
|
20
|
+
def self.rule_to_apply(premise_elements:)
|
21
|
+
case premise_elements.sort
|
22
|
+
when RULE_02
|
23
|
+
:rule2
|
24
|
+
when RULE_03
|
25
|
+
:rule3
|
26
|
+
when RULE_04
|
27
|
+
:rule4
|
28
|
+
when RULE_05
|
29
|
+
:rule5
|
30
|
+
when RULE_06
|
31
|
+
:rule6
|
32
|
+
when RULE_07
|
33
|
+
:rule7
|
34
|
+
else
|
35
|
+
:rule1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|