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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/DEVELOPMENT.md +26 -17
  4. data/LICENSE +1 -1
  5. data/README.md +198 -19
  6. data/lib/rspec/sleeping_king_studios/configuration.rb +63 -2
  7. data/lib/rspec/sleeping_king_studios/examples/property_examples.rb +115 -27
  8. data/lib/rspec/sleeping_king_studios/examples/rspec_matcher_examples.rb +66 -51
  9. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors.rb +8 -269
  10. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb +268 -0
  11. data/lib/rspec/sleeping_king_studios/matchers/base_matcher.rb +5 -15
  12. data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb +3 -63
  13. data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of_matcher.rb +65 -0
  14. data/lib/rspec/sleeping_king_studios/matchers/built_in/include.rb +3 -108
  15. data/lib/rspec/sleeping_king_studios/matchers/built_in/include_matcher.rb +134 -0
  16. data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to.rb +3 -258
  17. data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to_matcher.rb +116 -0
  18. data/lib/rspec/sleeping_king_studios/matchers/core/alias_method.rb +11 -0
  19. data/lib/rspec/sleeping_king_studios/matchers/core/alias_method_matcher.rb +107 -0
  20. data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb +9 -36
  21. data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean_matcher.rb +37 -0
  22. data/lib/rspec/sleeping_king_studios/matchers/core/construct.rb +3 -210
  23. data/lib/rspec/sleeping_king_studios/matchers/core/construct_matcher.rb +113 -0
  24. data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method.rb +11 -0
  25. data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method_matcher.rb +311 -0
  26. data/lib/rspec/sleeping_king_studios/matchers/core/have_constant.rb +16 -0
  27. data/lib/rspec/sleeping_king_studios/matchers/core/have_constant_matcher.rb +225 -0
  28. data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate.rb +11 -0
  29. data/lib/rspec/sleeping_king_studios/matchers/core/have_predicate_matcher.rb +97 -0
  30. data/lib/rspec/sleeping_king_studios/matchers/core/have_property.rb +3 -104
  31. data/lib/rspec/sleeping_king_studios/matchers/core/have_property_matcher.rb +108 -0
  32. data/lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb +3 -74
  33. data/lib/rspec/sleeping_king_studios/matchers/core/have_reader_matcher.rb +96 -0
  34. data/lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb +4 -59
  35. data/lib/rspec/sleeping_king_studios/matchers/core/have_writer_matcher.rb +55 -0
  36. data/lib/rspec/sleeping_king_studios/matchers/description.rb +62 -0
  37. data/lib/rspec/sleeping_king_studios/matchers/macros.rb +32 -0
  38. data/lib/rspec/sleeping_king_studios/matchers/shared/match_parameters.rb +168 -66
  39. data/lib/rspec/sleeping_king_studios/matchers/shared/match_property.rb +25 -0
  40. data/lib/rspec/sleeping_king_studios/matchers.rb +0 -4
  41. data/lib/rspec/sleeping_king_studios/support/method_signature.rb +51 -0
  42. data/lib/rspec/sleeping_king_studios/support/method_signature_expectation.rb +158 -0
  43. data/lib/rspec/sleeping_king_studios/support.rb +9 -0
  44. data/lib/rspec/sleeping_king_studios/version.rb +10 -4
  45. 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/have_mutator.rb
1
+ # lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb
2
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'
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::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
- 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
- # Checks whether the method accepts the specified number or range of
10
- # arguments.
11
+ # Convenience method for more fluent specs. Does nothing and returns self.
11
12
  #
12
- # @param [Method] method the method to check
13
- # @param [Integer, Range] arity the expected number or range of parameters
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
- # @return [Boolean] true if the method accepts the specified number or both
16
- # the specified minimum and maximum number of parameters; otherwise false
17
- def check_method_arity method, arity, expect_unlimited_arguments: false
18
- parameters = method.parameters
19
- required = parameters.count { |type, | :req == type }
20
- optional = parameters.count { |type, | :opt == type }
21
- variadic = parameters.count { |type, | :rest == type }
22
- reasons = {}
23
-
24
- reasons[:expected_unlimited_arguments] = { count: required + optional } if 0 == variadic && expect_unlimited_arguments
25
-
26
- min, max = arity.is_a?(Range) ?
27
- [arity.begin, arity.end] :
28
- [arity, arity]
29
-
30
- if min && min < required
31
- reasons[:not_enough_args] = { arity: min, count: required }
32
- elsif max && 0 == variadic && max > required + optional
33
- reasons[:too_many_args] = { arity: max, count: required + optional }
34
- end # if
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
- reasons.empty? ? nil : reasons
37
- end # method check_method_arity
60
+ method_signature_expectation.keywords = keywords
38
61
 
39
- # Checks whether the method accepts the specified keywords.
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
- # @param [Method] method the method to check
42
- # @param [Array<String, Symbol>] keywords the expected keywords
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 [Boolean] true if the method accepts the specified keywords;
45
- # otherwise false
46
- def check_method_keywords method, keywords, expect_arbitrary_keywords: false
47
- keywords ||= []
48
- parameters = method.parameters
49
- reasons = {}
50
-
51
- # Check for missing required keywords.
52
- if RUBY_VERSION >= "2.1.0"
53
- missing = []
54
- parameters.select { |type, _| :keyreq == type }.each do |_, keyword|
55
- missing << keyword unless keywords.include?(keyword)
56
- end # each
57
-
58
- reasons[:missing_keywords] = missing unless missing.empty?
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
- unless 0 < parameters.count { |type, _| :keyrest == type }
62
- reasons[:expected_arbitrary_keywords] = true if expect_arbitrary_keywords
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
- mismatch = []
65
- keywords.each do |keyword|
66
- mismatch << keyword unless
67
- parameters.include?([:key, keyword]) ||
68
- parameters.include?([:keyreq, keyword])
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
- reasons[:unexpected_keywords] = mismatch unless mismatch.empty?
72
- end # unless
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
- reasons.empty? ? nil : reasons
75
- end # method check_method_keywords
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
- # Checks whether the method expects a block.
78
- #
79
- # @param [Method] method the method to check
80
- #
81
- # @return [Boolean] true if the method expects a block argument; otherwise
82
- # false
83
- def check_method_block method
84
- 0 == method.parameters.count { |type, | :block == type } ? { :expected_block => true } : nil
85
- end # method check_method_block
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
- private :check_method_arity, :check_method_block, :check_method_keywords
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
@@ -6,7 +6,3 @@ module RSpec::SleepingKingStudios
6
6
  # Custom matchers for use with RSpec::Expectations.
7
7
  module Matchers; end
8
8
  end # module
9
-
10
- RSpec.configure do |config|
11
- config.include RSpec::SleepingKingStudios::Matchers
12
- end # configuration
@@ -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