postman_paf 0.3.0

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