rspec-sleeping_king_studios 2.1.1 → 2.2.0.rc.1
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 +4 -4
- data/CHANGELOG.md +52 -0
- data/DEVELOPMENT.md +26 -17
- data/LICENSE +1 -1
- data/README.md +198 -19
- data/lib/rspec/sleeping_king_studios/configuration.rb +63 -2
- data/lib/rspec/sleeping_king_studios/examples/property_examples.rb +115 -27
- data/lib/rspec/sleeping_king_studios/examples/rspec_matcher_examples.rb +66 -51
- data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors.rb +8 -269
- data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb +268 -0
- data/lib/rspec/sleeping_king_studios/matchers/base_matcher.rb +5 -15
- data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb +3 -63
- data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of_matcher.rb +65 -0
- data/lib/rspec/sleeping_king_studios/matchers/built_in/include.rb +3 -108
- data/lib/rspec/sleeping_king_studios/matchers/built_in/include_matcher.rb +134 -0
- data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to.rb +3 -258
- data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to_matcher.rb +116 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/alias_method.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/alias_method_matcher.rb +107 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb +9 -36
- data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean_matcher.rb +37 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/construct.rb +3 -210
- data/lib/rspec/sleeping_king_studios/matchers/core/construct_matcher.rb +113 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method_matcher.rb +311 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_constant.rb +16 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_constant_matcher.rb +225 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate_matcher.rb +97 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_property.rb +3 -104
- data/lib/rspec/sleeping_king_studios/matchers/core/have_property_matcher.rb +108 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb +3 -74
- data/lib/rspec/sleeping_king_studios/matchers/core/have_reader_matcher.rb +96 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb +4 -59
- data/lib/rspec/sleeping_king_studios/matchers/core/have_writer_matcher.rb +55 -0
- data/lib/rspec/sleeping_king_studios/matchers/description.rb +62 -0
- data/lib/rspec/sleeping_king_studios/matchers/macros.rb +32 -0
- data/lib/rspec/sleeping_king_studios/matchers/shared/match_parameters.rb +168 -66
- data/lib/rspec/sleeping_king_studios/matchers/shared/match_property.rb +25 -0
- data/lib/rspec/sleeping_king_studios/matchers.rb +0 -4
- data/lib/rspec/sleeping_king_studios/support/method_signature.rb +51 -0
- data/lib/rspec/sleeping_king_studios/support/method_signature_expectation.rb +158 -0
- data/lib/rspec/sleeping_king_studios/support.rb +9 -0
- data/lib/rspec/sleeping_king_studios/version.rb +10 -4
- metadata +46 -30
@@ -0,0 +1,96 @@
|
|
1
|
+
# subl lib/rspec/sleeping_king_studios/matchers/core/have_reader_matcher.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/base_matcher'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/core'
|
5
|
+
require 'rspec/sleeping_king_studios/matchers/shared/match_property'
|
6
|
+
|
7
|
+
module RSpec::SleepingKingStudios::Matchers::Core
|
8
|
+
# Matcher for testing whether an object has a specific property reader, e.g.
|
9
|
+
# responds to :property.
|
10
|
+
#
|
11
|
+
# @since 1.0.0
|
12
|
+
class HaveReaderMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
|
13
|
+
include RSpec::SleepingKingStudios::Matchers::Shared::MatchProperty
|
14
|
+
|
15
|
+
# (see BaseMatcher#description)
|
16
|
+
def description
|
17
|
+
value_message = value_to_string
|
18
|
+
"have reader :#{@expected}#{@value_set ? " with value #{value_message}" : ''}"
|
19
|
+
end # method description
|
20
|
+
|
21
|
+
# @param [String, Symbol] expected The property to check for on the actual
|
22
|
+
# object.
|
23
|
+
def initialize expected
|
24
|
+
@expected = expected.intern
|
25
|
+
end # method initialize
|
26
|
+
|
27
|
+
# (see BaseMatcher#does_not_match?)
|
28
|
+
def does_not_match? actual
|
29
|
+
super
|
30
|
+
|
31
|
+
matches_reader?(:none?)
|
32
|
+
end # method does_not_match?
|
33
|
+
|
34
|
+
# Checks if the object responds to #expected. Additionally, if a value
|
35
|
+
# expectation is set, compares the value of #expected to the specified
|
36
|
+
# value.
|
37
|
+
#
|
38
|
+
# @param [Object] actual The object to check.
|
39
|
+
#
|
40
|
+
# @return [Boolean] true If the object responds to #expected and matches
|
41
|
+
# the value expectation (if any); otherwise false.
|
42
|
+
def matches? actual
|
43
|
+
super
|
44
|
+
|
45
|
+
matches_reader?(:all?)
|
46
|
+
end # method matches?
|
47
|
+
|
48
|
+
# Sets a value expectation. The matcher will compare the value from
|
49
|
+
# #property with the specified value.
|
50
|
+
#
|
51
|
+
# @param [Object] value The value to compare.
|
52
|
+
#
|
53
|
+
# @return [HaveReaderMatcher] self
|
54
|
+
def with value
|
55
|
+
@value = value
|
56
|
+
@value_set = true
|
57
|
+
self
|
58
|
+
end # method with
|
59
|
+
alias_method :with_value, :with
|
60
|
+
|
61
|
+
# (see BaseMatcher#failure_message)
|
62
|
+
def failure_message
|
63
|
+
message = "expected #{@actual.inspect} to respond to :#{@expected}"
|
64
|
+
message << " and return #{value_to_string}" if @value_set
|
65
|
+
|
66
|
+
if !@matches_reader
|
67
|
+
message << ", but did not respond to :#{@expected}"
|
68
|
+
elsif !@matches_reader_value
|
69
|
+
message << ", but returned #{@actual.send(@expected).inspect}"
|
70
|
+
end # if
|
71
|
+
|
72
|
+
message
|
73
|
+
end # method failure_message
|
74
|
+
|
75
|
+
# (see BaseMatcher#failure_message_when_negated)
|
76
|
+
def failure_message_when_negated
|
77
|
+
message = "expected #{@actual.inspect} not to respond to :#{@expected}"
|
78
|
+
message << " and return #{value_to_string}" if @value_set
|
79
|
+
|
80
|
+
errors = []
|
81
|
+
errors << "responded to :#{@expected}" if @matches_reader
|
82
|
+
errors << "returned #{@actual.send(@expected).inspect}" if @matches_reader_value
|
83
|
+
|
84
|
+
message << ", but #{errors.join(" and ")}"
|
85
|
+
message
|
86
|
+
end # method failure_message
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def matches_reader? filter
|
91
|
+
[ responds_to_reader?,
|
92
|
+
matches_reader_value?
|
93
|
+
].send(filter) { |bool| bool }
|
94
|
+
end # method matches_property?
|
95
|
+
end # class
|
96
|
+
end # module
|
@@ -1,64 +1,9 @@
|
|
1
|
-
# lib/rspec/sleeping_king_studios/matchers/core/
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb
|
2
2
|
|
3
|
-
require 'rspec/sleeping_king_studios/matchers/
|
4
|
-
require 'rspec/sleeping_king_studios/matchers/
|
5
|
-
require 'rspec/sleeping_king_studios/matchers/shared/match_property'
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/core/have_writer_matcher'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/macros'
|
6
5
|
|
7
|
-
module RSpec::SleepingKingStudios::Matchers::
|
8
|
-
# Matcher for testing whether an object has a specific property writer, e.g.
|
9
|
-
# responds to :property= and updates the state.
|
10
|
-
#
|
11
|
-
# @since 1.0.0
|
12
|
-
class HaveWriterMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
|
13
|
-
include RSpec::SleepingKingStudios::Matchers::Shared::MatchProperty
|
14
|
-
|
15
|
-
# Generates a description of the matcher expectation.
|
16
|
-
#
|
17
|
-
# @return [String] The matcher description.
|
18
|
-
def description
|
19
|
-
"have writer :#{@expected}"
|
20
|
-
end # method description
|
21
|
-
|
22
|
-
# @param [String, Symbol] expected the property to check for on the actual
|
23
|
-
# object
|
24
|
-
def initialize expected
|
25
|
-
@expected = expected.to_s.gsub(/=$/,'').intern
|
26
|
-
end # method initialize
|
27
|
-
|
28
|
-
# Checks if the object responds to :expected=. Additionally, if a value
|
29
|
-
# expectation is set, assigns the value via :expected= and compares the
|
30
|
-
# subsequent value to the specified value using :expected or the block
|
31
|
-
# provided to #with.
|
32
|
-
#
|
33
|
-
# @param [Object] actual the object to check
|
34
|
-
#
|
35
|
-
# @return [Boolean] true if the object responds to :expected= and matches
|
36
|
-
# the value expectation (if any); otherwise false
|
37
|
-
def matches? actual
|
38
|
-
super
|
39
|
-
|
40
|
-
responds_to_writer?
|
41
|
-
end # method matches?
|
42
|
-
|
43
|
-
# @see BaseMatcher#failure_message
|
44
|
-
def failure_message
|
45
|
-
message = "expected #{@actual.inspect} to respond to :#{@expected}="
|
46
|
-
|
47
|
-
if !@matches_writer
|
48
|
-
message << ", but did not respond to :#{@expected}="
|
49
|
-
end # if
|
50
|
-
|
51
|
-
message
|
52
|
-
end # method failure_message
|
53
|
-
|
54
|
-
# @see BaseMatcher#failure_message_when_negated
|
55
|
-
def failure_message_when_negated
|
56
|
-
"expected #{@actual.inspect} not to respond to :#{@expected}="
|
57
|
-
end # method failure_message
|
58
|
-
end # class
|
59
|
-
end # module
|
60
|
-
|
61
|
-
module RSpec::SleepingKingStudios::Matchers
|
6
|
+
module RSpec::SleepingKingStudios::Matchers::Macros
|
62
7
|
# @see RSpec::SleepingKingStudios::Matchers::Core::HaveWriterMatcher#matches?
|
63
8
|
def have_writer expected
|
64
9
|
RSpec::SleepingKingStudios::Matchers::Core::HaveWriterMatcher.new expected
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/core/have_writer_matcher.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers/base_matcher'
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/core'
|
5
|
+
require 'rspec/sleeping_king_studios/matchers/shared/match_property'
|
6
|
+
|
7
|
+
module RSpec::SleepingKingStudios::Matchers::Core
|
8
|
+
# Matcher for testing whether an object has a specific property writer, e.g.
|
9
|
+
# responds to :property= and updates the state.
|
10
|
+
#
|
11
|
+
# @since 1.0.0
|
12
|
+
class HaveWriterMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
|
13
|
+
include RSpec::SleepingKingStudios::Matchers::Shared::MatchProperty
|
14
|
+
|
15
|
+
# Generates a description of the matcher expectation.
|
16
|
+
#
|
17
|
+
# @return [String] The matcher description.
|
18
|
+
def description
|
19
|
+
"have writer :#{@expected}"
|
20
|
+
end # method description
|
21
|
+
|
22
|
+
# @param [String, Symbol] expected the property to check for on the actual
|
23
|
+
# object
|
24
|
+
def initialize expected
|
25
|
+
@expected = expected.to_s.gsub(/=$/,'').intern
|
26
|
+
end # method initialize
|
27
|
+
|
28
|
+
# Checks if the object responds to :expected=. Additionally, if a value
|
29
|
+
# expectation is set, assigns the value via :expected= and compares the
|
30
|
+
# subsequent value to the specified value using :expected or the block
|
31
|
+
# provided to #with.
|
32
|
+
#
|
33
|
+
# @param [Object] actual the object to check
|
34
|
+
#
|
35
|
+
# @return [Boolean] true if the object responds to :expected= and matches
|
36
|
+
# the value expectation (if any); otherwise false
|
37
|
+
def matches? actual
|
38
|
+
super
|
39
|
+
|
40
|
+
responds_to_writer?
|
41
|
+
end # method matches?
|
42
|
+
|
43
|
+
# @see BaseMatcher#failure_message
|
44
|
+
def failure_message
|
45
|
+
"expected #{@actual.inspect} to respond to :#{@expected}="\
|
46
|
+
", but did not respond to :#{@expected}="
|
47
|
+
end # method failure_message
|
48
|
+
|
49
|
+
# @see BaseMatcher#failure_message_when_negated
|
50
|
+
def failure_message_when_negated
|
51
|
+
"expected #{@actual.inspect} not to respond to :#{@expected}="\
|
52
|
+
", but responded to :#{@expected}="
|
53
|
+
end # method failure_message
|
54
|
+
end # class
|
55
|
+
end # module
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/description.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers'
|
4
|
+
|
5
|
+
require 'sleeping_king_studios/tools/array_tools'
|
6
|
+
require 'sleeping_king_studios/tools/string_tools'
|
7
|
+
|
8
|
+
module RSpec::SleepingKingStudios::Matchers
|
9
|
+
# Reusable logic for building a matcher description across different versions
|
10
|
+
# of the base RSpec library.
|
11
|
+
#
|
12
|
+
# @since 2.2.0
|
13
|
+
module Description
|
14
|
+
# @api private
|
15
|
+
DEFAULT_EXPECTED_ITEMS = Object.new
|
16
|
+
|
17
|
+
# A short string that describes the purpose of the matcher.
|
18
|
+
#
|
19
|
+
# @return [String] the matcher description
|
20
|
+
def description
|
21
|
+
desc = matcher_name
|
22
|
+
|
23
|
+
desc << format_expected_items
|
24
|
+
|
25
|
+
desc
|
26
|
+
end # method description
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def expected_items_for_description
|
31
|
+
defined?(@expected) ? @expected : DEFAULT_EXPECTED_ITEMS
|
32
|
+
end # method expected_items_for_description
|
33
|
+
|
34
|
+
def format_expected_items
|
35
|
+
expected_items = expected_items_for_description
|
36
|
+
|
37
|
+
return '' if expected_items == DEFAULT_EXPECTED_ITEMS
|
38
|
+
|
39
|
+
if defined?(RSpec::Matchers::EnglishPhrasing)
|
40
|
+
# RSpec 3.4+
|
41
|
+
RSpec::Matchers::EnglishPhrasing.list(expected_items)
|
42
|
+
elsif defined?(to_sentence)
|
43
|
+
# RSpec 3.0-3.3
|
44
|
+
to_sentence(expected_items)
|
45
|
+
else
|
46
|
+
array_tools = ::SleepingKingStudios::Tools::ArrayTools
|
47
|
+
processed = [expected_items].flatten.map(&:inspect)
|
48
|
+
|
49
|
+
' ' << array_tools.humanize_list(processed)
|
50
|
+
end # if-elsif-else
|
51
|
+
end # method format_expected_items
|
52
|
+
|
53
|
+
def matcher_name
|
54
|
+
return @matcher_name if @matcher_name
|
55
|
+
|
56
|
+
string_tools = ::SleepingKingStudios::Tools::StringTools
|
57
|
+
name = string_tools.underscore(self.class.name.split('::').last)
|
58
|
+
|
59
|
+
@matcher_name = name.tr('_', ' ').sub(/ matcher\z/, '')
|
60
|
+
end # method matcher_name
|
61
|
+
end # module
|
62
|
+
end # module
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/matchers/macros.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/matchers'
|
4
|
+
|
5
|
+
module RSpec::SleepingKingStudios::Matchers
|
6
|
+
module Macros
|
7
|
+
# @see RSpec::Matchers::alias_matcher
|
8
|
+
def self.alias_matcher(new_name, old_name, options = {}, &description_override)
|
9
|
+
description_override ||= if defined?(RSpec::Matchers::Pretty)
|
10
|
+
->(str) { str.gsub(RSpec::Matchers::Pretty.split_words(old_name), RSpec::Matchers::Pretty.split_words(new_name)) }
|
11
|
+
elsif defined?(RSpec::Matchers::EnglishPhrasing)
|
12
|
+
->(str) { str.gsub(RSpec::Matchers::EnglishPhrasing.split_words(old_name), RSpec::Matchers::EnglishPhrasing.split_words(new_name)) }
|
13
|
+
else
|
14
|
+
->(str) { str }
|
15
|
+
end # if-elsif-else
|
16
|
+
|
17
|
+
klass = (options.is_a?(Hash) ? options[:klass] : nil) || RSpec::Matchers::AliasedMatcher
|
18
|
+
define_method(new_name) do |*args, &block|
|
19
|
+
matcher = __send__(old_name, *args, &block)
|
20
|
+
|
21
|
+
klass.new(matcher, description_override)
|
22
|
+
end # define_method
|
23
|
+
end # class method alias_matcher
|
24
|
+
end # module
|
25
|
+
|
26
|
+
# Ensure macros are defined on parent module for compatibility reasons.
|
27
|
+
include Macros
|
28
|
+
end # module
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
config.include RSpec::SleepingKingStudios::Matchers::Macros
|
32
|
+
end # configuration
|
@@ -1,89 +1,191 @@
|
|
1
1
|
# lib/rspec/sleeping_king_studios/matchers/shared/parameters_matcher.rb
|
2
2
|
|
3
3
|
require 'rspec/sleeping_king_studios/matchers'
|
4
|
+
require 'sleeping_king_studios/tools/array_tools'
|
5
|
+
require 'rspec/sleeping_king_studios/support/method_signature_expectation'
|
4
6
|
|
5
7
|
module RSpec::SleepingKingStudios::Matchers::Shared
|
6
8
|
# Helper methods for checking the parameters and keywords (Ruby 2.0 only) of
|
7
9
|
# a method.
|
8
10
|
module MatchParameters
|
9
|
-
#
|
10
|
-
# arguments.
|
11
|
+
# Convenience method for more fluent specs. Does nothing and returns self.
|
11
12
|
#
|
12
|
-
# @
|
13
|
-
|
13
|
+
# @return self
|
14
|
+
def argument
|
15
|
+
self
|
16
|
+
end # method argument
|
17
|
+
alias_method :arguments, :argument
|
18
|
+
|
19
|
+
# Adds a parameter count expectation and/or one or more keyword
|
20
|
+
# expectations.
|
14
21
|
#
|
15
|
-
# @
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
# @overload with count
|
23
|
+
# Adds a parameter count expectation.
|
24
|
+
#
|
25
|
+
# @param [Integer, Range, nil] count (optional) The number of expected
|
26
|
+
# parameters.
|
27
|
+
#
|
28
|
+
# @return [RespondToMatcher] self
|
29
|
+
# @overload with *keywords
|
30
|
+
# Adds one or more keyword expectations.
|
31
|
+
#
|
32
|
+
# @param [Array<String, Symbol>] keywords List of keyword arguments
|
33
|
+
# accepted by the method.
|
34
|
+
#
|
35
|
+
# @return self
|
36
|
+
# @overload with count, *keywords
|
37
|
+
# Adds a parameter count expectation and one or more keyword
|
38
|
+
# expectations.
|
39
|
+
#
|
40
|
+
# @param [Integer, Range, nil] count (optional) The number of expected
|
41
|
+
# parameters.
|
42
|
+
# @param [Array<String, Symbol>] keywords List of keyword arguments
|
43
|
+
# accepted by the method.
|
44
|
+
#
|
45
|
+
# @return self
|
46
|
+
def with *keywords
|
47
|
+
case keywords.first
|
48
|
+
when Range
|
49
|
+
arity = keywords.shift
|
50
|
+
|
51
|
+
method_signature_expectation.min_arguments = arity.begin
|
52
|
+
method_signature_expectation.max_arguments = arity.end
|
53
|
+
when Integer
|
54
|
+
arity = keywords.shift
|
55
|
+
|
56
|
+
method_signature_expectation.min_arguments = arity
|
57
|
+
method_signature_expectation.max_arguments = arity
|
58
|
+
end # case
|
35
59
|
|
36
|
-
|
37
|
-
end # method check_method_arity
|
60
|
+
method_signature_expectation.keywords = keywords
|
38
61
|
|
39
|
-
|
62
|
+
self
|
63
|
+
end # method with
|
64
|
+
|
65
|
+
# Adds a block expectation. The actual object will only match a block
|
66
|
+
# expectation if it expects a parameter of the form &block.
|
40
67
|
#
|
41
|
-
# @
|
42
|
-
|
68
|
+
# @return self
|
69
|
+
def with_a_block
|
70
|
+
method_signature_expectation.block_argument = true
|
71
|
+
|
72
|
+
self
|
73
|
+
end # method with_a_block
|
74
|
+
alias_method :and_a_block, :with_a_block
|
75
|
+
|
76
|
+
# Adds an arbitrary keyword expectation, e.g. that the method supports
|
77
|
+
# any keywords with splatted hash arguments of the form **kwargs.
|
78
|
+
def with_arbitrary_keywords
|
79
|
+
method_signature_expectation.any_keywords = true
|
80
|
+
|
81
|
+
self
|
82
|
+
end # method with_arbitrary_keywords
|
83
|
+
alias_method :and_arbitrary_keywords, :with_arbitrary_keywords
|
84
|
+
alias_method :with_any_keywords, :with_arbitrary_keywords
|
85
|
+
alias_method :and_any_keywords, :with_any_keywords
|
86
|
+
|
87
|
+
# Adds one or more keyword expectations.
|
88
|
+
#
|
89
|
+
# @param [Array<String, Symbol>] keywords List of keyword arguments
|
90
|
+
# accepted by the method.
|
91
|
+
#
|
92
|
+
# @return self
|
93
|
+
def with_keywords *keywords
|
94
|
+
method_signature_expectation.keywords = keywords
|
95
|
+
|
96
|
+
self
|
97
|
+
end # method with_keywords
|
98
|
+
alias_method :and_keywords, :with_keywords
|
99
|
+
|
100
|
+
# Adds an unlimited parameter count expectation, e.g. that the method
|
101
|
+
# supports splatted array arguments of the form *args.
|
43
102
|
#
|
44
|
-
# @return
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
103
|
+
# @return self
|
104
|
+
def with_unlimited_arguments
|
105
|
+
method_signature_expectation.unlimited_arguments = true
|
106
|
+
|
107
|
+
self
|
108
|
+
end # method with_unlimited_arguments
|
109
|
+
alias_method :and_unlimited_arguments, :with_unlimited_arguments
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
def check_method_signature method
|
115
|
+
method_signature_expectation.matches?(method)
|
116
|
+
end # method check_method_signature
|
117
|
+
|
118
|
+
# @api private
|
119
|
+
def format_errors errors
|
120
|
+
messages = []
|
121
|
+
|
122
|
+
# TODO: Replace this with " expected :arity arguments, but method can "\
|
123
|
+
# "receive :count arguments", with :count being one of the following:
|
124
|
+
# - an integer (for methods that do not have optional or variadic params)
|
125
|
+
# - between :min and :max (for methods with optional but not variadic
|
126
|
+
# params)
|
127
|
+
# - at least :min (for methods with variadic params)
|
128
|
+
if hsh = errors.fetch(:not_enough_args, false)
|
129
|
+
messages << " expected at least #{hsh[:expected]} arguments, but received #{hsh[:received]}"
|
59
130
|
end # if
|
60
131
|
|
61
|
-
|
62
|
-
|
132
|
+
if hsh = errors.fetch(:too_many_args, false)
|
133
|
+
messages << " expected at most #{hsh[:expected]} arguments, but received #{hsh[:received]}"
|
134
|
+
end # if
|
63
135
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end # each
|
136
|
+
# TODO: Replace this with " expected method to receive unlimited "\
|
137
|
+
# "arguments, but method can receive at most :max arguments"
|
138
|
+
if hsh = errors.fetch(:no_variadic_args, false)
|
139
|
+
messages << " expected at most #{hsh[:expected]} arguments, but received unlimited arguments"
|
140
|
+
end # if
|
70
141
|
|
71
|
-
|
72
|
-
|
142
|
+
# TODO: Replace this with " expected method to receive arbitrary "\
|
143
|
+
# "keywords, but the method can receive :keyword_list", with
|
144
|
+
# :keyword_list being a comma-separated list. If the method cannot
|
145
|
+
# receive keywords, replace last fragment with ", but the method cannot"\
|
146
|
+
# " receive keywords"
|
147
|
+
if errors.fetch(:no_variadic_keywords, false)
|
148
|
+
messages << " expected arbitrary keywords"
|
149
|
+
end # if
|
73
150
|
|
74
|
-
|
75
|
-
|
151
|
+
# TODO: Replace this with " expected method to receive keywords "\
|
152
|
+
# ":received_list, but the method requires keywords :required_list"
|
153
|
+
if ary = errors.fetch(:missing_keywords, false)
|
154
|
+
tools = ::SleepingKingStudios::Tools::ArrayTools
|
76
155
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
156
|
+
messages <<
|
157
|
+
" missing keyword#{ary.count == 1 ? '' : 's'} "\
|
158
|
+
"#{tools.humanize_list ary.map(&:inspect)}"
|
159
|
+
end # if
|
160
|
+
|
161
|
+
# TODO: Replace this with " expected method to receive keywords "\
|
162
|
+
# ":received_list, but the method can receive :keyword_list"
|
163
|
+
if ary = errors.fetch(:unexpected_keywords, false)
|
164
|
+
tools = ::SleepingKingStudios::Tools::ArrayTools
|
165
|
+
|
166
|
+
messages <<
|
167
|
+
" unexpected keyword#{ary.count == 1 ? '' : 's'} "\
|
168
|
+
"#{tools.humanize_list ary.map(&:inspect)}"
|
169
|
+
end # if
|
170
|
+
|
171
|
+
# TODO: Replace this with " expected method to receive a block "\
|
172
|
+
# "argument, but the method signature does not specify a block argument"
|
173
|
+
if errors.fetch(:no_block_argument, false)
|
174
|
+
messages << " unexpected block"
|
175
|
+
end # if
|
176
|
+
|
177
|
+
messages.join "\n"
|
178
|
+
end # method format_errors
|
179
|
+
|
180
|
+
# @api private
|
181
|
+
def method_signature_expectation
|
182
|
+
@method_signature_expectation ||=
|
183
|
+
::RSpec::SleepingKingStudios::Support::MethodSignatureExpectation.new
|
184
|
+
end # method_signature_expectation
|
86
185
|
|
87
|
-
|
186
|
+
# @api private
|
187
|
+
def method_signature_expectation?
|
188
|
+
!!@method_signature_expectation
|
189
|
+
end # method_signature_expectation
|
88
190
|
end # module
|
89
191
|
end # module
|
@@ -5,6 +5,23 @@ module RSpec::SleepingKingStudios::Matchers::Shared
|
|
5
5
|
module MatchProperty
|
6
6
|
private
|
7
7
|
|
8
|
+
# Checks whether the value of the predicate matches the expected value. If
|
9
|
+
# the value looks like an RSpec matcher (it responds to :matches?), runs
|
10
|
+
# value.matches?(); otherwise checks for equality using :==.
|
11
|
+
#
|
12
|
+
# @return [Boolean] true if the value matches the expected value; otherwise
|
13
|
+
# false.
|
14
|
+
def matches_predicate_value?
|
15
|
+
return false unless responds_to_predicate?
|
16
|
+
return true unless @value_set
|
17
|
+
|
18
|
+
actual_value = @actual.send(:"#{@expected}?")
|
19
|
+
|
20
|
+
@matches_predicate_value = (@value.respond_to?(:matches?) && @value.respond_to?(:description)) ?
|
21
|
+
@value.matches?(actual_value) :
|
22
|
+
@value == actual_value
|
23
|
+
end # method matches_reader_value?
|
24
|
+
|
8
25
|
# Checks whether the value of the reader matches the expected value. If the
|
9
26
|
# value looks like an RSpec matcher (it responds to :matches?), runs
|
10
27
|
# value.matches?(); otherwise checks for equality using :==.
|
@@ -22,6 +39,14 @@ module RSpec::SleepingKingStudios::Matchers::Shared
|
|
22
39
|
@value == actual_value
|
23
40
|
end # method matches_reader_value?
|
24
41
|
|
42
|
+
# Checks whether the object responds to the predicate method :#{property}?.
|
43
|
+
#
|
44
|
+
# @return [Boolean] true if the object responds to the method; otherwise
|
45
|
+
# false.
|
46
|
+
def responds_to_predicate?
|
47
|
+
@matches_predicate = @actual.respond_to?(:"#{@expected}?")
|
48
|
+
end # method responds_to_predicate?
|
49
|
+
|
25
50
|
# Checks whether the object responds to the reader method :#{property}.
|
26
51
|
#
|
27
52
|
# @return [Boolean] true if the object responds to the method; otherwise
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# lib/rspec/sleeping_king_studios/support/method_signature.rb
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/support'
|
4
|
+
|
5
|
+
module RSpec::SleepingKingStudios::Support
|
6
|
+
# @api private
|
7
|
+
class MethodSignature
|
8
|
+
def initialize method
|
9
|
+
parameters = method.parameters
|
10
|
+
|
11
|
+
required = parameters.count { |type, _| :req == type }
|
12
|
+
optional = parameters.count { |type, _| :opt == type }
|
13
|
+
variadic = parameters.count { |type, _| :rest == type }
|
14
|
+
|
15
|
+
@min_arguments = required
|
16
|
+
@max_arguments = required + optional
|
17
|
+
@unlimited_arguments = variadic > 0
|
18
|
+
|
19
|
+
required = parameters.select { |type, _| :keyreq == type }.map { |_, keyword| keyword }
|
20
|
+
optional = parameters.select { |type, _| :key == type }.map { |_, keyword| keyword }
|
21
|
+
variadic = parameters.count { |type, _| :keyrest == type }
|
22
|
+
|
23
|
+
@required_keywords = required
|
24
|
+
@optional_keywords = optional
|
25
|
+
@any_keywords = variadic > 0
|
26
|
+
|
27
|
+
@block_argument = parameters.count { |type, _| :block == type } > 0
|
28
|
+
end # method initialize
|
29
|
+
|
30
|
+
attr_reader :min_arguments,
|
31
|
+
:max_arguments,
|
32
|
+
:optional_keywords,
|
33
|
+
:required_keywords
|
34
|
+
|
35
|
+
def any_keywords?
|
36
|
+
!!@any_keywords
|
37
|
+
end # method any_keywords?
|
38
|
+
|
39
|
+
def block_argument?
|
40
|
+
!!@block_argument
|
41
|
+
end # method block_argument?
|
42
|
+
|
43
|
+
def keywords
|
44
|
+
@optional_keywords + @required_keywords
|
45
|
+
end # method keywords
|
46
|
+
|
47
|
+
def unlimited_arguments?
|
48
|
+
!!@unlimited_arguments
|
49
|
+
end # method unlimited_arguments?
|
50
|
+
end # class
|
51
|
+
end # module
|