lib/convenient_service/rspec/matchers/custom/results/base.rb in convenient_service-0.12.0 vs lib/convenient_service/rspec/matchers/custom/results/base.rb in convenient_service-0.13.0
- old
+ new
@@ -1,314 +1,277 @@
# frozen_string_literal: true
-require_relative "base/commands"
require_relative "base/constants"
-require_relative "base/errors"
+require_relative "base/entities"
+require_relative "base/exceptions"
module ConvenientService
module RSpec
module Matchers
module Custom
module Results
class Base
include Support::AbstractMethod
##
+ # @api private
+ #
+ # @!attribute [r] result
+ # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
+ #
+ attr_reader :result
+
+ ##
+ # @api private
+ #
# @return [String, Symbol]
#
abstract_method :statuses
##
- # @param result [ConvenientService::Service::Plugins::HasResult::Entities::Result]
+ # @api private
+ #
+ # @return [void]
+ #
+ def initialize(statuses: self.statuses, result: nil)
+ chain.statuses = statuses
+
+ @result = result
+ end
+
+ ##
+ # @api public
+ #
+ # @param result [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @return [Boolean]
#
def matches?(result)
@result = result
- rules = []
-
##
- # IMPORTANT: Makes `result.class.include?` from the following line idempotent.
+ # IMPORTANT: Makes `result.class.include?` idempotent.
#
- result.commit_config!(trigger: Constants::Triggers::BE_RESULT) if result.respond_to?(:commit_config!)
-
- rules << ->(result) { result.class.include?(Service::Plugins::HasResult::Entities::Result::Concern) }
-
- ##
- # IMPORTANT: Result status is NOT marked as checked intentionally, since it is a mutable operation.
+ # TODO: Explainer when `result.class.commit_config!` is required. It was introduced in panic when the first thread-safety issues occurred. Looks like it is an outdated operation now. It is probably useful only when a config has almost zero plugins.
#
- rules << ->(result) { result.status.in?(statuses) }
+ # TODO: Resolve a bug in `delegate_to`. It makes `respond_to?` always return `true`, even for classes that do NOT have the `commit_config!` method. See specs for details.
+ #
+ # let(:result) { "foo" }
+ #
+ # specify do
+ # expect { matcher.matches?(result) }
+ # .not_to delegate_to(result.class, :commit_config!)
+ # .with_any_arguments
+ # .without_calling_original
+ # end
+ #
+ # `result.class.method(:commit_config!)`
+ # # => <Method: String.commit_config!(*args, &block) /usr/local/bundle/gems/rspec-mocks-3.11.2/lib/rspec/mocks/method_double.rb:63>
+ #
+ result.class.commit_config!(trigger: Constants::Triggers::BE_RESULT) if result.class.respond_to?(:commit_config!)
- rules << ->(result) { result.service.instance_of?(service_class) } if used_of_service?
- rules << ->(result) { Commands::MatchResultStep.call(result: result, step: step) } if used_of_step?
- rules << ->(result) { result.unsafe_data == data } if used_data?
- rules << ->(result) { result.unsafe_message == message } if used_message?
- rules << ->(result) { result.unsafe_code == code } if used_code?
-
- condition = Utils::Proc.conjunct(rules)
-
- condition.call(result)
+ validator.valid_result?
end
##
+ # @api public
+ #
# @return [String]
#
def description
- expected_parts
+ printer.description
end
##
+ # @api public
+ #
# @return [String]
#
def failure_message
- "expected that `#{result.service.class}` result would #{default_text}"
+ printer.failure_message
end
##
+ # @api public
+ #
# @return [String]
#
# @internal
- # https://relishapp.com/rspec/rspec-expectations/v/3-11/docs/custom-matchers/define-a-custom-matcher#overriding-the-failure-message-when-negated
+ # - https://relishapp.com/rspec/rspec-expectations/v/3-11/docs/custom-matchers/define-a-custom-matcher#overriding-the-failure-message-when-negated
#
def failure_message_when_negated
- "expected that `#{result.service.class}` result would NOT #{default_text}"
+ printer.failure_message_when_negated
end
##
+ # @api public
+ #
# @param data [Hash]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def with_data(data)
- chain[:data] = data
+ chain.data = data
self
end
##
+ # @api public
+ #
# @param data [Hash]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def and_data(data)
- chain[:data] = data
+ chain.data = data
self
end
##
+ # @api public
+ #
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def without_data
- chain[:data] = {}
+ chain.data = {}
self
end
##
+ # @api public
+ #
# @param message [String]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def with_message(message)
- chain[:message] = message
+ chain.message = message
self
end
##
+ # @api public
+ #
# @param message [String]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def and_message(message)
- chain[:message] = message
+ chain.message = message
self
end
##
+ # @api public
+ #
# @return [String, Symbol]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def with_code(code)
- chain[:code] = code
+ chain.code = code
self
end
##
+ # @api public
+ #
# @return [String, Symbol]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def and_code(code)
- chain[:code] = code
+ chain.code = code
self
end
##
- # @param service_class [Class]
+ # @api public
+ #
+ # @param service [ConvenientService::Service]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
- def of_service(service_class)
- chain[:service_class] = service_class
+ def of_service(service)
+ chain.service = service
self
end
##
+ # @api public
+ #
# @param step [Class, Symbol]
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def of_step(step)
- chain[:step] = step
+ chain.step = step
self
end
##
+ # @api public
+ #
# @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
def without_step
- chain[:step] = nil
+ chain.step = nil
self
end
- private
-
##
- # @!attribute [r] result
- # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result]
+ # @api public
#
- attr_reader :result
-
- ##
- # @return [String]
+ # @param comparison_method [Symbo, String]
+ # @return [ConvenientService::RSpec::Matchers::Custom::Results::Base]
#
- def default_text
- expected_parts << "\n\n" << got_parts
- end
+ def comparing_by(comparison_method)
+ chain.comparison_method = comparison_method
- ##
- # @return [String]
- #
- # @internal
- # TODO: Align for easier visual comparison.
- # TODO: New line for each attribute.
- #
- def expected_parts
- parts = []
-
- parts << "be #{printable_statuses}"
- parts << "of service `#{service_class}`" if used_of_service?
- parts << Commands::GenerateExpectedStepPart.call(step: step) if used_of_step?
- parts << "with data `#{data}`" if used_data?
- parts << "with message `#{message}`" if used_message?
- parts << "with code `#{code}`" if used_code?
-
- parts.join(" ")
+ self
end
##
- # @return [String]
+ # @api private
#
- # @internal
- # TODO: Align for easier visual comparison.
- # TODO: New line for each attribute.
+ # @return [ConvenientService::RSpec::Matchers::Custom::Results::Base::Entities::Chain]
#
- def got_parts
- parts = []
-
- parts << "got `#{result.status}`"
- parts << "of service `#{result.service.class}`" if used_of_service?
- parts << Commands::GenerateGotStepPart.call(result: result) if used_of_step?
- parts << "with data `#{result.unsafe_data}`" if used_data?
- parts << "with message `#{result.unsafe_message}`" if used_message?
- parts << "with code `#{result.unsafe_code}`" if used_code?
-
- parts.join(" ")
+ def chain
+ @chain ||= Entities::Chain.new
end
##
- # @return [Boolean]
+ # @api private
#
- def used_data?
- chain.key?(:data)
- end
-
- ##
- # @return [Boolean]
+ # @return [ConvenientService::RSpec::Matchers::Custom::Results::Base::Entities::Printers::Base]
#
- def used_message?
- chain.key?(:message)
+ def printer
+ @printer ||= Entities::Printers.create(matcher: self)
end
##
- # @return [Boolean]
+ # @api private
#
- def used_code?
- chain.key?(:code)
- end
-
- ##
- # @return [Boolean]
+ # @return [ConvenientService::RSpec::Matchers::Custom::Results::Base::Entities::Validator]
#
- def used_of_service?
- chain.key?(:service_class)
+ def validator
+ @validator ||= Entities::Validator.new(matcher: self)
end
##
- # @return [Boolean]
+ # @api private
#
- def used_of_step?
- chain.key?(:step)
- end
-
- ##
- # @return [Hash]
+ # @param other [Object] Can be any type.
+ # @return [Boolean, nil]
#
- def data
- @data ||= chain[:data] || {}
- end
+ def ==(other)
+ return unless other.instance_of?(self.class)
- ##
- # @return [String]
- #
- def message
- @message ||= chain[:message] || ""
- end
+ return false if chain != other.chain
+ return false if result != other.result
- ##
- # @return [String, Symbol]
- #
- def code
- @code ||= chain[:code] || ""
- end
-
- ##
- # @return [Class]
- #
- def service_class
- Utils::Object.instance_variable_fetch(self, :@service_class) { chain[:service_class] }
- end
-
- ##
- # @return [Class]
- #
- def step
- Utils::Object.instance_variable_fetch(self, :@step) { chain[:step] }
- end
-
- ##
- # @return [Hash]
- #
- def chain
- @chain ||= {}
- end
-
- ##
- # @return [String]
- #
- def printable_statuses
- statuses.map { |status| "`#{status}`" }.join(" or ")
+ true
end
end
end
end
end