defra_ruby_validators 0.1.1 → 1.0.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 +4 -4
  2. data/README.md +1 -18
  3. data/config/locales/defra_ruby/validators/companies_house_number_validator/en.yml +10 -0
  4. data/lib/defra_ruby/validators.rb +15 -0
  5. data/lib/defra_ruby/validators/base_validator.rb +21 -0
  6. data/lib/defra_ruby/validators/companies_house_number_validator.rb +54 -0
  7. data/lib/defra_ruby/validators/companies_house_service.rb +36 -0
  8. data/lib/defra_ruby/validators/configuration.rb +35 -0
  9. data/lib/defra_ruby/validators/engine.rb +22 -0
  10. data/lib/defra_ruby/validators/version.rb +7 -0
  11. data/lib/defra_ruby_validators.rb +27 -9
  12. data/spec/cassettes/company_no_inactive.yml +14 -14
  13. data/spec/cassettes/company_no_not_found.yml +15 -15
  14. data/spec/cassettes/company_no_valid.yml +14 -14
  15. data/spec/defra_ruby/validators/companies_house_number_validator_spec.rb +88 -0
  16. data/spec/defra_ruby/validators/companies_house_service_spec.rb +58 -0
  17. data/spec/defra_ruby/validators/configuration_spec.rb +49 -0
  18. data/spec/defra_ruby/validators_spec.rb +12 -0
  19. data/spec/examples.txt +36 -27
  20. data/spec/spec_helper.rb +69 -19
  21. data/spec/support/defra_ruby_validators.rb +13 -2
  22. data/spec/support/dotenv.rb +4 -0
  23. data/spec/support/helpers/translator.rb +14 -0
  24. data/spec/support/pry.rb +7 -0
  25. data/spec/support/shared_examples/validators/invalid_record.rb +18 -0
  26. data/spec/support/shared_examples/validators/valid_record.rb +12 -0
  27. data/spec/support/shared_examples/validators/validator.rb +8 -0
  28. data/spec/support/simplecov.rb +17 -0
  29. metadata +37 -19
  30. data/config/locales/defra_ruby_validators/companies_house_number/en.yml +0 -14
  31. data/lib/defra_ruby_validators/companies_house_number_validator.rb +0 -56
  32. data/lib/defra_ruby_validators/companies_house_service.rb +0 -34
  33. data/lib/defra_ruby_validators/engine.rb +0 -20
  34. data/lib/defra_ruby_validators/validators.rb +0 -33
  35. data/lib/defra_ruby_validators/version.rb +0 -5
  36. data/spec/defra_ruby_validators/companies_house_number_validator_spec.rb +0 -125
  37. data/spec/defra_ruby_validators/companies_house_service_spec.rb +0 -55
  38. data/spec/defra_ruby_validators/version_spec.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e2b90f695b85e87e772c0ff9706b022a889ddfc
4
- data.tar.gz: d0fda70ca250ed7cedc5cb0243f9704858f5a3a3
3
+ metadata.gz: 0a6c04c81d7df4234ebf4905a40680e9f27be96b
4
+ data.tar.gz: 42b56455e1072d95b796c8403453326ce6d6ab85
5
5
  SHA512:
6
- metadata.gz: 6ce3932e23c36d6d657759fcfeb35976bf82d123469d702b18c681c0dc3fb32027309ec6070903d642a26bb1ce47712641dd7843266c85e3a50aca8cb5947f47
7
- data.tar.gz: 1784ad53fb92e0d93550bb583109c84ccff475ed50dc43120467ed2db5f0a86ef1d3304f8b939b7a8c7d3b1f4e10c97bda1a176838261070eb87dbd9aebb6d46
6
+ metadata.gz: f3464caf2e33d2a4dea606ad62728e82becb0416b4122728759e9f7973a877e7d19fcb7957dd175ac5302166ab189a616707b8d25e042f30664b0c9b068ac73a
7
+ data.tar.gz: 8c55a4ffba3ba440ecbefb37cb9de61b654373ea436c07f9c34841440437ba4105f90171b7058472f83b4376ef7e78fc92d40c8d26fd0c2fb88edc9b3561b545
data/README.md CHANGED
@@ -45,24 +45,7 @@ A company can be in various states for example liquidation, which means for the
45
45
  Add it to your model or form object using
46
46
 
47
47
  ```ruby
48
- validates :company_no, "defra_ruby_validators/companies_house_number": true
49
- ```
50
-
51
- A locale hint plus help text is also available for your views, that details what a registration number is and the restrictions, e.g
52
-
53
- ```erb
54
- <span class="form-hint"><%= t("defra_validators.companies_house_number.hint") %></span>
55
-
56
- <div class="form-group">
57
- <details>
58
- <summary>
59
- <span class="summary"><%= t("defra_validators.companies_house_number.help.heading") %></span>
60
- </summary>
61
- <div class="panel panel-border-narrow">
62
- <p><%= t("defra_validators.companies_house_number.help.#{@form.business_type}") %></p>
63
- </div>
64
- </details>
65
- </div>
48
+ validates :company_no, "defra_ruby/validators/companies_house_number": true
66
49
  ```
67
50
 
68
51
  ## Contributing to this project
@@ -0,0 +1,10 @@
1
+ en:
2
+ defra_ruby:
3
+ validators:
4
+ CompaniesHouseNumberValidator:
5
+ company_no:
6
+ blank: Enter a company registration number
7
+ invalid: "Enter a valid number - it should have 8 digits, or 2 letters followed by 6 digits, or 2 letters followed by 5 digits and another letter. If your number has only 7 digits, enter it with a zero at the start."
8
+ not_found: Companies House couldn't find a company with this number
9
+ inactive: Your company must be registered as an active company
10
+ error: There was an error connecting with Companies House. Hopefully this is a one off and will work if you try again.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ require_relative "validators/version"
6
+ require_relative "validators/configuration"
7
+ require_relative "validators/companies_house_service"
8
+
9
+ require_relative "validators/base_validator"
10
+ require_relative "validators/companies_house_number_validator"
11
+
12
+ module DefraRuby
13
+ module Validators
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRuby
4
+ module Validators
5
+ class BaseValidator < ActiveModel::EachValidator
6
+
7
+ protected
8
+
9
+ def error_message(attribute, error)
10
+ I18n.t("defra_ruby.validators.#{class_name}.#{attribute}.#{error}")
11
+ end
12
+
13
+ private
14
+
15
+ def class_name
16
+ self.class.name.split("::").last
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRuby
4
+ module Validators
5
+ class CompaniesHouseNumberValidator < BaseValidator
6
+
7
+ # Examples we need to validate are
8
+ # 10997904, 09764739
9
+ # SC534714, CE000958
10
+ # IP00141R, IP27702R, SP02252R
11
+ # https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/426891/uniformResourceIdentifiersCustomerGuide.pdf
12
+ VALID_COMPANIES_HOUSE_REGISTRATION_NUMBER_REGEX = Regexp.new(
13
+ /\A(\d{8,8}$)|([a-zA-Z]{2}\d{6}$)|([a-zA-Z]{2}\d{5}[a-zA-Z]{1}$)\z/i
14
+ ).freeze
15
+
16
+ def validate_each(record, attribute, value)
17
+ return false unless value_is_present?(record, attribute, value)
18
+ return false unless format_is_valid?(record, attribute, value)
19
+
20
+ validate_with_companies_house(record, attribute, value)
21
+ end
22
+
23
+ private
24
+
25
+ def value_is_present?(record, attribute, value)
26
+ return true if value.present?
27
+
28
+ record.errors[attribute] << error_message(attribute, "blank")
29
+ false
30
+ end
31
+
32
+ def format_is_valid?(record, attribute, value)
33
+ return true if value.match?(VALID_COMPANIES_HOUSE_REGISTRATION_NUMBER_REGEX)
34
+
35
+ record.errors[attribute] << error_message(attribute, "invalid")
36
+ false
37
+ end
38
+
39
+ def validate_with_companies_house(record, attribute, value)
40
+ case CompaniesHouseService.new(value).status
41
+ when :active
42
+ true
43
+ when :inactive
44
+ record.errors[attribute] << error_message(attribute, "inactive")
45
+ when :not_found
46
+ record.errors[attribute] << error_message(attribute, "not_found")
47
+ end
48
+ rescue StandardError
49
+ record.errors[attribute] << error_message(attribute, "error")
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rest-client"
4
+
5
+ module DefraRuby
6
+ module Validators
7
+ class CompaniesHouseService
8
+ def initialize(company_no)
9
+ @company_no = company_no
10
+ @url = "#{DefraRuby::Validators.configuration.companies_house_host}#{@company_no}"
11
+ @api_key = DefraRuby::Validators.configuration.companies_house_api_key
12
+ end
13
+
14
+ def status
15
+ response = RestClient::Request.execute(
16
+ method: :get,
17
+ url: @url,
18
+ user: @api_key,
19
+ password: ""
20
+ )
21
+
22
+ json = JSON.parse(response)
23
+
24
+ status_is_allowed?(json["company_status"]) ? :active : :inactive
25
+ rescue RestClient::ResourceNotFound
26
+ :not_found
27
+ end
28
+
29
+ private
30
+
31
+ def status_is_allowed?(status)
32
+ %w[active voluntary-arrangement].include?(status)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRuby
4
+ module Validators
5
+ class << self
6
+ attr_accessor :configuration
7
+ end
8
+
9
+ def self.configure
10
+ self.configuration ||= Configuration.new
11
+ yield(configuration)
12
+ end
13
+
14
+ class Configuration
15
+ ATTRIBUTES = %i[
16
+ companies_house_host
17
+ companies_house_api_key
18
+ ].freeze
19
+
20
+ attr_accessor(*ATTRIBUTES)
21
+
22
+ def initialize
23
+ @companies_house_host = "https://api.companieshouse.gov.uk/company/"
24
+ @companies_house_api_key = nil
25
+ end
26
+
27
+ def ensure_valid
28
+ missing_attributes = ATTRIBUTES.select { |a| public_send(a).nil? }
29
+ return true if missing_attributes.empty?
30
+
31
+ raise "The following DefraRuby::Validators configuration attributes are missing: #{missing_attributes}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "defra_ruby/validators"
4
+
5
+ module DefraRuby
6
+ module Validators
7
+ # Engine used to load in the custom validations
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace DefraRuby::Validators
10
+
11
+ # Add a load path for this specific Engine
12
+ config.autoload_paths += Dir[File.join(config.root, "lib", "**")]
13
+
14
+ # Load I18n translation files from engine before loading ones from the host app
15
+ # This means values in the host app can override those in the engine
16
+ config.before_initialize do
17
+ engine_locales = Dir["#{config.root}/config/locales/**/*.yml"]
18
+ config.i18n.load_path = engine_locales + config.i18n.load_path
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRuby
4
+ module Validators
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -1,14 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "defra_ruby_validators/engine"
3
+ require "defra_ruby/validators/engine"
4
4
 
5
+ # This breaks from the typical pattern of calling the root file in lib after the
6
+ # root namespace e.g. defra_ruby.rb
7
+ # The specific intent for this gem (engine) is to be used in our services rails
8
+ # engines i.e. an engine mounted in an engine.
9
+ #
10
+ # The standard pattern is to have an engine.rb in the relevant namespace. Hence
11
+ # in this project we have lib/defra_ruby/validators/engine.rb. When it comes to
12
+ # requiring this project in host engine this means it needs to require that
13
+ # file explicitly. For example in lib/waste_exemptions_engine/engine.rb the
14
+ # reference would be
15
+ #
16
+ # require "defra_ruby/validators/engine"
17
+ #
18
+ # That's fine, but it does mean this file becomes meaningless. Its not
19
+ # referenced by our specs as they refer to lib/defra_ruby/validators/validators.rb
20
+ # It's not referenced by engine.rb because it also references validators.rb.
21
+ #
22
+ # However if we call it defra_ruby_validators.rb and include a require to the
23
+ # engine, then the host engine can instead just do the following
24
+ #
25
+ # require "defra_ruby_validators"
26
+ #
27
+ # This makes the require statement consistent with the name of the gem, makes
28
+ # implementation a little simpler, and if we build up a suite of these engines
29
+ # makes them a little clearer to distinguish in the host engine's engine.rb.
5
30
  module DefraRubyValidators
6
- # See lib/defra_ruby_validators/validators.rb for the main content.
7
- # We have this file which just require's engine.rb to support using the gem
8
- # in a rails project.
9
- # For our test suite, we just want the typical listing of `require "filex"`,
10
- # as the engine references using Rails.
11
- # We also have the gem setup to support being configured from the host app.
12
- # This is all done in `validators.rb`, which is picked up by `engine.rb` as
13
- # well.
31
+ # The Defra Ruby packages namespace
14
32
  end
@@ -12,7 +12,7 @@ http_interactions:
12
12
  Accept-Encoding:
13
13
  - gzip, deflate
14
14
  User-Agent:
15
- - rest-client/2.0.2 (darwin17.3.0 x86_64) ruby/2.4.2p198
15
+ - rest-client/2.0.2 (darwin18.5.0 x86_64) ruby/2.4.2p198
16
16
  Host:
17
17
  - api.companieshouse.gov.uk
18
18
  Authorization:
@@ -22,14 +22,6 @@ http_interactions:
22
22
  code: 200
23
23
  message: OK
24
24
  headers:
25
- Date:
26
- - Mon, 28 Jan 2019 09:38:50 GMT
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '979'
31
- Connection:
32
- - keep-alive
33
25
  Access-Control-Allow-Credentials:
34
26
  - 'true'
35
27
  Access-Control-Allow-Headers:
@@ -44,18 +36,26 @@ http_interactions:
44
36
  - '3600'
45
37
  Cache-Control:
46
38
  - no-store, no-cache, must-revalidate, post-check=0, pre-check=0
39
+ Content-Type:
40
+ - application/json
41
+ Date:
42
+ - Mon, 13 May 2019 10:51:59 GMT
47
43
  Pragma:
48
44
  - no-cache
45
+ Server:
46
+ - CompaniesHouse
49
47
  X-Ratelimit-Limit:
50
48
  - '600'
51
49
  X-Ratelimit-Remain:
52
- - '596'
50
+ - '597'
53
51
  X-Ratelimit-Reset:
54
- - '1548668445'
52
+ - '1557745019'
55
53
  X-Ratelimit-Window:
56
54
  - 5m
57
- Server:
58
- - CompaniesHouse
55
+ Content-Length:
56
+ - '979'
57
+ Connection:
58
+ - keep-alive
59
59
  body:
60
60
  encoding: UTF-8
61
61
  string: '{"sic_codes":["82990"],"company_number":"07281919","has_been_liquidated":false,"accounts":{"last_accounts":{"made_up_to":"2013-06-30","type":"total-exemption-small"},"accounting_reference_date":{"day":"30","month":"06"}},"last_full_members_list_date":"2014-06-11","status":"active","type":"ltd","date_of_creation":"2010-06-11","registered_office_address":{"postal_code":"HA8
@@ -63,5 +63,5 @@ http_interactions:
63
63
  Elizabeth House","address_line_2":"54-58 High Street"},"undeliverable_registered_office_address":false,"company_name":"DIRECT
64
64
  SKIPS UK LTD","annual_return":{"last_made_up_to":"2014-06-11"},"jurisdiction":"england-wales","etag":"1cdef5bc2a020b3e9003b086033b64b78bcb28f6","company_status":"dissolved","has_insolvency_history":false,"has_charges":false,"links":{"self":"/company/07281919","filing_history":"/company/07281919/filing-history","officers":"/company/07281919/officers"},"date_of_cessation":"2016-01-05","can_file":false}'
65
65
  http_version:
66
- recorded_at: Mon, 28 Jan 2019 09:38:50 GMT
66
+ recorded_at: Mon, 13 May 2019 10:51:59 GMT
67
67
  recorded_with: VCR 4.0.0
@@ -12,7 +12,7 @@ http_interactions:
12
12
  Accept-Encoding:
13
13
  - gzip, deflate
14
14
  User-Agent:
15
- - rest-client/2.0.2 (darwin17.3.0 x86_64) ruby/2.4.2p198
15
+ - rest-client/2.0.2 (darwin18.5.0 x86_64) ruby/2.4.2p198
16
16
  Host:
17
17
  - api.companieshouse.gov.uk
18
18
  Authorization:
@@ -22,14 +22,6 @@ http_interactions:
22
22
  code: 404
23
23
  message: Not Found
24
24
  headers:
25
- Date:
26
- - Mon, 28 Jan 2019 09:38:50 GMT
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '70'
31
- Connection:
32
- - keep-alive
33
25
  Access-Control-Allow-Credentials:
34
26
  - 'true'
35
27
  Access-Control-Allow-Headers:
@@ -44,21 +36,29 @@ http_interactions:
44
36
  - '3600'
45
37
  Cache-Control:
46
38
  - no-store, no-cache, must-revalidate, post-check=0, pre-check=0
39
+ Content-Type:
40
+ - application/json
41
+ Date:
42
+ - Mon, 13 May 2019 10:51:59 GMT
47
43
  Pragma:
48
44
  - no-cache
45
+ Server:
46
+ - CompaniesHouse
49
47
  X-Ratelimit-Limit:
50
48
  - '600'
51
49
  X-Ratelimit-Remain:
52
- - '597'
50
+ - '598'
53
51
  X-Ratelimit-Reset:
54
- - '1548668445'
52
+ - '1557745019'
55
53
  X-Ratelimit-Window:
56
54
  - 5m
57
- Server:
58
- - CompaniesHouse
55
+ Content-Length:
56
+ - '70'
57
+ Connection:
58
+ - keep-alive
59
59
  body:
60
60
  encoding: UTF-8
61
- string: '{"errors":[{"error":"company-profile-not-found","type":"ch:service"}]}'
61
+ string: '{"errors":[{"type":"ch:service","error":"company-profile-not-found"}]}'
62
62
  http_version:
63
- recorded_at: Mon, 28 Jan 2019 09:38:50 GMT
63
+ recorded_at: Mon, 13 May 2019 10:51:59 GMT
64
64
  recorded_with: VCR 4.0.0
@@ -12,7 +12,7 @@ http_interactions:
12
12
  Accept-Encoding:
13
13
  - gzip, deflate
14
14
  User-Agent:
15
- - rest-client/2.0.2 (darwin17.3.0 x86_64) ruby/2.4.2p198
15
+ - rest-client/2.0.2 (darwin18.5.0 x86_64) ruby/2.4.2p198
16
16
  Host:
17
17
  - api.companieshouse.gov.uk
18
18
  Authorization:
@@ -22,14 +22,6 @@ http_interactions:
22
22
  code: 200
23
23
  message: OK
24
24
  headers:
25
- Date:
26
- - Sun, 27 Jan 2019 23:57:58 GMT
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '1178'
31
- Connection:
32
- - keep-alive
33
25
  Access-Control-Allow-Credentials:
34
26
  - 'true'
35
27
  Access-Control-Allow-Headers:
@@ -44,22 +36,30 @@ http_interactions:
44
36
  - '3600'
45
37
  Cache-Control:
46
38
  - no-store, no-cache, must-revalidate, post-check=0, pre-check=0
39
+ Content-Type:
40
+ - application/json
41
+ Date:
42
+ - Mon, 13 May 2019 10:51:59 GMT
47
43
  Pragma:
48
44
  - no-cache
45
+ Server:
46
+ - CompaniesHouse
49
47
  X-Ratelimit-Limit:
50
48
  - '600'
51
49
  X-Ratelimit-Remain:
52
- - '598'
50
+ - '599'
53
51
  X-Ratelimit-Reset:
54
- - '1548633621'
52
+ - '1557745019'
55
53
  X-Ratelimit-Window:
56
54
  - 5m
57
- Server:
58
- - CompaniesHouse
55
+ Content-Length:
56
+ - '1178'
57
+ Connection:
58
+ - keep-alive
59
59
  body:
60
60
  encoding: UTF-8
61
61
  string: '{"type":"ltd","company_name":"0800 WASTE LTD.","has_insolvency_history":false,"accounts":{"next_due":"2019-09-30","next_made_up_to":"2018-12-31","next_accounts":{"overdue":false,"period_start_on":"2018-01-01","due_on":"2019-09-30","period_end_on":"2018-12-31"},"accounting_reference_date":{"month":"12","day":"31"},"last_accounts":{"period_end_on":"2017-12-31","period_start_on":"2017-01-01","made_up_to":"2017-12-31"},"overdue":false},"undeliverable_registered_office_address":false,"etag":"0ec9d00cab0ffee2ef1f5d59f4f714fa5b1618f5","company_number":"09360070","registered_office_address":{"postal_code":"SM3
62
62
  9ND","locality":"Sutton","region":"Surrey","address_line_1":"21 Haslam Avenue"},"jurisdiction":"england-wales","date_of_creation":"2014-12-18","company_status":"active","has_charges":false,"sic_codes":["38110"],"last_full_members_list_date":"2015-12-18","confirmation_statement":{"overdue":false,"next_made_up_to":"2020-01-23","last_made_up_to":"2019-01-23","next_due":"2020-02-06"},"links":{"self":"/company/09360070","filing_history":"/company/09360070/filing-history","officers":"/company/09360070/officers"},"registered_office_is_in_dispute":false,"can_file":true}'
63
63
  http_version:
64
- recorded_at: Sun, 27 Jan 2019 23:57:58 GMT
64
+ recorded_at: Mon, 13 May 2019 10:51:58 GMT
65
65
  recorded_with: VCR 4.0.0