rspec-core 2.11.1 → 2.12.0

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 (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
+