rspec-sleeping_king_studios 2.0.0.beta.0 → 2.0.0.beta.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -13
  3. data/DEVELOPMENT.md +16 -0
  4. data/README.md +203 -100
  5. data/lib/rspec/sleeping_king_studios/all.rb +4 -0
  6. data/lib/rspec/sleeping_king_studios/configuration.rb +42 -0
  7. data/lib/rspec/sleeping_king_studios/examples/all.rb +5 -0
  8. data/lib/rspec/sleeping_king_studios/examples/property_examples.rb +58 -0
  9. data/lib/rspec/sleeping_king_studios/examples/rspec_matcher_examples.rb +105 -0
  10. data/lib/rspec/sleeping_king_studios/examples/shared_example_group.rb +62 -0
  11. data/lib/rspec/sleeping_king_studios/examples.rb +8 -0
  12. data/lib/rspec/sleeping_king_studios/matchers/active_model/all.rb +5 -0
  13. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors/error_expectation.rb +0 -1
  14. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors/message_expectation.rb +1 -2
  15. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors.rb +75 -24
  16. data/lib/rspec/sleeping_king_studios/matchers/active_model.rb +6 -3
  17. data/lib/rspec/sleeping_king_studios/matchers/all.rb +5 -0
  18. data/lib/rspec/sleeping_king_studios/matchers/base_matcher.rb +20 -1
  19. data/lib/rspec/sleeping_king_studios/matchers/built_in/all.rb +5 -0
  20. data/lib/rspec/sleeping_king_studios/matchers/built_in/be_kind_of.rb +16 -10
  21. data/lib/rspec/sleeping_king_studios/matchers/built_in/include.rb +14 -9
  22. data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to.rb +45 -31
  23. data/lib/rspec/sleeping_king_studios/matchers/built_in.rb +5 -3
  24. data/lib/rspec/sleeping_king_studios/matchers/core/all.rb +5 -0
  25. data/lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb +11 -4
  26. data/lib/rspec/sleeping_king_studios/matchers/core/construct.rb +56 -32
  27. data/lib/rspec/sleeping_king_studios/matchers/core/have_property.rb +59 -29
  28. data/lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb +31 -22
  29. data/lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb +21 -55
  30. data/lib/rspec/sleeping_king_studios/matchers/core.rb +5 -3
  31. data/lib/rspec/sleeping_king_studios/matchers/shared/match_parameters.rb +1 -3
  32. data/lib/rspec/sleeping_king_studios/matchers/shared/match_property.rb +52 -0
  33. data/lib/rspec/sleeping_king_studios/matchers.rb +10 -3
  34. data/lib/rspec/sleeping_king_studios/version.rb +22 -1
  35. data/lib/rspec/sleeping_king_studios.rb +7 -1
  36. metadata +38 -16
  37. data/lib/rspec/sleeping_king_studios/matchers/active_model/have_errors/require.rb +0 -7
  38. data/lib/rspec/sleeping_king_studios/matchers/active_model/require.rb +0 -8
  39. data/lib/rspec/sleeping_king_studios/matchers/built_in/require.rb +0 -7
  40. data/lib/rspec/sleeping_king_studios/matchers/core/require.rb +0 -7
  41. data/lib/rspec/sleeping_king_studios/matchers/meta/fail_with_actual.rb +0 -107
  42. data/lib/rspec/sleeping_king_studios/matchers/meta/pass_with_actual.rb +0 -95
  43. data/lib/rspec/sleeping_king_studios/matchers/meta/require.rb +0 -7
  44. data/lib/rspec/sleeping_king_studios/matchers/meta.rb +0 -5
  45. data/lib/rspec/sleeping_king_studios/matchers/require.rb +0 -12
  46. data/lib/rspec/sleeping_king_studios/matchers/shared/require.rb +0 -7
  47. data/lib/rspec/sleeping_king_studios/require.rb +0 -7
@@ -1,46 +1,65 @@
1
1
  # lib/rspec/sleeping_king_studios/matchers/built_in/respond_to.rb
2
2
 
3
3
  require 'rspec/sleeping_king_studios/matchers/base_matcher'
4
- require 'rspec/sleeping_king_studios/matchers/built_in/require'
4
+ require 'rspec/sleeping_king_studios/matchers/built_in'
5
5
  require 'rspec/sleeping_king_studios/matchers/shared/match_parameters'
6
+ require 'sleeping_king_studios/tools/enumerable_tools'
7
+ require 'sleeping_king_studios/tools/string_tools'
6
8
 
7
9
  module RSpec::SleepingKingStudios::Matchers::BuiltIn
8
10
  class RespondToMatcher < RSpec::Matchers::BuiltIn::RespondTo
9
11
  include RSpec::SleepingKingStudios::Matchers::Shared::MatchParameters
12
+ include SleepingKingStudios::Tools::EnumerableTools
13
+ include SleepingKingStudios::Tools::StringTools
14
+
15
+ def initialize *expected
16
+ @include_all = [true, false].include?(expected.last) ? expected.pop : false
17
+
18
+ super(*expected)
19
+ end # constructor
20
+
21
+ # Generates a description of the matcher expectation.
22
+ #
23
+ # @return [String] The matcher description.
24
+ def description
25
+ expected_message = format_expected_arguments
26
+ "respond to #{pp_names}#{expected_message.empty? ? '' : " with #{expected_message}"}"
27
+ end # method description
10
28
 
11
29
  # @overload with count
12
30
  # Adds a parameter count expectation.
13
- #
31
+ #
14
32
  # @param [Integer, Range, nil] count (optional) The number of expected
15
33
  # parameters.
16
- #
34
+ #
17
35
  # @return [RespondToMatcher] self
18
36
  # @overload with *keywords
19
37
  # Adds one or more keyword expectations (Ruby 2.0 only).
20
- #
38
+ #
21
39
  # @param [Array<String, Symbol>] keywords List of keyword arguments
22
40
  # accepted by the method.
23
- #
41
+ #
24
42
  # @return [RespondToMatcher] self
25
43
  # @overload with count, *keywords
26
44
  # Adds a parameter count expectation and one or more keyword
27
45
  # expectations (Ruby 2.0 only).
28
- #
46
+ #
29
47
  # @param [Integer, Range, nil] count (optional) The number of expected
30
48
  # parameters.
31
49
  # @param [Array<String, Symbol>] keywords List of keyword arguments
32
50
  # accepted by the method.
33
- #
51
+ #
34
52
  # @return [RespondToMatcher] self
35
53
  def with *keywords
36
54
  @expected_arity = keywords.shift if Integer === keywords.first || Range === keywords.first
37
55
  @expected_keywords = keywords
56
+
38
57
  self
39
58
  end # method with
40
59
 
41
60
  # Adds a block expectation. The actual object will only match a block
42
61
  # expectation if it expects a parameter of the form &block.
43
- #
62
+ #
44
63
  # @return [RespondToMatcher] self
45
64
  def with_a_block
46
65
  @expected_block = true
@@ -50,30 +69,32 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
50
69
  # @see BaseMatcher#failure_message
51
70
  def failure_message
52
71
  @failing_method_names ||= []
53
- methods, messages = @names - @failing_method_names, []
72
+ methods, messages = @failing_method_names, []
54
73
 
55
74
  methods.map do |method|
56
75
  message = "expected #{@actual.inspect} to respond to #{method.inspect}"
57
- if @actual.respond_to?(method)
76
+ if @actual.respond_to?(method, @include_all)
58
77
  message << " with arguments:\n#{format_errors_for_method method}"
59
78
  end # if-else
60
79
  messages << message
61
80
  end # method
81
+
62
82
  messages.join "\n"
63
83
  end # method failure_message
64
84
 
65
85
  # @see BaseMatcher#failure_message_when_negated
66
86
  def failure_message_when_negated
67
87
  @failing_method_names ||= []
68
- methods, messages = @names - @failing_method_names, []
88
+ methods, messages = @failing_method_names, []
69
89
 
70
90
  methods.map do |method|
71
- message = "expected #{@actual.inspect} not to respond to #{method.inspect}"
91
+ message = "expected #{@actual.inspect} not to respond to #{method.inspect}"
72
92
  unless (formatted = format_expected_arguments).empty?
73
93
  message << " with #{formatted}"
74
94
  end # unless
75
95
  messages << message
76
96
  end # method
97
+
77
98
  messages.join "\n"
78
99
  end # method failure_message_when_negated
79
100
 
@@ -83,7 +104,7 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
83
104
  @actual = actual
84
105
  @failing_method_reasons = {}
85
106
  @failing_method_names = @names.__send__(filter_method) do |name|
86
- @actual.respond_to?(name) &&
107
+ @actual.respond_to?(name, @include_all) &&
87
108
  matches_arity?(actual, name) &&
88
109
  matches_keywords?(actual, name) &&
89
110
  matches_block?(actual, name)
@@ -121,7 +142,7 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
121
142
  raise error
122
143
  end # if-else
123
144
  end # method matches_keywords?
124
-
145
+
125
146
  def matches_block? actual, name
126
147
  return true unless @expected_block
127
148
 
@@ -135,32 +156,25 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
135
156
 
136
157
  def format_expected_arguments
137
158
  messages = []
138
-
159
+
139
160
  if !@expected_arity.nil?
140
- messages << "#{@expected_arity.inspect} argument#{1 == @expected_arity ? "" : "s"}"
161
+ messages << "#{@expected_arity.inspect} #{pluralize @expected_arity, 'argument', 'arguments'}"
141
162
  end # if
142
163
 
143
164
  if !(@expected_keywords.nil? || @expected_keywords.empty?)
144
- messages << "keywords #{@expected_keywords.map(&:inspect).join(", ")}"
165
+ messages << "#{pluralize @expected_keywords.count, 'keyword', 'keywords'} #{humanize_list @expected_keywords.map(&:inspect)}"
145
166
  end # if
146
167
 
147
168
  if @expected_block
148
169
  messages << "a block"
149
170
  end # if
150
171
 
151
- case messages.count
152
- when 0..1
153
- messages.join(", ")
154
- when 2
155
- "#{messages[0]} and #{messages[1]}"
156
- else
157
- "#{messages[1..-1].join(", ")}, and #{messages[0]}"
158
- end # case
172
+ humanize_list messages
159
173
  end # method format_expected_arguments
160
174
 
161
175
  def format_errors_for_method method
162
176
  reasons, messages = @failing_method_reasons[method], []
163
-
177
+
164
178
  if hsh = reasons.fetch(:not_enough_args, false)
165
179
  messages << " expected at least #{hsh[:count]} arguments, but received #{hsh[:arity]}"
166
180
  elsif hsh = reasons.fetch(:too_many_args, false)
@@ -168,11 +182,11 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
168
182
  end # if-elsif
169
183
 
170
184
  if ary = reasons.fetch(:missing_keywords, false)
171
- messages << " missing keywords #{ary.map(&:inspect).join(", ")}"
185
+ messages << " missing #{pluralize ary.count, 'keyword', 'keywords'} #{humanize_list ary.map(&:inspect)}"
172
186
  end # if
173
187
 
174
188
  if ary = reasons.fetch(:unexpected_keywords, false)
175
- messages << " unexpected keywords #{ary.map(&:inspect).join(", ")}"
189
+ messages << " unexpected #{pluralize ary.count, 'keyword', 'keywords'} #{humanize_list ary.map(&:inspect)}"
176
190
  end # if
177
191
 
178
192
  if reasons.fetch(:expected_block, false)
@@ -180,13 +194,13 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn
180
194
  end # if
181
195
 
182
196
  messages.join "\n"
183
- end # method format_errors_for_method
197
+ end # method format_errors_for_method
184
198
  end # class
185
199
  end # module
186
200
 
187
201
  module RSpec::SleepingKingStudios::Matchers
188
202
  # @see RSpec::SleepingKingStudios::Matchers::BuiltIn::RespondToMatcher#matches?
189
- def respond_to expected
190
- RSpec::SleepingKingStudios::Matchers::BuiltIn::RespondToMatcher.new expected
203
+ def respond_to *expected
204
+ RSpec::SleepingKingStudios::Matchers::BuiltIn::RespondToMatcher.new *expected
191
205
  end # method respond_to
192
206
  end # module
@@ -1,5 +1,7 @@
1
1
  # lib/rspec/sleeping_king_studios/matchers/built_in.rb
2
2
 
3
- Dir[File.join File.dirname(__FILE__), 'built_in', '*.rb'].each do |file|
4
- require file
5
- end # end each
3
+ require 'rspec/sleeping_king_studios/matchers'
4
+
5
+ module RSpec::SleepingKingStudios::Matchers
6
+ module BuiltIn; end
7
+ end # module
@@ -0,0 +1,5 @@
1
+ # lib/rspec/sleeping_king_studios/matchers/core/all.rb
2
+
3
+ Dir[File.join File.dirname(__FILE__), '*.rb'].each do |file|
4
+ require file
5
+ end # end each
@@ -1,17 +1,24 @@
1
1
  # lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb
2
2
 
3
3
  require 'rspec/sleeping_king_studios/matchers/base_matcher'
4
- require 'rspec/sleeping_king_studios/matchers/core/require'
4
+ require 'rspec/sleeping_king_studios/matchers/core'
5
5
 
6
6
  module RSpec::SleepingKingStudios::Matchers::Core
7
7
  # Matcher for testing whether an object is true or false.
8
- #
8
+ #
9
9
  # @since 1.0.0
10
10
  class BeBooleanMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
11
+ # Generates a description of the matcher expectation.
12
+ #
13
+ # @return [String] The matcher description.
14
+ def description
15
+ 'be true or false'
16
+ end # method description
17
+
11
18
  # Checks if the object is true or false.
12
- #
19
+ #
13
20
  # @param [Object] actual the object to check
14
- #
21
+ #
15
22
  # @return [Boolean] true if the object is true or false, otherwise false
16
23
  def matches? actual
17
24
  super
@@ -1,24 +1,35 @@
1
1
  # spec/rspec/sleeping_king_studios/matchers/core/construct.rb
2
2
 
3
3
  require 'rspec/sleeping_king_studios/matchers/base_matcher'
4
- require 'rspec/sleeping_king_studios/matchers/core/require'
5
-
4
+ require 'rspec/sleeping_king_studios/matchers/core'
6
5
  require 'rspec/sleeping_king_studios/matchers/shared/match_parameters'
6
+ require 'sleeping_king_studios/tools/enumerable_tools'
7
+ require 'sleeping_king_studios/tools/string_tools'
7
8
 
8
9
  module RSpec::SleepingKingStudios::Matchers::Core
9
10
  # Matcher for checking whether an object can be constructed via #new and
10
11
  # #initialize, and the parameters accepted by #initialize.
11
- #
12
+ #
12
13
  # @since 1.0.0
13
14
  class ConstructMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
14
15
  include RSpec::SleepingKingStudios::Matchers::Shared::MatchParameters
16
+ include SleepingKingStudios::Tools::EnumerableTools
17
+ include SleepingKingStudios::Tools::StringTools
18
+
19
+ # Generates a description of the matcher expectation.
20
+ #
21
+ # @return [String] The matcher description.
22
+ def description
23
+ expected_message = format_expected_arguments
24
+ "construct#{expected_message.empty? ? '' : " with #{expected_message}"}"
25
+ end # method description
15
26
 
16
27
  # Checks if the object responds to :new. If so, allocates an instance and
17
28
  # checks the parameters expected by #initialize against the expected
18
29
  # parameters, if any.
19
- #
30
+ #
20
31
  # @param [Object] actual the object to check
21
- #
32
+ #
22
33
  # @return [Boolean] true if the object responds to :new and accepts the
23
34
  # specified parameters for #initialize; otherwise false
24
35
  def matches? actual
@@ -28,16 +39,33 @@ module RSpec::SleepingKingStudios::Matchers::Core
28
39
  matches_arity?(actual) &&
29
40
  matches_keywords?(actual)
30
41
  end # method matches?
31
-
32
- # Adds a parameter count expectation and/or one or more keyword
33
- # expectations (Ruby 2.0 only).
34
- #
35
- # @param [Integer, Range, nil] count the number of expected parameters; can
36
- # be an integer, a range, or nil (parameter count is ignored)
37
- # @param [Array<String, Symbol>] keywords list of keyword arguments
38
- # accepted by the method
39
- #
40
- # @return [ConstructMatcher] self
42
+
43
+ # @overload with(count)
44
+ # Adds a parameter count expectation and removes any keyword expectations
45
+ # (Ruby 2.0+ only).
46
+ #
47
+ # @param [Integer, Range] count the number of expected parameters
48
+ #
49
+ # @return [ConstructMatcher] self
50
+ #
51
+ # @overload with(count, *keywords)
52
+ # Adds a parameter count expectation and one or more keyword expectations
53
+ # (Ruby 2.0 only).
54
+ #
55
+ # @param [Integer, Range] count the number of expected parameters
56
+ # @param [Array<String, Symbol>] keywords list of keyword arguments
57
+ # accepted by the method
58
+ #
59
+ # @return [ConstructMatcher] self
60
+ #
61
+ # @overload with(*keywords)
62
+ # Removes a parameter count expectation (if any) and adds one or more
63
+ # keyword expectations (Ruby 2.0 only).
64
+ #
65
+ # @param [Array<String, Symbol>] keywords list of keyword arguments
66
+ # accepted by the method
67
+ #
68
+ # @return [ConstructMatcher] self
41
69
  def with *keywords
42
70
  @expected_arity = keywords.shift if Integer === keywords.first || Range === keywords.first
43
71
  @expected_keywords = keywords
@@ -45,11 +73,14 @@ module RSpec::SleepingKingStudios::Matchers::Core
45
73
  end # method with
46
74
 
47
75
  # Convenience method for more fluent specs. Does nothing and returns self.
48
- #
76
+ #
49
77
  # @return [ConstructMatcher] self
50
- def arguments
78
+ #
79
+ # @since 2.0.0
80
+ def argument
51
81
  self
52
- end # method arguments
82
+ end # method argument
83
+ alias_method :arguments, :argument
53
84
 
54
85
  # @see BaseMatcher#failure_message
55
86
  def failure_message
@@ -62,7 +93,7 @@ module RSpec::SleepingKingStudios::Matchers::Core
62
93
  def failure_message_when_negated
63
94
  message = "expected #{@actual.inspect} not to construct"
64
95
  unless (formatted = format_expected_arguments).empty?
65
- message << " with #{formatted}"
96
+ message << " with #{formatted}"
66
97
  end # unless
67
98
  message
68
99
  end # method failure_message_when_negated
@@ -71,7 +102,7 @@ module RSpec::SleepingKingStudios::Matchers::Core
71
102
 
72
103
  def matches_arity? actual
73
104
  return true unless @expected_arity
74
-
105
+
75
106
  if result = check_method_arity(actual.allocate.method(:initialize), @expected_arity)
76
107
  @failing_method_reasons.update result
77
108
  return false
@@ -100,22 +131,15 @@ module RSpec::SleepingKingStudios::Matchers::Core
100
131
  end # if
101
132
 
102
133
  if !(@expected_keywords.nil? || @expected_keywords.empty?)
103
- messages << "keywords #{@expected_keywords.map(&:inspect).join(", ")}"
134
+ messages << "#{pluralize @expected_keywords.count, 'keyword', 'keywords'} #{humanize_list @expected_keywords.map(&:inspect)}"
104
135
  end # if
105
136
 
106
- case messages.count
107
- when 0..1
108
- messages.join(", ")
109
- when 2
110
- "#{messages[0]} and #{messages[1]}"
111
- else
112
- "#{messages[1..-1].join(", ")}, and #{messages[0]}"
113
- end # case
137
+ humanize_list messages
114
138
  end # method format_expected_arguments
115
139
 
116
140
  def format_errors
117
141
  reasons, messages = @failing_method_reasons, []
118
-
142
+
119
143
  if hsh = reasons.fetch(:not_enough_args, false)
120
144
  messages << " expected at least #{hsh[:count]} arguments, but received #{hsh[:arity]}"
121
145
  elsif hsh = reasons.fetch(:too_many_args, false)
@@ -123,11 +147,11 @@ module RSpec::SleepingKingStudios::Matchers::Core
123
147
  end # if-elsif
124
148
 
125
149
  if ary = reasons.fetch(:missing_keywords, false)
126
- messages << " missing keywords #{ary.map(&:inspect).join(", ")}"
150
+ messages << " missing #{pluralize ary.count, 'keyword', 'keywords'} #{humanize_list ary.map(&:inspect)}"
127
151
  end # if
128
152
 
129
153
  if ary = reasons.fetch(:unexpected_keywords, false)
130
- messages << " unexpected keywords #{ary.map(&:inspect).join(", ")}"
154
+ messages << " unexpected #{pluralize ary.count, 'keyword', 'keywords'} #{humanize_list ary.map(&:inspect)}"
131
155
  end # if
132
156
 
133
157
  messages.join "\n"
@@ -1,77 +1,107 @@
1
1
  # lib/rspec/sleeping_king_studios/matchers/core/have_property.rb
2
2
 
3
3
  require 'rspec/sleeping_king_studios/matchers/base_matcher'
4
- require 'rspec/sleeping_king_studios/matchers/core/require'
4
+ require 'rspec/sleeping_king_studios/matchers/core'
5
+ require 'rspec/sleeping_king_studios/matchers/shared/match_property'
5
6
 
6
7
  module RSpec::SleepingKingStudios::Matchers::Core
7
8
  # Matcher for testing whether an object has a specific property, e.g.
8
9
  # responds to :property and :property= and :property= updates the value of
9
10
  # :property.
10
- #
11
+ #
11
12
  # @since 1.0.0
12
13
  class HavePropertyMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
14
+ include RSpec::SleepingKingStudios::Matchers::Shared::MatchProperty
15
+
16
+ # Generates a description of the matcher expectation.
17
+ #
18
+ # @return [String] The matcher description.
19
+ def description
20
+ value_message = value_to_string
21
+ "have property :#{@expected}#{@value_set ? " with value #{value_message}" : ''}"
22
+ end # method description
23
+
13
24
  # @param [String, Symbol] expected the property to check for on the actual
14
25
  # object
15
26
  def initialize expected
16
27
  @expected = expected.intern
17
28
  end # method initialize
18
29
 
30
+ def does_not_match? actual
31
+ super
32
+
33
+ matches_property?(:none?)
34
+ end # method does_not_match?
35
+
19
36
  # Checks if the object responds to :expected and :expected=. Additionally,
20
- # if a value expectation is set, assigns the value via :expected= and
21
- # compares the subsequent value of :expected to the specified value.
22
- #
37
+ # if a value expectation is set, compares the result of calling :expected
38
+ # to the value.
39
+ #
23
40
  # @param [Object] actual the object to check
24
- #
41
+ #
25
42
  # @return [Boolean] true if the object responds to :expected and
26
43
  # :expected= and matches the value expectation (if any); otherwise false
27
44
  def matches? actual
28
45
  super
29
46
 
30
- @match_reader = @actual.respond_to? @expected
31
- @match_writer = @actual.respond_to? :"#{@expected}="
32
-
33
- return false unless @match_reader && @match_writer
34
-
35
- if @value_set
36
- @actual.send :"#{@expected}=", @value
37
- return false unless @actual.send(@expected) == @value
38
- end # if
39
-
40
- true
47
+ matches_property?(:all?)
41
48
  end # method matches?
42
49
 
43
- # Sets a value expectation. The matcher will set the object's value to the
44
- # specified value using :property=, then compare the value from :property
45
- # with the specified value.
46
- #
50
+ # Sets a value expectation. The matcher will compare the value to the
51
+ # result of calling :property.
52
+ #
47
53
  # @param [Object] value the value to set and then compare
48
- #
54
+ #
49
55
  # @return [HavePropertyMatcher] self
50
56
  def with value
51
57
  @value = value
52
58
  @value_set = true
53
59
  self
54
60
  end # method with
61
+ alias_method :with_value, :with
55
62
 
56
63
  # @see BaseMatcher#failure_message
57
64
  def failure_message
58
65
  methods = []
59
- methods << ":#{@expected}" unless @match_reader
60
- methods << ":#{@expected}=" unless @match_writer
66
+ methods << ":#{@expected}" unless @matches_reader
67
+ methods << ":#{@expected}=" unless @matches_writer
68
+
69
+ message = "expected #{@actual.inspect} to respond to :#{@expected} and :#{@expected}="
70
+ message << " and return #{value_to_string}" if @value_set
61
71
 
62
- return "expected #{@actual.inspect} to respond to #{methods.join " and "}" unless methods.empty?
72
+ errors = []
73
+ errors << "did not respond to #{methods.join " or "}" unless methods.empty?
74
+ errors << "returned #{@actual.send(@expected).inspect}" unless @matches_reader_value || !@value_set
63
75
 
64
- "unexpected value for #{@actual.inspect}\##{@expected}" +
65
- "\n expected: #{@value.inspect}" +
66
- "\n got: #{@actual.send(@expected).inspect}"
76
+ message << ", but #{errors.join(" and ")}"
77
+ message
67
78
  end # failure_message
68
79
 
69
80
  # @see BaseMatcher#failure_message_when_negated
70
81
  def failure_message_when_negated
82
+ methods = []
83
+ methods << ":#{@expected}" if @matches_reader
84
+ methods << ":#{@expected}=" if @matches_writer
85
+
71
86
  message = "expected #{@actual.inspect} not to respond to :#{@expected} or :#{@expected}="
72
- message << " with value #{@value.inspect}" if @value_set
87
+ message << " and return #{value_to_string}" if @value_set
88
+
89
+ errors = []
90
+ errors << "responded to #{methods.join " and "}" unless methods.empty?
91
+ errors << "returned #{@actual.send(@expected).inspect}" if @matches_reader_value
92
+
93
+ message << ", but #{errors.join(" and ")}"
73
94
  message
74
95
  end # failure_message_when_negated
96
+
97
+ private
98
+
99
+ def matches_property? filter
100
+ [ responds_to_reader?,
101
+ responds_to_writer?,
102
+ matches_reader_value?
103
+ ].send(filter) { |bool| bool }
104
+ end # method matches_property?
75
105
  end # class
76
106
  end # module
77
107
 
@@ -1,14 +1,25 @@
1
1
  # lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb
2
2
 
3
3
  require 'rspec/sleeping_king_studios/matchers/base_matcher'
4
- require 'rspec/sleeping_king_studios/matchers/core/require'
4
+ require 'rspec/sleeping_king_studios/matchers/core'
5
+ require 'rspec/sleeping_king_studios/matchers/shared/match_property'
5
6
 
6
7
  module RSpec::SleepingKingStudios::Matchers::Core
7
8
  # Matcher for testing whether an object has a specific property reader, e.g.
8
9
  # responds to :property.
9
- #
10
+ #
10
11
  # @since 1.0.0
11
12
  class HaveReaderMatcher < 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
+ value_message = value_to_string
20
+ "have reader :#{@expected}#{@value_set ? " with value #{value_message}" : ''}"
21
+ end # method description
22
+
12
23
  # @param [String, Symbol] expected the property to check for on the actual
13
24
  # object
14
25
  def initialize expected
@@ -18,50 +29,48 @@ module RSpec::SleepingKingStudios::Matchers::Core
18
29
  # Checks if the object responds to :expected. Additionally, if a value
19
30
  # expectation is set, compares the value of :expected to the specified
20
31
  # value.
21
- #
32
+ #
22
33
  # @param [Object] actual the object to check
23
- #
34
+ #
24
35
  # @return [Boolean] true if the object responds to :expected and matches
25
36
  # the value expectation (if any); otherwise false
26
37
  def matches? actual
27
38
  super
28
39
 
29
- return false unless @match_reader = @actual.respond_to?(@expected)
30
-
31
- if @value_set
32
- return false unless @match_value = @actual.send(@expected) == @value
33
- end # if
34
-
35
- true
40
+ responds_to_reader? && matches_reader_value?
36
41
  end # method matches?
37
42
 
38
43
  # Sets a value expectation. The matcher will compare the value from
39
44
  # :property with the specified value.
40
- #
45
+ #
41
46
  # @param [Object] value the value to compare
42
- #
47
+ #
43
48
  # @return [HaveReaderMatcher] self
44
49
  def with value
45
50
  @value = value
46
51
  @value_set = true
47
52
  self
48
53
  end # method with
54
+ alias_method :with_value, :with
49
55
 
50
56
  # @see BaseMatcher#failure_message
51
57
  def failure_message
52
- unless @match_reader
53
- return "expected #{@actual} to respond to #{@expected.inspect}"
54
- end # unless
55
-
56
- "unexpected value for #{@actual}\##{@expected}\n" +
57
- " expected: #{@value.inspect}\n" +
58
- " got: #{@actual.send(@expected).inspect}"
58
+ message = "expected #{@actual} to respond to :#{@expected}"
59
+ message << " and return #{value_to_string}" if @value_set
60
+
61
+ if !@matches_reader
62
+ message << ", but did not respond to :#{@expected}"
63
+ elsif !@matches_reader_value
64
+ message << ", but returned #{@actual.send(@expected).inspect}"
65
+ end # if
66
+
67
+ message
59
68
  end # method failure_message
60
69
 
61
70
  # @see BaseMatcher#failure_message_when_negated
62
71
  def failure_message_when_negated
63
- message = "expected #{@actual} not to respond to #{@expected.inspect}"
64
- message << " with value #{@value.inspect}" if @value_set
72
+ message = "expected #{@actual} not to respond to :#{@expected}"
73
+ message << " and return #{value_to_string}" if @value_set
65
74
  message
66
75
  end # method failure_message
67
76
  end # class