rspec-core 2.11.1 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/Changelog.md +59 -0
  2. data/README.md +22 -0
  3. data/features/command_line/example_name_option.feature +5 -5
  4. data/features/command_line/exit_status.feature +6 -6
  5. data/features/command_line/format_option.feature +2 -2
  6. data/features/command_line/line_number_appended_to_path.feature +2 -2
  7. data/features/command_line/line_number_option.feature +2 -2
  8. data/features/command_line/pattern_option.feature +2 -2
  9. data/features/command_line/rake_task.feature +62 -8
  10. data/features/command_line/ruby.feature +1 -1
  11. data/features/command_line/tag.feature +1 -1
  12. data/features/configuration/alias_example_to.feature +2 -2
  13. data/features/configuration/custom_settings.feature +3 -3
  14. data/features/configuration/default_path.feature +3 -3
  15. data/features/configuration/fail_fast.feature +5 -5
  16. data/features/configuration/read_options_from_file.feature +4 -4
  17. data/features/example_groups/basic_structure.feature +2 -2
  18. data/features/example_groups/shared_context.feature +3 -3
  19. data/features/example_groups/shared_examples.feature +25 -7
  20. data/features/expectation_framework_integration/configure_expectation_framework.feature +6 -6
  21. data/features/filtering/exclusion_filters.feature +5 -5
  22. data/features/filtering/if_and_unless.feature +5 -5
  23. data/features/filtering/inclusion_filters.feature +5 -5
  24. data/features/filtering/run_all_when_everything_filtered.feature +3 -3
  25. data/features/formatters/custom_formatter.feature +2 -2
  26. data/features/formatters/json_formatter.feature +30 -0
  27. data/features/formatters/text_formatter.feature +5 -5
  28. data/features/helper_methods/arbitrary_methods.feature +2 -2
  29. data/features/helper_methods/let.feature +2 -2
  30. data/features/helper_methods/modules.feature +6 -6
  31. data/features/hooks/around_hooks.feature +11 -11
  32. data/features/hooks/before_and_after_hooks.feature +15 -11
  33. data/features/hooks/filtering.feature +6 -6
  34. data/features/metadata/current_example.feature +1 -1
  35. data/features/metadata/described_class.feature +1 -1
  36. data/features/metadata/user_defined.feature +4 -4
  37. data/features/mock_framework_integration/use_any_framework.feature +6 -6
  38. data/features/mock_framework_integration/use_flexmock.feature +5 -5
  39. data/features/mock_framework_integration/use_mocha.feature +5 -5
  40. data/features/mock_framework_integration/use_rr.feature +5 -5
  41. data/features/mock_framework_integration/use_rspec.feature +5 -5
  42. data/features/pending/pending_examples.feature +11 -11
  43. data/features/spec_files/arbitrary_file_suffix.feature +1 -1
  44. data/features/step_definitions/additional_cli_steps.rb +2 -0
  45. data/features/subject/attribute_of_subject.feature +5 -5
  46. data/features/subject/explicit_subject.feature +5 -5
  47. data/features/subject/implicit_receiver.feature +2 -2
  48. data/features/subject/implicit_subject.feature +3 -3
  49. data/lib/autotest/rspec2.rb +1 -1
  50. data/lib/rspec/core.rb +53 -32
  51. data/lib/rspec/core/configuration.rb +123 -15
  52. data/lib/rspec/core/configuration_options.rb +17 -2
  53. data/lib/rspec/core/example.rb +5 -4
  54. data/lib/rspec/core/example_group.rb +19 -9
  55. data/lib/rspec/core/extensions/ordered.rb +12 -6
  56. data/lib/rspec/core/formatters.rb +55 -0
  57. data/lib/rspec/core/formatters/base_formatter.rb +43 -38
  58. data/lib/rspec/core/formatters/base_text_formatter.rb +9 -5
  59. data/lib/rspec/core/formatters/documentation_formatter.rb +1 -1
  60. data/lib/rspec/core/formatters/helpers.rb +30 -5
  61. data/lib/rspec/core/formatters/html_formatter.rb +58 -368
  62. data/lib/rspec/core/formatters/html_printer.rb +407 -0
  63. data/lib/rspec/core/formatters/json_formatter.rb +73 -0
  64. data/lib/rspec/core/formatters/snippet_extractor.rb +3 -1
  65. data/lib/rspec/core/hooks.rb +4 -4
  66. data/lib/rspec/core/metadata.rb +14 -6
  67. data/lib/rspec/core/mocking/with_mocha.rb +25 -2
  68. data/lib/rspec/core/mocking/with_rspec.rb +6 -2
  69. data/lib/rspec/core/option_parser.rb +28 -7
  70. data/lib/rspec/core/project_initializer.rb +0 -1
  71. data/lib/rspec/core/rake_task.rb +49 -38
  72. data/lib/rspec/core/reporter.rb +2 -2
  73. data/lib/rspec/core/shared_example_group.rb +89 -41
  74. data/lib/rspec/core/subject.rb +6 -2
  75. data/lib/rspec/core/version.rb +1 -1
  76. data/lib/rspec/core/world.rb +2 -2
  77. data/spec/autotest/rspec_spec.rb +6 -1
  78. data/spec/command_line/order_spec.rb +67 -0
  79. data/spec/rspec/core/configuration_options_spec.rb +45 -38
  80. data/spec/rspec/core/configuration_spec.rb +219 -44
  81. data/spec/rspec/core/deprecations_spec.rb +9 -0
  82. data/spec/rspec/core/drb_command_line_spec.rb +1 -7
  83. data/spec/rspec/core/drb_options_spec.rb +1 -1
  84. data/spec/rspec/core/dsl_spec.rb +17 -9
  85. data/spec/rspec/core/example_group_spec.rb +51 -5
  86. data/spec/rspec/core/example_spec.rb +39 -7
  87. data/spec/rspec/core/filter_manager_spec.rb +20 -30
  88. data/spec/rspec/core/formatters/base_formatter_spec.rb +29 -1
  89. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +6 -2
  90. data/spec/rspec/core/formatters/helpers_spec.rb +12 -0
  91. data/spec/rspec/core/formatters/html_formatted-1.8.7-rbx.html +462 -0
  92. data/spec/rspec/core/formatters/html_formatted-1.9.2-jruby.html +410 -0
  93. data/spec/rspec/core/formatters/html_formatted-1.9.3-rbx.html +462 -0
  94. data/spec/rspec/core/formatters/html_formatter_spec.rb +11 -3
  95. data/spec/rspec/core/formatters/json_formatter_spec.rb +110 -0
  96. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +8 -0
  97. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-rbx.html +462 -0
  98. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2-jruby.html +410 -0
  99. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3-rbx.html +462 -0
  100. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +11 -3
  101. data/spec/rspec/core/hooks_filtering_spec.rb +6 -6
  102. data/spec/rspec/core/metadata_spec.rb +29 -0
  103. data/spec/rspec/core/option_parser_spec.rb +42 -0
  104. data/spec/rspec/core/project_initializer_spec.rb +2 -2
  105. data/spec/rspec/core/rake_task_spec.rb +60 -17
  106. data/spec/rspec/core/reporter_spec.rb +17 -0
  107. data/spec/rspec/core/runner_spec.rb +1 -0
  108. data/spec/rspec/core/shared_example_group_spec.rb +17 -5
  109. data/spec/rspec/core/subject_spec.rb +11 -0
  110. data/spec/spec_helper.rb +32 -2
  111. data/spec/support/config_options_helper.rb +1 -10
  112. data/spec/support/helper_methods.rb +13 -0
  113. data/spec/support/isolated_directory.rb +10 -0
  114. data/spec/support/isolated_home_directory.rb +16 -0
  115. metadata +145 -148
  116. data/lib/rspec/core/extensions.rb +0 -4
@@ -4,7 +4,7 @@ module RSpec
4
4
  # This class extracts code snippets by looking at the backtrace of the passed error
5
5
  class SnippetExtractor
6
6
  class NullConverter; def convert(code, pre); code; end; end
7
-
7
+
8
8
  begin
9
9
  require 'syntax/convertors/html'
10
10
  @@converter = Syntax::Convertors::HTML.for_syntax "ruby"
@@ -40,6 +40,8 @@ module RSpec
40
40
  else
41
41
  "# Couldn't get snippet for #{file}"
42
42
  end
43
+ rescue SecurityError
44
+ "# Couldn't get snippet for #{file}"
43
45
  end
44
46
 
45
47
  def post_process(highlighted, offending_line)
@@ -119,7 +119,7 @@ module RSpec
119
119
  private
120
120
  def process host, globals, position, scope
121
121
  globals[position][scope].each do |hook|
122
- unless host.ancestors.any? { |a| a.hooks[position][scope].include? hook }
122
+ unless host.parent_groups.any? { |a| a.hooks[position][scope].include? hook }
123
123
  self[position][scope] << hook if scope == :each || hook.options_apply?(host)
124
124
  end
125
125
  end
@@ -426,7 +426,7 @@ module RSpec
426
426
 
427
427
  # @private
428
428
  def around_each_hooks_for(example, initial_procsy=nil)
429
- AroundHookCollection.new(ancestors.map {|a| a.hooks[:around][:each]}.flatten).for(example, initial_procsy)
429
+ AroundHookCollection.new(parent_groups.map {|a| a.hooks[:around][:each]}.flatten).for(example, initial_procsy)
430
430
  end
431
431
 
432
432
  private
@@ -448,11 +448,11 @@ module RSpec
448
448
  end
449
449
 
450
450
  def before_each_hooks_for(example)
451
- HookCollection.new(ancestors.reverse.map {|a| a.hooks[:before][:each]}.flatten).for(example)
451
+ HookCollection.new(parent_groups.reverse.map {|a| a.hooks[:before][:each]}.flatten).for(example)
452
452
  end
453
453
 
454
454
  def after_each_hooks_for(example)
455
- HookCollection.new(ancestors.map {|a| a.hooks[:after][:each]}.flatten).for(example)
455
+ HookCollection.new(parent_groups.map {|a| a.hooks[:after][:each]}.flatten).for(example)
456
456
  end
457
457
 
458
458
  def register_hook prepend_or_append, hook, *args, &block
@@ -31,6 +31,8 @@ module RSpec
31
31
  line = line.sub(/\A([^:]+:\d+)$/, '\\1')
32
32
  return nil if line == '-e:1'
33
33
  line
34
+ rescue SecurityError
35
+ nil
34
36
  end
35
37
 
36
38
  # @private
@@ -41,7 +43,18 @@ module RSpec
41
43
  # ExampleMetadataHash and GroupMetadataHash, which get mixed in to
42
44
  # Metadata for ExampleGroups and Examples (respectively).
43
45
  def [](key)
44
- return super if has_key?(key)
46
+ store_computed(key) unless has_key?(key)
47
+ super
48
+ end
49
+
50
+ def fetch(key, *args)
51
+ store_computed(key) unless has_key?(key)
52
+ super
53
+ end
54
+
55
+ private
56
+
57
+ def store_computed(key)
45
58
  case key
46
59
  when :location
47
60
  store(:location, location)
@@ -49,7 +62,6 @@ module RSpec
49
62
  file_path, line_number = file_and_line_number
50
63
  store(:file_path, file_path)
51
64
  store(:line_number, line_number)
52
- super
53
65
  when :execution_result
54
66
  store(:execution_result, {})
55
67
  when :describes, :described_class
@@ -61,13 +73,9 @@ module RSpec
61
73
  store(:full_description, full_description)
62
74
  when :description
63
75
  store(:description, build_description_from(*self[:description_args]))
64
- else
65
- super
66
76
  end
67
77
  end
68
78
 
69
- private
70
-
71
79
  def location
72
80
  "#{self[:file_path]}:#{self[:line_number]}"
73
81
  end
@@ -1,5 +1,28 @@
1
- require 'mocha/standalone'
2
- require 'mocha/object'
1
+ # In order to support all versions of mocha, we have to jump through some
2
+ # hoops here.
3
+ #
4
+ # mocha >= '0.13.0':
5
+ # require 'mocha/api' is required
6
+ # require 'mocha/object' raises a LoadError b/c the file no longer exists
7
+ # mocha < '0.13.0', >= '0.9.7'
8
+ # require 'mocha/api' is required
9
+ # require 'mocha/object' is required
10
+ # mocha < '0.9.7':
11
+ # require 'mocha/api' raises a LoadError b/c the file does not yet exist
12
+ # require 'mocha/standalone' is required
13
+ # require 'mocha/object' is required
14
+ begin
15
+ require 'mocha/api'
16
+
17
+ begin
18
+ require 'mocha/object'
19
+ rescue LoadError
20
+ # Mocha >= 0.13.0 no longer contains this file nor needs it to be loaded
21
+ end
22
+ rescue LoadError
23
+ require 'mocha/standalone'
24
+ require 'mocha/object'
25
+ end
3
26
 
4
27
  module RSpec
5
28
  module Core
@@ -3,9 +3,13 @@ require 'rspec/mocks'
3
3
  module RSpec
4
4
  module Core
5
5
  module MockFrameworkAdapter
6
-
6
+
7
7
  def self.framework_name; :rspec end
8
-
8
+
9
+ def self.configuration
10
+ RSpec::Mocks.configuration
11
+ end
12
+
9
13
  def setup_mocks_for_rspec
10
14
  RSpec::Mocks::setup(self)
11
15
  end
@@ -13,15 +13,35 @@ module RSpec::Core
13
13
 
14
14
  def parse!(args)
15
15
  return {} if args.empty?
16
- if args.include?("--formatter")
17
- RSpec.deprecate("the --formatter option", "-f or --format")
18
- args[args.index("--formatter")] = "--format"
19
- end
16
+
17
+ convert_deprecated_args(args)
18
+
20
19
  options = args.delete('--tty') ? {:tty => true} : {}
21
- parser(options).parse!(args)
20
+ begin
21
+ parser(options).parse!(args)
22
+ rescue OptionParser::InvalidOption => e
23
+ abort "#{e.message}\n\nPlease use --help for a listing of valid options"
24
+ end
25
+
22
26
  options
23
27
  end
24
28
 
29
+ def convert_deprecated_args(args)
30
+ args.map! { |arg|
31
+ case arg
32
+ when "--formatter"
33
+ RSpec.deprecate("the --formatter option", "-f or --format")
34
+ "--format"
35
+ when "--default_path"
36
+ "--default-path"
37
+ when "--line_number"
38
+ "--line-number"
39
+ else
40
+ arg
41
+ end
42
+ }
43
+ end
44
+
25
45
  alias_method :parse, :parse!
26
46
 
27
47
  def parser(options)
@@ -92,6 +112,7 @@ module RSpec::Core
92
112
  ' [d]ocumentation (group and example names)',
93
113
  ' [h]tml',
94
114
  ' [t]extmate',
115
+ ' [j]son',
95
116
  ' custom formatter class name') do |o|
96
117
  options[:formatters] ||= []
97
118
  options[:formatters] << [o]
@@ -139,7 +160,7 @@ FILTERING
139
160
  (options[:full_description] ||= []) << Regexp.compile(Regexp.escape(o))
140
161
  end
141
162
 
142
- parser.on('-l', '--line_number LINE', 'Specify line number of an example or group (may be',
163
+ parser.on('-l', '--line-number LINE', 'Specify line number of an example or group (may be',
143
164
  ' used more than once).') do |o|
144
165
  (options[:line_numbers] ||= []) << o
145
166
  end
@@ -158,7 +179,7 @@ FILTERING
158
179
  options[filter_type][name] = value.nil? ? true : eval(value) rescue value
159
180
  end
160
181
 
161
- parser.on('--default_path PATH', 'Set the default path where RSpec looks for examples (can',
182
+ parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can',
162
183
  ' be a path to a file or a directory).') do |path|
163
184
  options[:default_path] = path
164
185
  end
@@ -6,7 +6,6 @@ module RSpec
6
6
  end
7
7
 
8
8
  def run
9
- warn "The --configure option no longer needs any arguments, so #{@arg} was ignored." if @arg
10
9
  create_spec_helper_file
11
10
  create_dot_rspec_file
12
11
  delete_if_confirmed("autotest/discover.rb", <<-MESSAGE)
@@ -1,4 +1,3 @@
1
- require 'rspec/core'
2
1
  require 'rspec/core/deprecation'
3
2
  require 'rake'
4
3
  require 'rake/tasklib'
@@ -63,6 +62,9 @@ module RSpec
63
62
 
64
63
  # Use rcov for code coverage?
65
64
  #
65
+ # Due to the many ways `rcov` can run, if this option is enabled, it is
66
+ # required that `require 'rspec/autorun'` appears in `spec_helper`.rb
67
+ #
66
68
  # default:
67
69
  # false
68
70
  attr_accessor :rcov
@@ -109,34 +111,47 @@ module RSpec
109
111
  @rspec_opts = opts
110
112
  end
111
113
 
112
- def initialize(*args)
114
+ def initialize(*args, &task_block)
115
+ setup_ivars(args)
116
+
117
+ desc "Run RSpec code examples" unless ::Rake.application.last_comment
118
+
119
+ task name, *args do |_, task_args|
120
+ RakeFileUtils.send(:verbose, verbose) do
121
+ task_block.call(*[self, task_args].slice(0, task_block.arity)) if task_block
122
+ run_task verbose
123
+ end
124
+ end
125
+ end
126
+
127
+ def setup_ivars(args)
113
128
  @name = args.shift || :spec
114
- @pattern, @rcov_path, @rcov_opts, @ruby_opts, @rspec_opts = nil, nil, nil, nil, nil
129
+ @rcov_opts, @ruby_opts, @rspec_opts = nil, nil, nil
115
130
  @warning, @rcov = false, false
116
131
  @verbose, @fail_on_error = true, true
117
132
 
118
- yield self if block_given?
119
-
120
- @rcov_path ||= 'rcov'
121
- @rspec_path ||= 'rspec'
122
- @pattern ||= './spec{,/*/**}/*_spec.rb'
133
+ @rcov_path = 'rcov'
134
+ @rspec_path = 'rspec'
135
+ @pattern = './spec{,/*/**}/*_spec.rb'
136
+ end
123
137
 
124
- desc("Run RSpec code examples") unless ::Rake.application.last_comment
138
+ def has_files?
139
+ empty = files_to_run.empty?
140
+ puts "No examples matching #{pattern} could be found" if empty
141
+ not empty
142
+ end
125
143
 
126
- task name do
127
- RakeFileUtils.send(:verbose, verbose) do
128
- if files_to_run.empty?
129
- puts "No examples matching #{pattern} could be found"
130
- else
131
- begin
132
- puts spec_command if verbose
133
- success = system(spec_command)
134
- rescue
135
- puts failure_message if failure_message
136
- end
137
- raise("#{spec_command} failed") if fail_on_error unless success
138
- end
144
+ def run_task(verbose)
145
+ files = has_files?
146
+ if files
147
+ command = spec_command
148
+ begin
149
+ puts command if verbose
150
+ success = system(command)
151
+ rescue
152
+ puts failure_message if failure_message
139
153
  end
154
+ raise("#{command} failed") if fail_on_error unless success
140
155
  end
141
156
  end
142
157
 
@@ -144,29 +159,25 @@ module RSpec
144
159
 
145
160
  def files_to_run
146
161
  if ENV['SPEC']
147
- FileList[ ENV['SPEC'] ]
162
+ FileList[ ENV['SPEC'] ].sort
148
163
  else
149
- FileList[ pattern ].map { |f| f.gsub(/"/, '\"').gsub(/'/, "\\\\'") }
164
+ FileList[ pattern ].sort.map { |f| f.shellescape }
150
165
  end
151
166
  end
152
167
 
153
168
  def spec_command
154
- @spec_command ||= begin
155
- cmd_parts = []
156
- cmd_parts << RUBY
157
- cmd_parts << ruby_opts
158
- cmd_parts << "-w" if @warning
159
- cmd_parts << "-S" << runner
160
- cmd_parts << "-Ispec:lib" << rcov_opts if rcov
161
- cmd_parts << files_to_run
162
- cmd_parts << "--" if rcov && rspec_opts
163
- cmd_parts << rspec_opts
164
- cmd_parts.flatten.reject(&blank).join(" ")
165
- end
169
+ cmd_parts = []
170
+ cmd_parts << RUBY
171
+ cmd_parts << ruby_opts
172
+ cmd_parts << "-w" if @warning
173
+ cmd_parts << "-S" << runner
174
+ cmd_parts << "-Ispec:lib" << rcov_opts if rcov
175
+ cmd_parts << files_to_run
176
+ cmd_parts << "--" if rcov && rspec_opts
177
+ cmd_parts << rspec_opts
178
+ cmd_parts.flatten.reject(&blank).join(" ")
166
179
  end
167
180
 
168
- private
169
-
170
181
  def runner
171
182
  rcov ? rcov_path : rspec_path
172
183
  end
@@ -38,7 +38,7 @@ module RSpec::Core
38
38
  end
39
39
 
40
40
  def start(expected_example_count)
41
- @start = Time.now
41
+ @start = RSpec::Core::Time.now
42
42
  notify :start, expected_example_count
43
43
  end
44
44
 
@@ -89,7 +89,7 @@ module RSpec::Core
89
89
  alias_method :abort, :finish
90
90
 
91
91
  def stop
92
- @duration = Time.now - @start if @start
92
+ @duration = (RSpec::Core::Time.now - @start).to_f if @start
93
93
  notify :stop
94
94
  end
95
95
 
@@ -29,68 +29,116 @@ module RSpec
29
29
  # @see ExampleGroup.include_examples
30
30
  # @see ExampleGroup.include_context
31
31
  def shared_examples *args, &block
32
- if key? args.first
33
- key = args.shift
34
- raise_key_taken key if key_taken? key
35
- RSpec.world.shared_example_groups[key] = block
36
- end
37
-
38
- unless args.empty?
39
- mod = Module.new
40
- (class << mod; self; end).send :define_method, :extended do |host|
41
- host.class_eval(&block)
42
- end
43
- RSpec.configuration.extend mod, *args
44
- end
32
+ Registry.add_group(*args, &block)
45
33
  end
46
34
 
47
35
  alias_method :shared_context, :shared_examples
48
36
  alias_method :share_examples_for, :shared_examples
49
37
  alias_method :shared_examples_for, :shared_examples
50
38
 
39
+ # @deprecated
51
40
  def share_as(name, &block)
52
- if Object.const_defined?(name)
53
- mod = Object.const_get(name)
54
- raise_name_error unless mod.created_from_caller(caller)
55
- end
41
+ RSpec.deprecate("Rspec::Core::SharedExampleGroup#share_as",
42
+ "RSpec::SharedContext or shared_examples")
43
+ Registry.add_const(name, &block)
44
+ end
45
+
46
+ # @private
47
+ #
48
+ # Used internally to manage the shared example groups and
49
+ # constants. We want to limit the number of methods we add
50
+ # to objects we don't own (main and Module) so this allows
51
+ # us to have helper methods that don't get added to those
52
+ # objects.
53
+ module Registry
54
+ extend self
56
55
 
57
- mod = Module.new do
58
- @shared_block = block
59
- @caller_line = caller.last
56
+ def add_group(*args, &block)
57
+ ensure_block_has_source_location(block, caller[1])
60
58
 
61
- def self.created_from_caller(other_caller)
62
- @caller_line == other_caller.last
59
+ if key? args.first
60
+ key = args.shift
61
+ warn_if_key_taken key, block
62
+ RSpec.world.shared_example_groups[key] = block
63
63
  end
64
64
 
65
- def self.included(kls)
66
- kls.describe(&@shared_block)
67
- kls.children.first.metadata[:shared_group_name] = name
65
+ unless args.empty?
66
+ mod = Module.new
67
+ (class << mod; self; end).send :define_method, :extended do |host|
68
+ host.class_eval(&block)
69
+ end
70
+ RSpec.configuration.extend mod, *args
68
71
  end
69
72
  end
70
73
 
71
- shared_const = Object.const_set(name, mod)
72
- RSpec.world.shared_example_groups[shared_const] = block
73
- end
74
+ def add_const(name, &block)
75
+ if Object.const_defined?(name)
76
+ mod = Object.const_get(name)
77
+ raise_name_error unless mod.created_from_caller(caller)
78
+ end
74
79
 
75
- private
80
+ mod = Module.new do
81
+ @shared_block = block
82
+ @caller_line = caller.last
76
83
 
77
- def key? candidate
78
- [String, Symbol, Module].any? { |cls| cls === candidate }
79
- end
84
+ def self.created_from_caller(other_caller)
85
+ @caller_line == other_caller.last
86
+ end
80
87
 
81
- def raise_name_error
82
- raise NameError, "The first argument (#{name}) to share_as must be a legal name for a constant not already in use."
83
- end
88
+ def self.included(kls)
89
+ kls.describe(&@shared_block)
90
+ kls.children.first.metadata[:shared_group_name] = name
91
+ end
92
+ end
84
93
 
85
- def raise_key_taken key
86
- raise ArgumentError, "Shared example group '#{key}' already exists"
87
- end
94
+ shared_const = Object.const_set(name, mod)
95
+ RSpec.world.shared_example_groups[shared_const] = block
96
+ end
88
97
 
89
- def key_taken? key
90
- RSpec.world.shared_example_groups.has_key?(key)
98
+ private
99
+
100
+ def key? candidate
101
+ [String, Symbol, Module].any? { |cls| cls === candidate }
102
+ end
103
+
104
+ def raise_name_error
105
+ raise NameError, "The first argument (#{name}) to share_as must be a legal name for a constant not already in use."
106
+ end
107
+
108
+ def warn_if_key_taken key, new_block
109
+ return unless existing_block = example_block_for(key)
110
+
111
+ Kernel.warn <<-WARNING.gsub(/^ +\|/, '')
112
+ |WARNING: Shared example group '#{key}' has been previously defined at:
113
+ | #{formatted_location existing_block}
114
+ |...and you are now defining it at:
115
+ | #{formatted_location new_block}
116
+ |The new definition will overwrite the original one.
117
+ WARNING
118
+ end
119
+
120
+ def formatted_location block
121
+ block.source_location.join ":"
122
+ end
123
+
124
+ def example_block_for key
125
+ RSpec.world.shared_example_groups[key]
126
+ end
127
+
128
+ def ensure_block_has_source_location(block, caller_line)
129
+ return if block.respond_to?(:source_location)
130
+
131
+ block.extend Module.new {
132
+ define_method :source_location do
133
+ caller_line.split(':')
134
+ end
135
+ }
136
+ end
91
137
  end
92
138
  end
93
139
  end
94
140
  end
95
141
 
96
- include RSpec::Core::SharedExampleGroup
142
+ extend RSpec::Core::SharedExampleGroup
143
+ Module.send(:include, RSpec::Core::SharedExampleGroup)
144
+