spinach 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +1 -0
  2. data/.yardopts +6 -0
  3. data/Gemfile +1 -0
  4. data/Guardfile +7 -0
  5. data/Rakefile +1 -0
  6. data/Readme.md +147 -10
  7. data/features/{generate_features.feature → automatic_feature_generation.feature} +0 -0
  8. data/features/steps/automatic_feature_generation.rb +5 -2
  9. data/features/steps/exit_status.rb +8 -3
  10. data/features/steps/feature_name_guessing.rb +4 -1
  11. data/features/steps/reporting/display_run_summary.rb +7 -4
  12. data/features/steps/reporting/error_reporting.rb +7 -2
  13. data/features/steps/reporting/show_step_source_location.rb +9 -5
  14. data/features/steps/reporting/undefined_feature_reporting.rb +4 -1
  15. data/features/steps/rspec_compatibility.rb +6 -2
  16. data/features/support/spinach_runner.rb +2 -9
  17. data/lib/spinach/capybara.rb +3 -4
  18. data/lib/spinach/cli.rb +0 -1
  19. data/lib/spinach/config.rb +0 -8
  20. data/lib/spinach/dsl.rb +4 -13
  21. data/lib/spinach/exceptions.rb +1 -1
  22. data/lib/spinach/feature_steps.rb +1 -9
  23. data/lib/spinach/generators/feature_generator.rb +3 -2
  24. data/lib/spinach/generators.rb +1 -1
  25. data/lib/spinach/hookable.rb +81 -0
  26. data/lib/spinach/hooks.rb +132 -0
  27. data/lib/spinach/reporter/stdout/error_reporting.rb +163 -0
  28. data/lib/spinach/reporter/stdout.rb +3 -151
  29. data/lib/spinach/reporter.rb +39 -25
  30. data/lib/spinach/runner/{feature.rb → feature_runner.rb} +5 -15
  31. data/lib/spinach/runner/scenario_runner.rb +65 -0
  32. data/lib/spinach/runner.rb +5 -11
  33. data/lib/spinach/version.rb +1 -1
  34. data/lib/spinach.rb +20 -8
  35. data/spinach.gemspec +2 -3
  36. data/test/spinach/capybara_test.rb +4 -3
  37. data/test/spinach/cli_test.rb +0 -1
  38. data/test/spinach/feature_steps_test.rb +6 -23
  39. data/test/spinach/generators/feature_generator_test.rb +2 -2
  40. data/test/spinach/generators_test.rb +2 -2
  41. data/test/spinach/hookable_test.rb +59 -0
  42. data/test/spinach/hooks_test.rb +28 -0
  43. data/test/spinach/reporter/stdout/error_reporting_test.rb +265 -0
  44. data/test/spinach/reporter/stdout_test.rb +1 -238
  45. data/test/spinach/reporter_test.rb +58 -103
  46. data/test/spinach/runner/{feature_test.rb → feature_runner_test.rb} +21 -23
  47. data/test/spinach/runner/scenario_runner_test.rb +111 -0
  48. data/test/spinach/runner_test.rb +1 -1
  49. data/test/spinach_test.rb +19 -18
  50. data/test/test_helper.rb +1 -1
  51. metadata +60 -61
  52. data/lib/spinach/runner/scenario.rb +0 -77
  53. data/test/spinach/runner/scenario_test.rb +0 -120
@@ -0,0 +1,132 @@
1
+ require_relative 'hookable'
2
+
3
+ module Spinach
4
+ # Spinach's hooks is a subscription mechanism to allow developers to define
5
+ # certain callbacks given several Spinach signals, like running a feature,
6
+ # executing a particular step and such.
7
+ class Hooks
8
+ include Hookable
9
+
10
+ # Runs before the entire spinach run
11
+ #
12
+ # @example
13
+ # Spinach.before_run do
14
+ # # Whatever
15
+ # end
16
+ hook :before_run
17
+
18
+ # Runs after the entire spinach run
19
+ #
20
+ # @example
21
+ # Spinach.after_run do |status|
22
+ # # status is true when the run is successful, false otherwise
23
+ # end
24
+ hook :after_run
25
+
26
+ # Runs before every feature,
27
+ #
28
+ # @example
29
+ # Spinach.before_feature do |feature_data|
30
+ # # feature_data is a hash of the parsed feature data
31
+ # end
32
+ hook :before_feature
33
+
34
+ # Runs after every feature
35
+ #
36
+ # @example
37
+ # Spinach.after_feature do |feature_data|
38
+ # # feature_data is a hash of the parsed feature data
39
+ # end
40
+ hook :after_feature
41
+
42
+ # Runs when an undefined feature is found
43
+ #
44
+ # @example
45
+ # Spinach.on_undefined_feature do |feature_data, exception|
46
+ # # feature_data is a hash of the parsed feature data
47
+ # # exception contains the thrown exception
48
+ # end
49
+ hook :on_undefined_feature
50
+
51
+ # Runs before every scenario
52
+ #
53
+ # @example
54
+ # Spinach.before_scenario do |scenario_data|
55
+ # # feature_data is a hash of the parsed scenario data
56
+ # end
57
+ hook :before_scenario
58
+
59
+ # Runs after every scenario
60
+ #
61
+ # @example
62
+ # Spinach.after_scenario do |scenario_data|
63
+ # # feature_data is a hash of the parsed scenario data
64
+ # end
65
+ hook :after_scenario
66
+
67
+ # Runs before every step execution
68
+ #
69
+ # @example
70
+ # Spinach.before_step do |step_data|
71
+ # # step_data contains a hash with this step's data
72
+ # end
73
+ hook :before_step
74
+
75
+ # Runs after every step execution
76
+ #
77
+ # @example
78
+ # Spinach.before_step do |step_data|
79
+ # # step_data contains a hash with this step's data
80
+ # end
81
+ hook :after_step
82
+
83
+ # Runs after every successful step execution
84
+ #
85
+ # @example
86
+ # Spinach.on_successful_step do |step_data, location|
87
+ # # step_data contains a hash with this step's data
88
+ # # step_location contains a string indication this step definition's
89
+ # # location
90
+ # end
91
+ hook :on_successful_step
92
+
93
+ # Runs after every failed step execution
94
+ #
95
+ # @example
96
+ # Spinach.on_failed_step do |step_data, location|
97
+ # # step_data contains a hash with this step's data
98
+ # # step_location contains a string indication this step definition's
99
+ # # location
100
+ # end
101
+ hook :on_failed_step
102
+
103
+ # Runs after every step execution that raises an exception
104
+ #
105
+ # @example
106
+ # Spinach.on_error_step do |step_data, location|
107
+ # # step_data contains a hash with this step's data
108
+ # # step_location contains a string indication this step definition's
109
+ # # location
110
+ # end
111
+ hook :on_error_step
112
+
113
+ # Runs every time a step which is not defined is called
114
+ #
115
+ # @example
116
+ # Spinach.on_undefined_step do |step_data, location|
117
+ # # step_data contains a hash with this step's data
118
+ # # step_location contains a string indication this step definition's
119
+ # # location
120
+ # end
121
+ hook :on_undefined_step
122
+
123
+ # Runs every time a step is skipped because there has been an unsuccessful
124
+ # one just before.
125
+ #
126
+ # @example
127
+ # Spinach.on_undefined_step do |step_data|
128
+ # # step_data contains a hash with this step's data
129
+ # end
130
+ hook :on_skipped_step
131
+ end
132
+ end
@@ -0,0 +1,163 @@
1
+ module Spinach
2
+ class Reporter
3
+ class Stdout < Reporter
4
+ # This module handles Stdout's reporter error reporting capabilities.
5
+ module ErrorReporting
6
+
7
+ # Prints the errors for this run.
8
+ #
9
+ def error_summary
10
+ error.puts "\nError summary:\n"
11
+ report_error_steps
12
+ report_failed_steps
13
+ report_undefined_features
14
+ report_undefined_steps
15
+ end
16
+
17
+ # Prints the steps that raised an error.
18
+ #
19
+ def report_error_steps
20
+ report_errors('Errors', error_steps, :light_red) if error_steps.any?
21
+ end
22
+
23
+ # Prints failing steps.
24
+ #
25
+ def report_failed_steps
26
+ report_errors('Failures', failed_steps, :light_red) if failed_steps.any?
27
+ end
28
+
29
+ # Prints undefined steps.
30
+ #
31
+ def report_undefined_steps
32
+ report_errors('Undefined steps', undefined_steps, :yellow) if undefined_steps.any?
33
+ end
34
+
35
+ def report_undefined_features
36
+ if undefined_features.any?
37
+ error.puts " Undefined features (#{undefined_features.length})".light_yellow
38
+ undefined_features.each do |feature|
39
+ error.puts " #{feature['name']}".yellow
40
+ end
41
+ end
42
+ end
43
+
44
+ # Prints the error for a given set of steps
45
+ #
46
+ # @param [String] banner
47
+ # the text to prepend as the title
48
+ #
49
+ # @param [Array] steps
50
+ # the steps to output
51
+ #
52
+ # @param [Symbol] color
53
+ # The color code to use with Colorize to colorize the output.
54
+ #
55
+ def report_errors(banner, steps, color)
56
+ error.puts " #{banner} (#{steps.length})".colorize(color)
57
+ steps.each do |error|
58
+ report_error error
59
+ end
60
+ error.puts ""
61
+ end
62
+
63
+ # Prints an error in a nice format
64
+ #
65
+ # @param [Array] error
66
+ # An array containing the feature, scenario, step and exception
67
+ #
68
+ # @param [Symbol] format
69
+ # The format to output the error. Currently supproted formats are
70
+ # :summarized (default) and :full
71
+ #
72
+ # @return [String]
73
+ # The error report
74
+ #
75
+ def report_error(error, format=:summarized)
76
+ case format
77
+ when :summarized
78
+ self.error.puts summarized_error(error)
79
+ when :full
80
+ self.error.puts full_error(error)
81
+ else
82
+ raise "Format not defined"
83
+ end
84
+ end
85
+
86
+ # Returns summarized error report
87
+ #
88
+ # @param [Array] error
89
+ # An array containing the feature, scenario, step and exception
90
+ #
91
+ # @return [String]
92
+ # The summarized error report
93
+ #
94
+ def summarized_error(error)
95
+ feature, scenario, step, exception = error
96
+ summary = " #{feature['name']} :: #{scenario['name']} :: #{full_step step}"
97
+ if exception.kind_of?(Spinach::StepNotDefinedException)
98
+ summary.yellow
99
+ else
100
+ summary.red
101
+ end
102
+ end
103
+
104
+ # Returns a complete error report
105
+ #
106
+ # @param [Array] error
107
+ # An array containing the feature, scenario, step and exception
108
+ #
109
+ # @return [String]
110
+ # The coplete error report
111
+ #
112
+ def full_error(error)
113
+ feature, scenario, step, exception = error
114
+ output = "\n"
115
+ output += report_exception(exception)
116
+ output +="\n"
117
+
118
+ if exception.kind_of?(Spinach::StepNotDefinedException)
119
+ output << "\n"
120
+ output << " You can define it with: \n\n".yellow
121
+ suggestion = Generators::StepGenerator.new(step).generate
122
+ suggestion.split("\n").each do |line|
123
+ output << " #{line}\n".yellow
124
+ end
125
+ output << "\n"
126
+ else
127
+ if options[:backtrace]
128
+ output += "\n"
129
+ exception.backtrace.map do |line|
130
+ output << " #{line}\n"
131
+ end
132
+ else
133
+ output << " #{exception.backtrace[0]}"
134
+ end
135
+ end
136
+ output
137
+ end
138
+
139
+
140
+ # Prints a information when an exception is raised.
141
+ #
142
+ # @param [Exception] exception
143
+ # The exception to report
144
+ #
145
+ # @return [String]
146
+ # The exception report
147
+ #
148
+ def report_exception(exception)
149
+ output = exception.message.split("\n").map{ |line|
150
+ " #{line}"
151
+ }.join("\n")
152
+
153
+ if exception.kind_of?(Spinach::StepNotDefinedException)
154
+ output.yellow
155
+ else
156
+ output.red
157
+ end
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require_relative 'stdout/error_reporting'
2
3
 
3
4
  module Spinach
4
5
  class Reporter
@@ -6,6 +7,8 @@ module Spinach
6
7
  #
7
8
  class Stdout < Reporter
8
9
 
10
+ include ErrorReporting
11
+
9
12
  # The output buffers to store the reports.
10
13
  attr_reader :out, :error
11
14
 
@@ -191,138 +194,6 @@ module Spinach
191
194
  out.puts "Steps Summary: #{successful_summary}, #{undefined_summary}, #{failed_summary}, #{error_summary}\n\n"
192
195
  end
193
196
 
194
- # Prints the errors for this run.
195
- #
196
- def error_summary
197
- error.puts "\nError summary:\n"
198
- report_error_steps
199
- report_failed_steps
200
- report_undefined_features
201
- report_undefined_steps
202
- end
203
-
204
- # Prints the steps that raised an error.
205
- #
206
- def report_error_steps
207
- report_errors('Errors', error_steps, :light_red) if error_steps.any?
208
- end
209
-
210
- # Prints failing steps.
211
- #
212
- def report_failed_steps
213
- report_errors('Failures', failed_steps, :light_red) if failed_steps.any?
214
- end
215
-
216
- # Prints undefined steps.
217
- #
218
- def report_undefined_steps
219
- report_errors('Undefined steps', undefined_steps, :yellow) if undefined_steps.any?
220
- end
221
-
222
- def report_undefined_features
223
- if undefined_features.any?
224
- error.puts " Undefined features (#{undefined_features.length})".light_yellow
225
- undefined_features.each do |feature|
226
- error.puts " #{feature['name']}".yellow
227
- end
228
- end
229
- end
230
-
231
- # Prints the error for a given set of steps
232
- #
233
- # @param [String] banner
234
- # the text to prepend as the title
235
- #
236
- # @param [Array] steps
237
- # the steps to output
238
- #
239
- # @param [Symbol] color
240
- # The color code to use with Colorize to colorize the output.
241
- #
242
- def report_errors(banner, steps, color)
243
- error.puts " #{banner} (#{steps.length})".colorize(color)
244
- steps.each do |error|
245
- report_error error
246
- end
247
- error.puts ""
248
- end
249
-
250
- # Prints an error in a nice format
251
- #
252
- # @param [Array] error
253
- # An array containing the feature, scenario, step and exception
254
- #
255
- # @param [Symbol] format
256
- # The format to output the error. Currently supproted formats are
257
- # :summarized (default) and :full
258
- #
259
- # @returns [String]
260
- # The error report
261
- #
262
- def report_error(error, format=:summarized)
263
- case format
264
- when :summarized
265
- self.error.puts summarized_error(error)
266
- when :full
267
- self.error.puts full_error(error)
268
- else
269
- raise "Format not defined"
270
- end
271
- end
272
-
273
- # Returns summarized error report
274
- #
275
- # @param [Array] error
276
- # An array containing the feature, scenario, step and exception
277
- #
278
- # @returns [String]
279
- # The summarized error report
280
- #
281
- def summarized_error(error)
282
- feature, scenario, step, exception = error
283
- summary = " #{feature['name']} :: #{scenario['name']} :: #{full_step step}"
284
- if exception.kind_of?(Spinach::StepNotDefinedException)
285
- summary.yellow
286
- else
287
- summary.red
288
- end
289
- end
290
-
291
- # Returns a complete error report
292
- #
293
- # @param [Array] error
294
- # An array containing the feature, scenario, step and exception
295
- #
296
- # @returns [String]
297
- # The coplete error report
298
- #
299
- def full_error(error)
300
- feature, scenario, step, exception = error
301
- output = "\n"
302
- output += report_exception(exception)
303
- output +="\n"
304
-
305
- if exception.kind_of?(Spinach::StepNotDefinedException)
306
- output << "\n"
307
- output << " You can define it with: \n\n".yellow
308
- suggestion = Generators::StepGenerator.new(step).generate
309
- suggestion.split("\n").each do |line|
310
- output << " #{line}\n".yellow
311
- end
312
- output << "\n"
313
- else
314
- if options[:backtrace]
315
- output += "\n"
316
- exception.backtrace.map do |line|
317
- output << " #{line}\n"
318
- end
319
- else
320
- output << " #{exception.backtrace[0]}"
321
- end
322
- end
323
- output
324
- end
325
-
326
197
  # Constructs the full step definition
327
198
  #
328
199
  # @param [Hash] step
@@ -332,25 +203,6 @@ module Spinach
332
203
  "#{step['keyword'].strip} #{step['name'].strip}"
333
204
  end
334
205
 
335
- # Prints a information when an exception is raised.
336
- #
337
- # @param [Exception] exception
338
- # The exception to report
339
- #
340
- # @returns [String]
341
- # The exception report
342
- #
343
- def report_exception(exception)
344
- output = exception.message.split("\n").map{ |line|
345
- " #{line}"
346
- }.join("\n")
347
-
348
- if exception.kind_of?(Spinach::StepNotDefinedException)
349
- output.yellow
350
- else
351
- output.red
352
- end
353
- end
354
206
  end
355
207
  end
356
208
  end
@@ -19,35 +19,32 @@ module Spinach
19
19
 
20
20
  # A Hash with options for the reporter
21
21
  #
22
- attr_reader :options
23
-
24
- attr_accessor :current_feature, :current_scenario
22
+ attr_reader :options, :current_feature, :current_scenario
25
23
 
26
24
  attr_reader :undefined_steps, :failed_steps, :error_steps, :undefined_features, :successful_steps
27
25
 
26
+ # Hooks the reporter to the runner endpoints
28
27
  def bind
29
- runner.after_run method(:after_run)
30
- feature_runner.before_run method(:before_feature_run)
31
- feature_runner.after_run method(:after_feature_run)
32
- feature_runner.when_not_found method(:on_feature_not_found)
33
- scenario_runner.before_run method(:before_scenario_run)
34
- scenario_runner.after_run method(:after_scenario_run)
35
- scenario_runner.on_successful_step method(:on_successful_step)
36
- scenario_runner.on_undefined_step method(:on_undefined_step)
37
- scenario_runner.on_failed_step method(:on_failed_step)
38
- scenario_runner.on_error_step method(:on_error_step)
39
- scenario_runner.on_skipped_step method(:on_skipped_step)
28
+ Spinach.hooks.tap do |hooks|
29
+ hooks.after_run { |*args| after_run(*args) }
30
+ hooks.before_feature { |*args| before_feature_run(*args) }
31
+ hooks.after_feature { |*args| after_feature_run(*args) }
32
+ hooks.on_undefined_feature { |*args| on_feature_not_found(*args) }
33
+ hooks.before_scenario { |*args| before_scenario_run(*args) }
34
+ hooks.after_scenario { |*args| after_scenario_run(*args) }
35
+ hooks.on_successful_step { |*args| on_successful_step(*args) }
36
+ hooks.on_undefined_step { |*args| on_undefined_step(*args) }
37
+ hooks.on_failed_step { |*args| on_failed_step(*args) }
38
+ hooks.on_error_step { |*args| on_error_step(*args) }
39
+ hooks.on_skipped_step { |*args| on_skipped_step(*args) }
40
40
 
41
- feature_runner.before_run method(:current_feature=)
42
- feature_runner.after_run method(:clear_current_feature)
43
- scenario_runner.before_run method(:current_scenario=)
44
- scenario_runner.after_run method(:clear_current_scenario)
41
+ hooks.before_feature { |*args| set_current_feature(*args) }
42
+ hooks.after_feature { |*args| clear_current_feature(*args) }
43
+ hooks.before_scenario { |*args| set_current_scenario(*args) }
44
+ hooks.after_scenario { |*args| clear_current_scenario(*args) }
45
+ end
45
46
  end
46
47
 
47
- def feature_runner; Runner::Feature; end
48
- def scenario_runner; Runner::Scenario; end
49
- def runner; Runner; end
50
-
51
48
  def after_run(*args); end;
52
49
  def before_feature_run(*args); end
53
50
  def after_feature_run(*args); end
@@ -60,14 +57,31 @@ module Spinach
60
57
  def on_undefined_step(*args); end;
61
58
  def on_skipped_step(*args); end;
62
59
 
60
+ # Stores the current feature
61
+ #
62
+ # @param [Hash]
63
+ # the data for this feature
64
+ def set_current_feature(data)
65
+ @current_feature = data
66
+ end
67
+
68
+ # Clears this current feature
63
69
  def clear_current_feature(*args)
64
- self.current_feature = nil
70
+ @current_feature = nil
65
71
  end
66
72
 
67
- def clear_current_scenario(*args)
68
- self.current_scenario = nil
73
+ # Stores the current scenario
74
+ #
75
+ # @param [Hash]
76
+ # the data for this scenario
77
+ def set_current_scenario(data)
78
+ @current_scenario = data
69
79
  end
70
80
 
81
+ # Clears this current scenario
82
+ def clear_current_scenario(*args)
83
+ @current_scenario = nil
84
+ end
71
85
  end
72
86
  end
73
87
 
@@ -1,22 +1,12 @@
1
- require 'hooks'
2
-
3
1
  module Spinach
4
2
  class Runner
5
3
  # A feature runner handles a particular feature run.
6
4
  #
7
- class Feature
8
- include Hooks
9
-
10
- # The {Reporter} used in this feature.
11
- attr_reader :reporter
5
+ class FeatureRunner
12
6
 
13
7
  # The file that describes the feature.
14
8
  attr_reader :filename
15
9
 
16
- define_hook :before_run
17
- define_hook :after_run
18
- define_hook :when_not_found
19
-
20
10
  # @param [String] filename
21
11
  # path to the feature file. Scenario line could be passed to run just
22
12
  # that scenario.
@@ -62,20 +52,20 @@ module Spinach
62
52
  #
63
53
  # @api public
64
54
  def run
65
- run_hook :before_run, data
55
+ Spinach.hooks.run_before_feature data
66
56
 
67
57
  scenarios.each do |scenario|
68
58
  if !@scenario_line || scenario['line'].to_s == @scenario_line
69
- success = Scenario.new(feature_name, scenario).run
59
+ success = ScenarioRunner.new(feature_name, scenario).run
70
60
  @failed = true unless success
71
61
  end
72
62
  end
73
63
 
74
64
  rescue Spinach::FeatureStepsNotFoundException => e
75
- run_hook :when_not_found, data, e
65
+ Spinach.hooks.run_on_undefined_feature data, e
76
66
  @failed = true
77
67
  ensure
78
- run_hook :after_run, data
68
+ Spinach.hooks.run_after_feature data
79
69
  return !@failed
80
70
  end
81
71
  end
@@ -0,0 +1,65 @@
1
+ module Spinach
2
+ class Runner
3
+ # A Scenario Runner handles a particular scenario run.
4
+ #
5
+ class ScenarioRunner
6
+ attr_reader :feature_name, :data
7
+
8
+ # @param [String] feature_name
9
+ # The feature name
10
+ #
11
+ # @param [Hash] data
12
+ # The parsed feature data.
13
+ #
14
+ # @api public
15
+ def initialize(feature_name, data)
16
+ @feature_name = feature_name
17
+ @data = data
18
+ end
19
+
20
+ def steps
21
+ @steps ||= data['steps']
22
+ end
23
+
24
+ # @return [FeatureSteps]
25
+ # The feature object used to run this scenario.
26
+ #
27
+ # @api public
28
+ def feature_steps
29
+ @feature_steps ||= Spinach.find_feature_steps(feature_name).new
30
+ end
31
+
32
+ # Runs this scenario
33
+ # @return [True, False]
34
+ # true if this scenario succeeded, false if not
35
+ def run
36
+ Spinach.hooks.run_before_scenario data
37
+ steps.each do |step|
38
+ Spinach.hooks.run_before_step step
39
+ unless @exception
40
+ begin
41
+ step_location = feature_steps.execute_step(step['name'])
42
+ Spinach.hooks.run_on_successful_step step, step_location
43
+ rescue Spinach::FeatureStepsNotFoundException => e
44
+ raise e
45
+ rescue *Spinach.config[:failure_exceptions] => e
46
+ @exception = e
47
+ Spinach.hooks.run_on_failed_step step, @exception, step_location
48
+ rescue Spinach::StepNotDefinedException => e
49
+ @exception = e
50
+ Spinach.hooks.run_on_undefined_step step, @exception
51
+ rescue Exception => e
52
+ @exception = e
53
+ Spinach.hooks.run_on_error_step step, @exception, step_location
54
+ end
55
+ else
56
+ Spinach.hooks.run_on_skipped_step step
57
+ end
58
+ Spinach.hooks.run_after_step step
59
+ end
60
+ Spinach.hooks.run_after_scenario data
61
+ !@exception
62
+ end
63
+ end
64
+ end
65
+ end