postman_paf 0.3.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gem-push.yml +48 -0
  3. data/.github/workflows/gem-test.yml +22 -0
  4. data/.gitignore +17 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +36 -0
  7. data/CHANGELOG.md +5 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +6 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +174 -0
  12. data/Rakefile +12 -0
  13. data/bin/console +15 -0
  14. data/bin/setup +8 -0
  15. data/lib/postman_paf/converter.rb +65 -0
  16. data/lib/postman_paf/exceptions/exceptions.rb +29 -0
  17. data/lib/postman_paf/exceptions/last_part_exceptions.rb +36 -0
  18. data/lib/postman_paf/exceptions/rule_3_exceptions.rb +35 -0
  19. data/lib/postman_paf/exceptions/rule_5_and_7_exceptions.rb +24 -0
  20. data/lib/postman_paf/exceptions/rule_6_exceptions.rb +48 -0
  21. data/lib/postman_paf/exceptions/which_exception.rb +103 -0
  22. data/lib/postman_paf/printable_address.rb +35 -0
  23. data/lib/postman_paf/rules/address_builder.rb +30 -0
  24. data/lib/postman_paf/rules/building_number.rb +35 -0
  25. data/lib/postman_paf/rules/rule_1.rb +27 -0
  26. data/lib/postman_paf/rules/rule_2.rb +23 -0
  27. data/lib/postman_paf/rules/rule_3.rb +45 -0
  28. data/lib/postman_paf/rules/rule_4.rb +24 -0
  29. data/lib/postman_paf/rules/rule_5.rb +35 -0
  30. data/lib/postman_paf/rules/rule_6.rb +69 -0
  31. data/lib/postman_paf/rules/rule_7.rb +33 -0
  32. data/lib/postman_paf/rules/rules.rb +71 -0
  33. data/lib/postman_paf/rules/which_rule.rb +40 -0
  34. data/lib/postman_paf/validator.rb +65 -0
  35. data/lib/postman_paf/version.rb +5 -0
  36. data/lib/postman_paf.rb +32 -0
  37. data/postman_paf.gemspec +53 -0
  38. 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