checkability 0.5.0 → 1.0.0

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