riot 0.12.1 → 0.12.2

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 (62) hide show
  1. data/.gitignore +10 -0
  2. data/.yardopts +6 -0
  3. data/CHANGELOG +58 -46
  4. data/Gemfile +4 -0
  5. data/README.markdown +322 -85
  6. data/Rakefile +3 -38
  7. data/lib/riot.rb +74 -11
  8. data/lib/riot/assertion.rb +32 -1
  9. data/lib/riot/assertion_macro.rb +57 -10
  10. data/lib/riot/assertion_macros/any.rb +4 -2
  11. data/lib/riot/assertion_macros/assigns.rb +18 -4
  12. data/lib/riot/assertion_macros/empty.rb +2 -0
  13. data/lib/riot/assertion_macros/equals.rb +4 -0
  14. data/lib/riot/assertion_macros/equivalent_to.rb +5 -1
  15. data/lib/riot/assertion_macros/exists.rb +4 -2
  16. data/lib/riot/assertion_macros/includes.rb +5 -1
  17. data/lib/riot/assertion_macros/kind_of.rb +5 -1
  18. data/lib/riot/assertion_macros/matches.rb +5 -1
  19. data/lib/riot/assertion_macros/nil.rb +3 -1
  20. data/lib/riot/assertion_macros/not_borat.rb +6 -0
  21. data/lib/riot/assertion_macros/raises.rb +13 -7
  22. data/lib/riot/assertion_macros/respond_to.rb +5 -1
  23. data/lib/riot/assertion_macros/same_elements.rb +6 -2
  24. data/lib/riot/assertion_macros/size.rb +5 -1
  25. data/lib/riot/context.rb +58 -10
  26. data/lib/riot/context_helpers.rb +20 -4
  27. data/lib/riot/context_options.rb +14 -4
  28. data/lib/riot/message.rb +87 -6
  29. data/lib/riot/middleware.rb +69 -4
  30. data/lib/riot/reporter.rb +71 -110
  31. data/lib/riot/reporter/dot_matrix.rb +49 -0
  32. data/lib/riot/reporter/io.rb +85 -0
  33. data/lib/riot/reporter/pretty_dot_matrix.rb +38 -0
  34. data/lib/riot/reporter/silent.rb +18 -0
  35. data/lib/riot/reporter/story.rb +52 -0
  36. data/lib/riot/rr.rb +28 -4
  37. data/lib/riot/runnable.rb +53 -0
  38. data/lib/riot/situation.rb +45 -0
  39. data/lib/riot/version.rb +4 -0
  40. data/riot.gemspec +14 -155
  41. data/test/core/assertion_macros/any_test.rb +10 -10
  42. data/test/core/assertion_macros/assigns_test.rb +7 -7
  43. data/test/core/assertion_macros/equivalent_to_test.rb +3 -3
  44. data/test/core/assertion_macros/exists_test.rb +4 -4
  45. data/test/core/assertion_macros/includes_test.rb +2 -2
  46. data/test/core/assertion_macros/kind_of_test.rb +3 -3
  47. data/test/core/assertion_macros/matches_test.rb +2 -2
  48. data/test/core/assertion_macros/nil_test.rb +2 -2
  49. data/test/core/assertion_macros/raises_test.rb +10 -10
  50. data/test/core/assertion_macros/respond_to_test.rb +2 -2
  51. data/test/core/assertion_macros/same_elements_test.rb +4 -4
  52. data/test/core/assertion_macros/size_test.rb +6 -6
  53. data/test/core/context/asserts_with_arguments_test.rb +12 -0
  54. data/test/core/context/using_describe_in_a_test.rb +1 -1
  55. data/test/core/report_test.rb +9 -5
  56. data/test/core/runnable/message_test.rb +10 -6
  57. data/test/teststrap.rb +0 -6
  58. metadata +20 -33
  59. data/TODO.markdown +0 -14
  60. data/VERSION +0 -1
  61. data/test.watchr +0 -70
  62. data/test/benchmark/colorize.rb +0 -39
@@ -16,12 +16,14 @@ module Riot
16
16
  class ExistsMacro < AssertionMacro
17
17
  register :exists
18
18
 
19
+ # (see Riot::AssertionMacro#evaluate)
19
20
  def evaluate(actual)
20
- actual.nil? ? fail("expected a non-nil value") : pass("is not nil")
21
+ actual.nil? ? fail("expected a non-nil value") : pass("does exist")
21
22
  end
22
23
 
24
+ # (see Riot::AssertionMacro#devaluate)
23
25
  def devaluate(actual)
24
- actual.nil? ? pass("is nil") : fail("expected a nil value")
26
+ actual.nil? ? pass("does exist") : fail("expected a nil value")
25
27
  end
26
28
  end
27
29
  end
@@ -13,6 +13,8 @@ module Riot
13
13
  class IncludesMacro < AssertionMacro
14
14
  register :includes
15
15
 
16
+ # (see Riot::AssertionMacro#evaluate)
17
+ # @param [Object] expected the object that is expected to be included
16
18
  def evaluate(actual, expected)
17
19
  if actual.include?(expected)
18
20
  pass new_message.includes(expected)
@@ -21,11 +23,13 @@ module Riot
21
23
  end
22
24
  end
23
25
 
26
+ # (see Riot::AssertionMacro#devaluate)
27
+ # @param [Object] expected the object that is not expected to be included
24
28
  def devaluate(actual, expected)
25
29
  if actual.include?(expected)
26
30
  fail expected_message(actual).to_not_include(expected)
27
31
  else
28
- pass new_message.does_not_include(expected)
32
+ pass new_message.includes(expected)
29
33
  end
30
34
  end
31
35
 
@@ -10,6 +10,8 @@ module Riot
10
10
  class KindOfMacro < AssertionMacro
11
11
  register :kind_of
12
12
 
13
+ # (see Riot::AssertionMacro#evaluate)
14
+ # @param [Class] expected the expected class of actual
13
15
  def evaluate(actual, expected)
14
16
  if actual.kind_of?(expected)
15
17
  pass new_message.is_a_kind_of(expected)
@@ -18,11 +20,13 @@ module Riot
18
20
  end
19
21
  end
20
22
 
23
+ # (see Riot::AssertionMacro#devaluate)
24
+ # @param [Class] expected the unexpected class of actual
21
25
  def devaluate(actual, expected)
22
26
  if actual.kind_of?(expected)
23
27
  fail expected_message.not_kind_of(expected).not(actual.class)
24
28
  else
25
- pass new_message.is_not_a_kind_of(expected)
29
+ pass new_message.is_a_kind_of(expected)
26
30
  end
27
31
  end
28
32
  end
@@ -10,6 +10,8 @@ module Riot
10
10
  class MatchesMacro < AssertionMacro
11
11
  register :matches
12
12
 
13
+ # (see Riot::AssertionMacro#evaluate)
14
+ # @param [Regex, String] expected the string or regex to be used in comparison
13
15
  def evaluate(actual, expected)
14
16
  expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
15
17
  if actual.to_s =~ expected
@@ -19,12 +21,14 @@ module Riot
19
21
  end
20
22
  end
21
23
 
24
+ # (see Riot::AssertionMacro#devaluate)
25
+ # @param [Regex, String] expected the string or regex to be used in comparison
22
26
  def devaluate(actual, expected)
23
27
  expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
24
28
  if actual.to_s =~ expected
25
29
  fail(expected_message(expected).not_to_match(actual))
26
30
  else
27
- pass(new_message.does_not_match(expected))
31
+ pass(new_message.matches(expected))
28
32
  end
29
33
  end
30
34
  end
@@ -11,12 +11,14 @@ module Riot
11
11
  class NilMacro < AssertionMacro
12
12
  register :nil
13
13
 
14
+ # (see Riot::AssertionMacro#evaluate)
14
15
  def evaluate(actual)
15
16
  actual.nil? ? pass("is nil") : fail(expected_message.nil.not(actual))
16
17
  end
17
18
 
19
+ # (see Riot::AssertionMacro#devaluate)
18
20
  def devaluate(actual)
19
- actual.nil? ? fail(expected_message.is_nil.not('non-nil')) : pass("is not nil")
21
+ actual.nil? ? fail(expected_message.is_nil.not('non-nil')) : pass("is nil")
20
22
  end
21
23
  end
22
24
  end
@@ -12,14 +12,20 @@ module Riot
12
12
  # It would be kind of like a double negative:
13
13
  #
14
14
  # denies("you are funny") { true }.not!
15
+ #
16
+ # @deprecated Please use the denies assertion instead
15
17
  class NotMacro < AssertionMacro
16
18
  register :not!
17
19
 
20
+ # (see Riot::AssertionMacro#evaluate)
18
21
  def evaluate(actual)
22
+ warn "not! is deprecated; please use the denies assertion instead"
19
23
  actual ? fail("expected to exist ... not!") : pass("does exist ... not!")
20
24
  end
21
25
 
26
+ # (see Riot::AssertionMacro#devaluate)
22
27
  def devaluate(actual)
28
+ warn "not! is deprecated; please use the denies assertion instead"
23
29
  actual ? pass("does not exist ... not!") : fail("expected to not exist ... not!")
24
30
  end
25
31
  end
@@ -18,12 +18,15 @@ module Riot
18
18
  register :raises
19
19
  expects_exception!
20
20
 
21
+ # (see Riot::AssertionMacro#evaluate)
22
+ # @param [Class] expected_class the expected Exception class
23
+ # @param [String, nil] expected_message an optional exception message or message partial
21
24
  def evaluate(actual_exception, expected_class, expected_message=nil)
22
25
  actual_message = actual_exception && actual_exception.message
23
26
  if actual_exception.nil?
24
- fail should_have_message.raised(expected_class).but.raised_nothing
27
+ fail new_message.expected_to_raise(expected_class).but.raised_nothing
25
28
  elsif expected_class != actual_exception.class
26
- fail should_have_message.raised(expected_class).not(actual_exception.class)
29
+ fail new_message.expected_to_raise(expected_class).not(actual_exception.class)
27
30
  elsif expected_message && !(actual_message.to_s =~ %r[#{expected_message}])
28
31
  fail expected_message(expected_message).for_message.not(actual_message)
29
32
  else
@@ -32,23 +35,26 @@ module Riot
32
35
  end
33
36
  end # evaluate
34
37
 
38
+ # (see Riot::AssertionMacro#devaluate)
39
+ # @param [Class] expected_class the unexpected Exception class
40
+ # @param [String, nil] expected_message an optional exception message or message partial
35
41
  def devaluate(actual_exception, expected_class, expected_message=nil)
36
42
  actual_message = actual_exception && actual_exception.message
37
43
  if actual_exception.nil?
38
- pass new_message.raised_nothing
44
+ pass new_message.raises(expected_class)
39
45
  elsif expected_class != actual_exception.class
40
46
  if expected_message && !(actual_message.to_s =~ %r[#{expected_message}])
41
- pass new_message.not_raised(expected_class).with_message(expected_message)
47
+ pass new_message.raises(expected_class).with_message(expected_message)
42
48
  else
43
- pass new_message.not_raised(expected_class)
49
+ pass new_message.raises(expected_class)
44
50
  end
45
51
  else
46
- message = should_have_message.not_raised(expected_class)
52
+ message = new_message.expected_to_not_raise(expected_class)
47
53
  if expected_message
48
54
  fail message.with_message(expected_message).but.raised(actual_exception.class).
49
55
  with_message(actual_exception.message)
50
56
  else
51
- fail message.but.raised(actual_exception.class)
57
+ fail message
52
58
  end
53
59
  end
54
60
  end # devaluate
@@ -11,6 +11,8 @@ module Riot
11
11
  register :respond_to
12
12
  register :responds_to
13
13
 
14
+ # (see Riot::AssertionMacro#evaluate)
15
+ # @param [Symbol, String] expected the method name that actual should respond to
14
16
  def evaluate(actual, expected)
15
17
  if actual.respond_to?(expected)
16
18
  pass(new_message.responds_to(expected))
@@ -19,11 +21,13 @@ module Riot
19
21
  end
20
22
  end
21
23
 
24
+ # (see Riot::AssertionMacro#devaluate)
25
+ # @param [Symbol, String] expected the method name that actual should not respond to
22
26
  def devaluate(actual, expected)
23
27
  if actual.respond_to?(expected)
24
28
  fail(expected_message.method(expected).is_defined)
25
29
  else
26
- pass new_message.does_not_respond_to(expected)
30
+ pass new_message.responds_to(expected)
27
31
  end
28
32
  end
29
33
 
@@ -11,14 +11,18 @@ module Riot
11
11
  register :same_elements
12
12
  require 'set'
13
13
 
14
+ # (see Riot::AssertionMacro#evaluate)
15
+ # @param [Object] expected the collection of elements that actual should be equivalent to
14
16
  def evaluate(actual, expected)
15
17
  same = (Set.new(expected) == Set.new(actual))
16
- same ? pass : fail(expected_message.elements(expected).to_match(actual))
18
+ same ? pass(new_message.has_same_elements_as(expected)) : fail(expected_message.elements(expected).to_match(actual))
17
19
  end
18
20
 
21
+ # (see Riot::AssertionMacro#devaluate)
22
+ # @param [Object] expected the collection of elements that actual should not be equivalent to
19
23
  def devaluate(actual, expected)
20
24
  same = (Set.new(expected) == Set.new(actual))
21
- same ? fail(expected_message.elements(expected).not_to_match(actual)) : pass
25
+ same ? fail(expected_message.elements(expected).not_to_match(actual)) : pass(new_message.has_same_elements_as(expected))
22
26
  end
23
27
 
24
28
  end
@@ -14,14 +14,18 @@ module Riot
14
14
  class SizeMacro < AssertionMacro
15
15
  register :size
16
16
 
17
+ # (see Riot::AssertionMacro#evaluate)
18
+ # @param [Number] expected the expected size of actual
17
19
  def evaluate(actual, expected)
18
20
  failure_message = expected_message.size_of(actual).to_be(expected).not(actual.size)
19
21
  expected === actual.size ? pass(new_message.is_of_size(expected)) : fail(failure_message)
20
22
  end
21
23
 
24
+ # (see Riot::AssertionMacro#devaluate)
25
+ # @param [Number] expected the unexpected size of actual
22
26
  def devaluate(actual, expected)
23
27
  failure_message = expected_message.size_of(actual).to_not_be(expected).not(actual.size)
24
- expected === actual.size ? fail(failure_message) : pass(new_message.is_not_size(expected))
28
+ expected === actual.size ? fail(failure_message) : pass(new_message.is_size(expected))
25
29
  end
26
30
  end
27
31
  end
@@ -4,14 +4,24 @@ require 'riot/context_helpers'
4
4
  module Riot
5
5
  RootContext = Struct.new(:setups, :teardowns, :detailed_description, :options)
6
6
 
7
+ # Defines the classes {Riot::Context} will use when creating new assertions and situations.
7
8
  module ContextClassOverrides
9
+ # Returns the default class used for generating new {Riot::Assertion Assertion} instances. Defaults to
10
+ # {Riot::Assertion}.
11
+ #
12
+ # @return [Class]
8
13
  def assertion_class; Assertion; end
14
+
15
+ # Returns the default class used for generating new {Riot::Situation Situation} instances. Defaults to
16
+ # {Riot::Situation}.
17
+ #
18
+ # @return [Class]
9
19
  def situation_class; Situation; end
10
20
  end # ContextClassOverrides
11
21
 
12
- # You make your assertions within a Context. The context stores setup and teardown blocks, and allows for
13
- # nesting and refactoring into helpers. Extension developers may also configure ContextMiddleware objects
14
- # in order to extend the functionality of a Context.
22
+ # An {Riot::Assertion} is declared within a Context. The context stores setup and teardown
23
+ # blocks, and allows for nesting and refactoring. Extension developers may also configure
24
+ # {Riot::ContextMiddleware Middleware} objects in order to extend the functionality of a Context.
15
25
  class Context
16
26
  include ContextClassOverrides
17
27
  include ContextOptions
@@ -19,19 +29,24 @@ module Riot
19
29
 
20
30
  # The set of middleware helpers configured for the current test space.
21
31
  #
22
- # @return [Array]
32
+ # @return [Array<Riot::ContextMiddleware>]
23
33
  def self.middlewares; @middlewares ||= []; end
24
34
 
25
- # The description of the context.
35
+ # The partial description of just this context.
26
36
  #
27
37
  # @return [String]
28
38
  attr_reader :description
29
39
 
30
40
  # The parent context.
31
41
  #
32
- # @return [Riot::Context]
42
+ # @return [Riot::Context, nil] a context or nil
33
43
  attr_reader :parent
34
44
 
45
+ # Creates a new Context
46
+ #
47
+ # @param [String] description a partial description of this context
48
+ # @param [Riot::Context, nil] parent a parent context or nothing
49
+ # @param [lambda] definition the body of this context
35
50
  def initialize(description, parent=nil, &definition)
36
51
  @parent = parent || RootContext.new([],[], "", {})
37
52
  @description = description
@@ -43,25 +58,34 @@ module Riot
43
58
  # Create a new test context.
44
59
  #
45
60
  # @param [String] description
61
+ # @return [Riot::Context] the newly created context
46
62
  def context(description, &definition)
47
63
  new_context(description, self.class, &definition)
48
64
  end
49
65
  alias_method :describe, :context
50
66
 
67
+ # @private
51
68
  # Returns an ordered list of the setup blocks for the context.
52
69
  #
53
- # @return [Array[Riot::RunnableBlock]]
70
+ # @return [Array<Riot::RunnableBlock>]
54
71
  def setups
55
72
  @parent.setups + @setups
56
73
  end
57
74
 
75
+ # @private
58
76
  # Returns an ordered list of the teardown blocks for the context.
59
77
  #
60
- # @return [Array[Riot::RunnableBlock]]
78
+ # @return [Array<Riot::RunnableBlock>]
61
79
  def teardowns
62
80
  @parent.teardowns + @teardowns
63
81
  end
64
82
 
83
+ # Executes the setups, hookups, assertions, and teardowns and passes results on to a given
84
+ # {Riot::Reporter Reporter}. Sub-contexts will also be executed and provided the given reporter. A new
85
+ # {Riot::Situation Situation} will be created from the specified {#situation_class Situation class}.
86
+ #
87
+ # @param [Riot::Reporter] reporter the reporter to report results to
88
+ # @return [Riot::Reporter] the given reporter
65
89
  def run(reporter)
66
90
  reporter.describe_context(self) unless @assertions.empty?
67
91
  local_run(reporter, situation_class.new)
@@ -69,17 +93,26 @@ module Riot
69
93
  reporter
70
94
  end
71
95
 
96
+ # @private
97
+ # Used mostly for testing purposes; this method does the actual running of just this context.
98
+ # @param [Riot::Reporter] reporter the reporter to report results to
99
+ # @param [Riot::Situation] situation the situation to use for executing the context.
72
100
  def local_run(reporter, situation)
73
101
  runnables.each { |runnable| reporter.report(runnable.to_s, runnable.run(situation)) }
74
102
  end
75
103
 
76
- # Prints the full description from the context tree
104
+ # Prints the full description from the context tree, grabbing the description from the parent and
105
+ # appending the description given to this context.
106
+ #
107
+ # @return [String] the full description for this context
77
108
  def detailed_description
78
109
  "#{parent.detailed_description} #{description}".strip
79
110
  end
80
111
 
81
112
  private
82
113
 
114
+ # Iterative over the registered middlewares and let them configure this context instance if they so
115
+ # choose. {Riot::AllImportantMiddleware} will always be the last in the chain.
83
116
  def prepare_middleware(&context_definition)
84
117
  last_middleware = AllImportantMiddleware.new(&context_definition)
85
118
  Context.middlewares.inject(last_middleware) do |last_middleware, middleware|
@@ -87,12 +120,27 @@ module Riot
87
120
  end.call(self)
88
121
  end
89
122
 
123
+ # The collection of things that are {Riot::RunnableBlock} instances in this context.
124
+ #
125
+ # @return [Array<Riot::RunnableBlock>]
90
126
  def runnables
91
127
  setups + @assertions + teardowns
92
128
  end
93
129
 
94
- def run_sub_contexts(reporter) @contexts.each { |ctx| ctx.run(reporter) }; end
130
+ # Execute each sub context.
131
+ #
132
+ # @param [Riot::Reporter] reporter the reporter instance to use
133
+ # @return [nil]
134
+ def run_sub_contexts(reporter)
135
+ @contexts.each { |ctx| ctx.run(reporter) }
136
+ end
95
137
 
138
+ # Creates a new context instance and appends it to the set of immediate children sub-contexts.
139
+ #
140
+ # @param [String] description a partial description
141
+ # @param [Class] klass the context class that a sub-context will be generated from
142
+ # @param [lambda] definition the body of the sub-context
143
+ # @return [#run] something that hopefully responds to run and is context-like
96
144
  def new_context(description, klass, &definition)
97
145
  (@contexts << klass.new(description, self, &definition)).last
98
146
  end
@@ -14,6 +14,9 @@ module Riot
14
14
  #
15
15
  # If you provide +true+ as the first argument, the setup will be unshifted onto the list of setups,
16
16
  # ensuring it will be run before any other setups. This is really only useful for context middlewares.
17
+ #
18
+ # @param [Boolean] premium indicates importance of the setup
19
+ # @return [Riot::Setup]
17
20
  def setup(premium=false, &definition)
18
21
  setup = Setup.new(&definition)
19
22
  premium ? @setups.unshift(setup) : @setups.push(setup)
@@ -29,6 +32,9 @@ module Riot
29
32
  # helper(:foo) { "bar" }
30
33
  # asserts("a foo") { foo }.equals("bar")
31
34
  # end
35
+ #
36
+ # @param [String, Symbol] name the name of the helper
37
+ # @return [Riot::Helper]
32
38
  def helper(name, &block)
33
39
  (@setups << Helper.new(name, &block)).last
34
40
  end
@@ -44,6 +50,8 @@ module Riot
44
50
  # You would do this:
45
51
  #
46
52
  # hookup { topic.do_something } # Yay!
53
+ #
54
+ # @return [Riot::Setup]
47
55
  def hookup(&definition)
48
56
  setup { self.instance_eval(&definition); topic }
49
57
  end
@@ -51,6 +59,8 @@ module Riot
51
59
  # Add a teardown block. You may define multiple of these as well.
52
60
  #
53
61
  # teardown { Bombs.drop! }
62
+ #
63
+ # @return [Riot::Setup]
54
64
  def teardown(&definition)
55
65
  (@teardowns << Setup.new(&definition)).last
56
66
  end
@@ -73,7 +83,8 @@ module Riot
73
83
  # Passing a Symbol to +asserts+ enables this behaviour. For more information on
74
84
  # assertion macros, see {Riot::AssertionMacro}.
75
85
  #
76
- # @param [String, Symbol] the property being tested
86
+ # @param [String, Symbol] what description of test or property to inspect on the topic
87
+ # @return [Riot::Assertion]
77
88
  def asserts(what, &definition)
78
89
  new_assertion("asserts", what, &definition)
79
90
  end
@@ -83,7 +94,8 @@ module Riot
83
94
  #
84
95
  # should("ensure expected") { "bar" }.equals("bar")
85
96
  #
86
- # @param [String, Symbol] the property being tested
97
+ # @param [String, Symbol] what description of test or property to inspect on the topic
98
+ # @return [Riot::Assertion]
87
99
  def should(what, &definition)
88
100
  new_assertion("should", what, &definition)
89
101
  end
@@ -106,7 +118,8 @@ module Riot
106
118
  # Passing a Symbol to +denies+ enables this behaviour. For more information on
107
119
  # assertion macros, see {Riot::AssertionMacro}.
108
120
  #
109
- # @param [String, Symbol] the property being tested
121
+ # @param [String, Symbol] what description of test or property to inspect on the topic
122
+ # @return [Riot::Assertion]
110
123
  def denies(what, &definition)
111
124
  new_assertion("denies", what, true, &definition)
112
125
  end
@@ -114,6 +127,9 @@ module Riot
114
127
  # Makes an assertion on the topic itself, e.g.
115
128
  #
116
129
  # asserts_topic.matches(/^ab+/)
130
+ #
131
+ # @param [String] what description of test
132
+ # @return [Riot::Assertion]
117
133
  def asserts_topic(what="that it")
118
134
  asserts(what) { topic }
119
135
  end
@@ -124,7 +140,7 @@ module Riot
124
140
  description = "#{scope} ##{what}"
125
141
  elsif what.kind_of?(Array)
126
142
  definition ||= proc { topic.send(*what) }
127
- description = "#{scope} ##{what.shift} with argument(s): #{what}"
143
+ description = "#{scope} ##{what.first} with argument(s): #{what[1..-1]}"
128
144
  else
129
145
  description = "#{scope} #{what}"
130
146
  end