rspec-sleeping_king_studios 2.1.1 → 2.2.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|