checkability 0.5.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae53afa68f33c527bee835d3c84e304c7596f46665cc41ff1df67b2e4189f595
4
- data.tar.gz: 8de80e50a31b5e3c7b2f498b8c81de8793b4d80f9a4665653dd12be745331721
3
+ metadata.gz: f427c18e28549b00de27a9b29d5c3278be27a07f76b5efeb3c88bd07c9fa8004
4
+ data.tar.gz: 68c2e5ca988d593452a15b07c2b0a9f6155cf4a170434c40509c2e8f570a1cff
5
5
  SHA512:
6
- metadata.gz: 32ed1b0305e36d037d38bde6acf78f4c7d4ae04e49764d4129fb5d9f455cf349607131ea17b5a500a0f47ab38f31698d06eeaec07dc9a014b030518be42e66fa
7
- data.tar.gz: cec99b497500443955ef650e56948037e8e7855456f14aa66a491e71ecd884c9d1553c64bbb10fd7a1ce23ae535fa1d42bb730a0979bfead71db07a625bc2407
6
+ metadata.gz: 6f86d27e1c09d25597de9a6f82701aba4ebfe77c29c469857c1cb2b07d492e87485a9f234c957083220df84f03d66d991b1e700a7aa4121782e567a8be9a3cb2
7
+ data.tar.gz: d52836824181062c8e294b56e27d3b6d486b5860d6f13be64d2fe6cefd6c9e98f22d3980a1d35d5ebde74374f3df3c89a4be8bf1b2feb7be4f69ff750c23713c
data/README.md CHANGED
@@ -12,12 +12,12 @@ How to use my plugin.
12
12
  inside of any rails model do
13
13
  ```ruby
14
14
  class SomeModel
15
- acts_as_checkable strategy: proc { |a, b, c| a && (b || c) },
16
- checkers: uk_postcode_checkers
15
+ extend UKPostcodeCheckersConf
16
+ acts_as_checkable uk_postcode_checkers_conf
17
17
  end
18
18
  ```
19
19
  where `uk_postcode_checkers` is method which returns hash with configurations
20
- https://github.com/azazelo/postcode_checker/blob/master/app/models/concerns/u_k_postcode_checkers.rb
20
+ https://github.com/azazelo/postcode_checker/blob/master/app/models/concerns/u_k_postcode_checkers_conf.rb
21
21
  then in your controller:
22
22
  ```ruby
23
23
  class ChecksController < ApplicationController
@@ -30,7 +30,7 @@ class ChecksController < ApplicationController
30
30
  message, alert_class =
31
31
  if @check.valid?
32
32
  @check.perform_check
33
- [@check.messages.join('<br/>'), _alert_class(@check.allowed)]
33
+ [@check.ch_messages.join('<br/>'), _alert_class(@check.ch_allowed)]
34
34
  else
35
35
  [@check.errors.full_messages.join('<br/>'), _alert_class(false)]
36
36
  end
data/lib/checkability.rb CHANGED
@@ -9,5 +9,6 @@ require_relative 'checkability/external_api_checker'
9
9
  require_relative 'checkability/external_api_connector'
10
10
  require_relative 'checkability/validator'
11
11
  require_relative 'checkability/acts_as_checkable'
12
+ require_relative 'checkability/base_checker'
12
13
 
13
14
  ActiveRecord::Base.include Checkability::ActsAsCheckable
@@ -12,35 +12,42 @@ module Checkability
12
12
 
13
13
  class_methods do
14
14
  def acts_as_checkable(options = {})
15
- raise ArgumentError, "Hash expected, got #{options.class.name}" if !options.is_a?(Hash) && !options.empty?
15
+ if !options.is_a?(Hash) && !options.empty?
16
+ raise ArgumentError,
17
+ "Hash expected, got #{options.class.name}"
18
+ end
16
19
 
17
- class_attribute :checkable_conf
20
+ class_attribute :ch_conf
18
21
 
19
- self.checkable_conf = options
22
+ self.ch_conf = options
20
23
  end
21
24
  end
22
25
 
23
- attr_accessor :allowed, :messages
26
+ attr_accessor :ch_allowed, :ch_messages
27
+
28
+ def initialize(params)
29
+ @ch_messages = []
30
+ @ch_allowed = nil
31
+ super(params)
32
+ end
24
33
 
25
34
  def perform_check
26
- _setup
27
- self.allowed = _check
28
- messages << "#{allowed}::'#{value}' is #{_allowness}. "
35
+ Checkability::Checkable.new(self).check(ch_conf)
36
+ ch_messages << "#{ch_allowed}::'#{_value}' is #{_allowness}. "
29
37
  end
30
38
 
31
39
  private
32
40
 
33
- def _setup
34
- self.allowed = nil
35
- self.messages = []
41
+ def _value
42
+ send(_attr_name)
36
43
  end
37
44
 
38
- def _allowness
39
- allowed ? 'ALLOWED' : 'NOT allowed'
45
+ def _attr_name
46
+ ch_conf[:attr_name] || :value
40
47
  end
41
48
 
42
- def _check
43
- Checkability::Checkable.new(self).check(checkable_conf)
49
+ def _allowness
50
+ ch_allowed ? 'ALLOWED' : 'NOT allowed'
44
51
  end
45
52
  end
46
53
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'checker'
4
+
5
+ module Checkability
6
+ # @abstract
7
+ class BaseChecker < Checker
8
+ # @return [Handler]
9
+ attr_reader :stop_process_on_success, :stop_process_on_failure,
10
+ :success_message, :failure_message
11
+
12
+ def initialize(opts = {})
13
+ @stop_process_on_failure = opts[:stop_process_on_failure] || false
14
+ @stop_process_on_success = opts[:stop_process_on_success] || false
15
+ @success_message = opts[:success_message] || 'Success.'
16
+ @failure_message = opts[:failure_message] || 'Failed.'
17
+
18
+ @next_handler = nil
19
+ post_initialize(opts) # implemented in subclass
20
+ end
21
+
22
+ # subclass should implement
23
+ def post_initialize(_opts)
24
+ nil
25
+ end
26
+
27
+ # @param [Handler] handler
28
+ #
29
+ # @return [Handler]
30
+ def next_handler(handler)
31
+ @next_handler = handler
32
+
33
+ handler
34
+ end
35
+
36
+ # @abstract
37
+ #
38
+ # @param [String] request
39
+ #
40
+ # @return [Boolean, nil]
41
+ def handle(check_obj)
42
+ res, mess = result_and_message(check_obj)
43
+ check_obj.ch_messages << mess
44
+ check_obj.ch_allowed = res
45
+
46
+ return if _stop_here?(res)
47
+
48
+ @next_handler&.handle(check_obj) if @next_handler
49
+ end
50
+
51
+ def result_and_message(check_obj = nil)
52
+ res = result(check_obj)
53
+
54
+ str = res ? success_message : failure_message
55
+ [res, message(res, str)]
56
+ # rescue StandardError => e
57
+ # [false, message(false, e)]
58
+ end
59
+
60
+ # subclass should implement
61
+ def result(_check_obj)
62
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
63
+ end
64
+
65
+ # subclass may override
66
+ def message(res, str)
67
+ "#{res}::#{str}"
68
+ end
69
+
70
+ private
71
+
72
+ def _stop_here?(res)
73
+ (res && stop_process_on_success) || (!res && stop_process_on_failure)
74
+ end
75
+ end
76
+ end
@@ -1,40 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  module Checkability
4
6
  # Implements check method to Iterate on chechers
5
7
  # Possible to implemet as Iterator in future
6
8
  #
7
9
  class Checkable
8
- attr_reader :checkable
10
+ attr_accessor :check_obj
11
+
12
+ extend Forwardable
13
+ def_delegators :@check_obj, :ch_messages, :ch_allowed
9
14
 
10
- def initialize(checkable)
11
- @checkable = checkable
15
+ def initialize(check_obj)
16
+ @check_obj = check_obj
12
17
  end
13
18
 
14
- # strategy is a proc
15
- # like { |a,b,c| a && ( b || c ) }
16
- # where a,b,c are checkers
17
- # and each should return true|false
18
- # checker_confs is an array of checker_conf hashes
19
- # e.g. [storage_checker, external_api_checker]
20
- def check(opts = {})
21
- results = []
22
- opts[:checker_confs].each do |checker_conf|
23
- results << (res = _checker_to_check_value(checker_conf))
24
- break if res && checker_conf[:stop_process_if_success]
25
- break if res == false && checker_conf[:stop_process_if_failure]
26
- end
27
- opts[:strategy].call(results)
19
+ # As in result handlers should behave like this:
20
+ # validator .next_handler(storage)
21
+ # storage .next_handler(api_validator)
22
+ # api_validator.next_handler(api_finder)
23
+ # api_validator.next_handler(nil)
24
+ #
25
+ # validator.handle(request)
26
+ #
27
+ def check(handler_confs)
28
+ first_handler_name = handler_confs.keys.first
29
+ first_handler = _handlers(handler_confs)[first_handler_name]
30
+
31
+ first_handler.handle(check_obj)
32
+ rescue StandardError => e
33
+ check_obj.ch_messages << "false::#{e}: #{handler_confs}."
34
+ false
28
35
  end
29
36
 
30
37
  private
31
38
 
32
- def _checker_to_check_value(checker_conf)
33
- k = "Checkability::#{checker_conf[:name].to_s.camelize}".constantize
34
- k.new(checker_conf).check_value(checkable)
35
- rescue NameError => e
36
- checkable.messages << "false::#{e}: #{checker_conf[:name]}."
37
- false
39
+ def _handlers(handler_confs)
40
+ handlers = _make_handlers(handler_confs)
41
+
42
+ handlers.each do |handler_name, handler|
43
+ next_handler_name = handler_confs[handler_name][:next_handler]
44
+ handler.next_handler(handlers[next_handler_name]) if handlers[next_handler_name]
45
+ end
46
+ end
47
+
48
+ def _make_handlers(confs)
49
+ confs.transform_values { |conf| _make_handler(conf) }
50
+ end
51
+
52
+ def _make_handler(conf)
53
+ Checkability.const_get(conf[:name].to_s.camelize)
54
+ .new(conf)
38
55
  end
39
56
  end
40
57
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkability
4
+ # @abstract
5
+ class Checker
6
+ # hook method to initialize concreet attributes
7
+ # @abstract
8
+ #
9
+ # @param [Hash]
10
+ #
11
+ # @return new object
12
+ def post_initialize(_opts)
13
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
14
+ end
15
+
16
+ # @abstract
17
+ #
18
+ # @param [Handler] handler
19
+ def next_handler=(_handler)
20
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
21
+ end
22
+
23
+ # @abstract
24
+ #
25
+ # @param [String] request
26
+ #
27
+ # @return [String, nil]
28
+ def handle(_handler)
29
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
30
+ end
31
+
32
+ # @abstract
33
+ #
34
+ # @param [String] request
35
+ #
36
+ # @return Array [ [Boolean, true|false], String, message] ]
37
+ def result_and_message(_object)
38
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
39
+ end
40
+
41
+ # @abstract
42
+ #
43
+ # @param [Checkable object] request
44
+ #
45
+ # @return [Boolean, true|false]
46
+ def result(_object)
47
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
48
+ end
49
+
50
+ # @abstract
51
+ #
52
+ # @params [Boolean], [String]
53
+ #
54
+ # @return [String]
55
+ def message(_res, _str)
56
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
57
+ end
58
+ end
59
+ end
@@ -1,55 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday'
4
- require 'net/http'
5
- require 'net/https'
6
- require 'json'
3
+ require 'forwardable'
7
4
 
8
5
  module Checkability
9
6
  # Checks if postcode exists in external API
10
7
  #
11
- class ExternalApiChecker
12
- attr_reader :path, :path_suffix, :check_method, :connection, :http_verb,
13
- :failure_message, :success_message
8
+ class ExternalApiChecker < BaseChecker
9
+ attr_reader :path, :path_suffix, :check_method, :connector, :http_verb
14
10
 
15
- def initialize(conf = {})
11
+ extend Forwardable
12
+ def_delegators :@connector, :connection
13
+
14
+ def post_initialize(conf = {})
16
15
  @path = conf[:path]
17
16
  @http_verb = conf[:http_verb] || :get
18
17
  @path_suffix = conf[:path_suffix] || ''
19
18
  @check_method = conf[:check_method]
20
- @failure_message = conf[:failure_message] || 'Failed.'
21
- @success_message = conf[:success_message] || 'Success.'
22
- @connection = Checkability::ExternalApiConnector.new(conf)
19
+ @connector = conf[:connector] ||
20
+ Checkability::ExternalApiConnector.new(conf[:path])
21
+ @resp = nil
23
22
  end
24
23
 
25
- def check_value(checkable)
26
- @resp = connection
27
- .connect
28
- .send(http_verb, "#{checkable.value.delete(' ')}#{path_suffix}")
29
- result, message = _result_and_message
30
- checkable.messages << message
31
- result
24
+ def result(check_obj)
25
+ return false unless resp(check_obj).status == 200
26
+
27
+ check_method.call(_parsed(resp(check_obj)))
32
28
  end
33
29
 
34
- private
30
+ def resp(check_obj)
31
+ @resp ||= connection
32
+ .send(http_verb,
33
+ "#{check_obj.value.delete(' ')}#{path_suffix}")
34
+ # .get('SE17QD')
35
+ end
35
36
 
36
- def _message(str, res)
37
+ def message(res, str = nil)
37
38
  "#{res}::#{path}: #{str}"
38
39
  end
39
40
 
41
+ private
42
+
40
43
  def _parsed(resp)
41
44
  JSON.parse(resp.body)
42
45
  end
43
-
44
- def _result_and_message
45
- return [false, _message(@resp.status, false)] unless @resp.status == 200
46
-
47
- return [true, _message(success_message, true)] if check_method
48
- .call(_parsed(@resp))
49
-
50
- [false, _message(failure_message, false)]
51
- rescue StandardError => e
52
- [false, _message(e, false)]
53
- end
54
46
  end
55
47
  end
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday'
4
+
3
5
  module Checkability
4
6
  # Create connection
5
7
  #
6
8
  class ExternalApiConnector
7
9
  attr_reader :path
8
10
 
9
- def initialize(conf)
10
- @path = conf[:path]
11
+ def initialize(path)
12
+ @path = path
11
13
  end
12
14
 
13
- def connect
15
+ def connection
14
16
  Faraday.new(url: path) do |faraday|
15
17
  faraday.headers['Content-Type'] = 'application/json'
16
18
  faraday.adapter Faraday.default_adapter
@@ -1,32 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'base_checker'
3
4
  module Checkability
4
5
  # Checks if postcode exists in Storage
5
6
  #
6
- class StorageChecker
7
- attr_reader :storage_class
7
+ class StorageChecker < BaseChecker
8
+ attr_reader :storage_class, :attr_name
8
9
 
9
- def initialize(conf = {})
10
+ def post_initialize(conf = {})
10
11
  @storage_class = conf[:storage_class]
12
+ @attr_name = conf[:attr_name] || :value
11
13
  end
12
14
 
13
- def check_value(checkable)
14
- value = checkable.value.upcase
15
- result = _present_in_storage(value)
16
- checkable.messages << (
17
- result ? _message('Found', result) : _message('Not found', result))
18
- result
15
+ def result(checkable)
16
+ value = _normalize_value(checkable.send(attr_name))
17
+ storage_class.where(attr_name => value).present?
19
18
  end
20
19
 
21
- def _present_in_storage(value)
22
- storage_class.where(value: value)
23
- .or(storage_class.where(value: value.strip))
24
- .or(storage_class.where(value: value.delete(' ')))
25
- .present?
20
+ def message(res, str = nil)
21
+ "#{res}::Allowed #{storage_class}s list: #{str}"
26
22
  end
27
23
 
28
- def _message(str, res)
29
- "#{res}::Allowed #{storage_class}s list: #{str}."
24
+ private
25
+
26
+ def _normalize_value(value)
27
+ value.delete(' ').upcase
30
28
  end
31
29
  end
32
30
  end
@@ -3,27 +3,15 @@
3
3
  module Checkability
4
4
  # Checks if postcode comply with regex
5
5
  #
6
- class Validator
6
+ class Validator < BaseChecker
7
7
  attr_reader :format
8
8
 
9
- def initialize(conf = {})
9
+ def post_initialize(conf = {})
10
10
  @format = conf[:format]
11
11
  end
12
12
 
13
- def check_value(checkable)
14
- result, message = _result_and_message(checkable)
15
- checkable.messages << message
16
- result
17
- end
18
-
19
- private
20
-
21
- def _result_and_message(checkable)
22
- if (checkable.value.delete(' ') =~ format[:regex]).nil?
23
- [false, "false::Value is NOT COMPLY with format of #{format[:name]}."]
24
- else
25
- [true, "true::Value is COMPLY with format of #{format[:name]}."]
26
- end
13
+ def result(checkable)
14
+ !(checkable.value.delete(' ') =~ format[:regex]).nil?
27
15
  end
28
16
  end
29
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Checkability
4
- VERSION = '0.5.0'
4
+ VERSION = '1.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkability
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Eremeev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-01 00:00:00.000000000 Z
11
+ date: 2021-04-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Provide Checkers functionality.
14
14
  email:
@@ -24,7 +24,9 @@ files:
24
24
  - bin/checkability
25
25
  - lib/checkability.rb
26
26
  - lib/checkability/acts_as_checkable.rb
27
+ - lib/checkability/base_checker.rb
27
28
  - lib/checkability/checkable.rb
29
+ - lib/checkability/checker.rb
28
30
  - lib/checkability/external_api_checker.rb
29
31
  - lib/checkability/external_api_connector.rb
30
32
  - lib/checkability/railtie.rb