transpec 1.10.4 → 1.11.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -3
  3. data/CHANGELOG.md +9 -0
  4. data/README.md +96 -18
  5. data/README.md.erb +117 -51
  6. data/lib/transpec/base_rewriter.rb +25 -28
  7. data/lib/transpec/cli.rb +27 -14
  8. data/lib/transpec/configuration.rb +2 -1
  9. data/lib/transpec/converter.rb +54 -32
  10. data/lib/transpec/dynamic_analyzer/helper.rb.erb +44 -0
  11. data/lib/transpec/dynamic_analyzer/node_util.rb +17 -0
  12. data/lib/transpec/dynamic_analyzer/rewriter.rb +12 -4
  13. data/lib/transpec/dynamic_analyzer/runtime_data.rb +3 -4
  14. data/lib/transpec/dynamic_analyzer.rb +14 -52
  15. data/lib/transpec/option_parser.rb +89 -81
  16. data/lib/transpec/processed_source.rb +48 -0
  17. data/lib/transpec/record.rb +2 -2
  18. data/lib/transpec/report.rb +5 -1
  19. data/lib/transpec/rspec_dsl.rb +12 -2
  20. data/lib/transpec/rspec_version.rb +8 -7
  21. data/lib/transpec/spec_suite.rb +114 -0
  22. data/lib/transpec/syntax/example.rb +4 -20
  23. data/lib/transpec/syntax/example_group.rb +38 -0
  24. data/lib/transpec/syntax/have.rb +6 -9
  25. data/lib/transpec/syntax/its.rb +5 -7
  26. data/lib/transpec/syntax/method_stub.rb +12 -13
  27. data/lib/transpec/syntax/mixin/any_instance_block.rb +14 -6
  28. data/lib/transpec/syntax/mixin/context_sensitive.rb +41 -0
  29. data/lib/transpec/syntax/mixin/matcher_owner.rb +16 -26
  30. data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +1 -1
  31. data/lib/transpec/syntax/mixin/no_message_allowance.rb +2 -2
  32. data/lib/transpec/syntax/mixin/useless_and_return.rb +2 -2
  33. data/lib/transpec/syntax/oneliner_should.rb +9 -13
  34. data/lib/transpec/syntax/operator.rb +3 -7
  35. data/lib/transpec/syntax/pending.rb +4 -20
  36. data/lib/transpec/syntax/rspec_configure/configuration_modification.rb +86 -0
  37. data/lib/transpec/syntax/rspec_configure/framework.rb +45 -86
  38. data/lib/transpec/syntax/rspec_configure.rb +18 -6
  39. data/lib/transpec/syntax/should.rb +3 -5
  40. data/lib/transpec/syntax/should_receive.rb +1 -1
  41. data/lib/transpec/syntax.rb +8 -0
  42. data/lib/transpec/util.rb +0 -8
  43. data/lib/transpec/version.rb +2 -2
  44. data/spec/acceptance/configuration_modification_spec.rb +132 -0
  45. data/spec/acceptance/conversion_spec.rb +114 -0
  46. data/spec/support/shared_context.rb +6 -12
  47. data/spec/transpec/cli_spec.rb +21 -23
  48. data/spec/transpec/configuration_spec.rb +2 -1
  49. data/spec/transpec/converter_spec.rb +292 -213
  50. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +3 -3
  51. data/spec/transpec/dynamic_analyzer_spec.rb +8 -2
  52. data/spec/transpec/option_parser_spec.rb +42 -4
  53. data/spec/transpec/processed_source_spec.rb +67 -0
  54. data/spec/transpec/rspec_version_spec.rb +8 -2
  55. data/spec/transpec/spec_suite_spec.rb +107 -0
  56. data/spec/transpec/syntax/allow_spec.rb +9 -27
  57. data/spec/transpec/syntax/example_group_spec.rb +172 -0
  58. data/spec/transpec/syntax/expect_spec.rb +18 -54
  59. data/spec/transpec/syntax/have_spec.rb +35 -14
  60. data/spec/transpec/syntax/its_spec.rb +27 -7
  61. data/spec/transpec/syntax/method_stub_spec.rb +31 -8
  62. data/spec/transpec/syntax/oneliner_should_spec.rb +22 -131
  63. data/spec/transpec/syntax/rspec_configure_spec.rb +118 -15
  64. data/spec/transpec/syntax/should_spec.rb +51 -82
  65. data/tasks/fixtures/guard/COMMIT_EDITMSG +80 -0
  66. data/tasks/fixtures/mail/COMMIT_EDITMSG +84 -0
  67. data/tasks/fixtures/twitter/COMMIT_EDITMSG +36 -0
  68. data/tasks/lib/transpec_test.rb +23 -2
  69. data/tasks/readme.rake +35 -0
  70. metadata +22 -4
  71. data/lib/transpec/parser.rb +0 -9
@@ -7,10 +7,8 @@ require 'optparse'
7
7
  require 'rainbow'
8
8
  require 'rainbow/ext/string' unless String.respond_to?(:color)
9
9
 
10
- # rubocop:disable ClassLength
11
-
12
10
  module Transpec
13
- class OptionParser
11
+ class OptionParser # rubocop:disable ClassLength
14
12
  CONFIG_ATTRS_FOR_KEEP_TYPES = {
15
13
  should: :convert_should=,
16
14
  oneliner: :convert_oneliner=,
@@ -22,6 +20,11 @@ module Transpec
22
20
  deprecated: :convert_deprecated_method=
23
21
  }
24
22
 
23
+ CONFIG_ATTRS_FOR_CONVERT_TYPES = {
24
+ stub_with_hash: :convert_stub_with_hash_to_allow_to_receive_and_return=,
25
+ example_group: :convert_example_group=
26
+ }
27
+
25
28
  VALID_BOOLEAN_MATCHER_TYPES = %w(truthy,falsey truthy,falsy true,false)
26
29
 
27
30
  attr_reader :configuration
@@ -36,7 +39,7 @@ module Transpec
36
39
  end
37
40
 
38
41
  def parse(args)
39
- args = exclude_deprecated_options(args)
42
+ args = convert_deprecated_options(args)
40
43
  @parser.parse!(args)
41
44
  args
42
45
  end
@@ -47,28 +50,27 @@ module Transpec
47
50
 
48
51
  private
49
52
 
50
- # rubocop:disable MethodLength
51
- def setup_parser
53
+ def setup_parser # rubocop:disable MethodLength
52
54
  @parser = create_parser
53
55
 
54
56
  define_option('-f', '--force') do
55
57
  configuration.forced = true
56
58
  end
57
59
 
58
- define_option('-s', '--skip-dynamic-analysis') do
59
- configuration.skip_dynamic_analysis = true
60
- end
61
-
62
60
  define_option('-c', '--rspec-command COMMAND') do |command|
63
61
  configuration.rspec_command = command
64
62
  end
65
63
 
66
64
  define_option('-k', '--keep TYPE[,TYPE...]') do |types|
67
- types.split(',').each do |type|
68
- config_attr = CONFIG_ATTRS_FOR_KEEP_TYPES[type.to_sym]
69
- fail ArgumentError, "Unknown syntax type #{type.inspect}" unless config_attr
70
- configuration.send(config_attr, false)
71
- end
65
+ configure_conversion(CONFIG_ATTRS_FOR_KEEP_TYPES, types, false)
66
+ end
67
+
68
+ define_option('-v', '--convert TYPE[,TYPE...]') do |types|
69
+ configure_conversion(CONFIG_ATTRS_FOR_CONVERT_TYPES, types, true)
70
+ end
71
+
72
+ define_option('-s', '--skip-dynamic-analysis') do
73
+ configuration.skip_dynamic_analysis = true
72
74
  end
73
75
 
74
76
  define_option('-n', '--negative-form FORM') do |form|
@@ -88,10 +90,6 @@ module Transpec
88
90
  configuration.add_receiver_arg_to_any_instance_implementation_block = false
89
91
  end
90
92
 
91
- define_option('-t', '--convert-stub-with-hash') do
92
- configuration.convert_stub_with_hash_to_stub_and_return = true
93
- end
94
-
95
93
  define_option('-p', '--no-parentheses-matcher-arg') do
96
94
  configuration.parenthesize_matcher_arg = false
97
95
  end
@@ -105,7 +103,6 @@ module Transpec
105
103
  exit
106
104
  end
107
105
  end
108
- # rubocop:enable MethodLength
109
106
 
110
107
  def create_parser
111
108
  banner = "Usage: transpec [options] [files or directories]\n\n"
@@ -116,81 +113,72 @@ module Transpec
116
113
 
117
114
  def define_option(*options, &block)
118
115
  description_lines = descriptions[options.first]
116
+ description_lines = description_lines.map { |line| highlight_text(line) }
119
117
  @parser.on(*options, *description_lines, &block)
120
118
  end
121
119
 
122
- # rubocop:disable MethodLength, AlignHash
123
- def descriptions
120
+ # rubocop:disable AlignHash
121
+ def descriptions # rubocop:disable MethodLength
124
122
  @descriptions ||= {
125
123
  '-f' => [
126
- 'Force processing even if the current Git',
127
- 'repository is not clean.'
124
+ 'Force processing even if the current Git repository is not clean.'
128
125
  ],
129
126
  '-s' => [
130
- 'Skip dynamic analysis and convert with only',
131
- 'static analysis. Note that specifying this',
132
- 'option decreases the conversion accuracy.'
127
+ 'Skip dynamic analysis and convert with only static analysis. Note',
128
+ 'that specifying this option decreases the conversion accuracy.'
133
129
  ],
134
130
  '-c' => [
135
- 'Specify a command to run your specs that is',
136
- 'used for dynamic analysis.',
131
+ 'Specify a command to run your specs that is used for dynamic',
132
+ 'analysis.',
137
133
  'Default: "bundle exec rspec"'
138
134
  ],
139
135
  '-k' => [
140
- 'Keep specific syntaxes by disabling',
141
- 'conversions.',
142
- 'Available syntax types:',
143
- " #{'should'.bright} (to #{'expect(obj).to'.underline})",
144
- " #{'oneliner'.bright} (from #{'should'.underline} to #{'is_expected.to'.underline})",
145
- " #{'should_receive'.bright} (to #{'expect(obj).to receive'.underline})",
146
- " #{'stub'.bright} (to #{'allow(obj).to receive'.underline})",
147
- " #{'have_items'.bright} (to #{'expect(obj.size).to eq(n)'.underline})",
148
- " #{'its'.bright} (to #{'describe { subject { }; it { } }'.underline})",
149
- " #{'pending'.bright} (to #{'skip'.underline})",
150
- " #{'deprecated'.bright} (e.g. from #{'mock'.underline} to #{'double'.underline})",
136
+ 'Keep specific syntaxes by disabling conversions.',
137
+ 'Conversion Types:',
138
+ ' *should* (to `expect(obj).to`)',
139
+ ' *oneliner* (from `it { should ... }` to `it { is_expected.to ... }`)',
140
+ ' *should_receive* (to `expect(obj).to receive`)',
141
+ ' *stub* (to `allow(obj).to receive`)',
142
+ ' *have_items* (to `expect(collection.size).to eq(n)`)',
143
+ " *its* (to `describe '#attr' { subject { }; it { } }`)",
144
+ ' *pending* (to `skip`)',
145
+ ' *deprecated* (all other deprecated syntaxes to latest syntaxes)',
151
146
  'These are all converted by default.'
152
147
  ],
148
+ '-v' => [
149
+ 'Enable specific conversions that are disabled by default.',
150
+ 'Conversion Types:',
151
+ ' *stub_with_hash* (`obj.stub(:msg => val)` to',
152
+ ' `allow(obj).to receive(:msg).and_return(val)`)',
153
+ 'These conversions are disabled by default.'
154
+ ],
153
155
  '-n' => [
154
- "Specify a negative form of #{'to'.underline} that is used in",
155
- "the #{'expect(...).to'.underline} syntax.",
156
- "Either #{'not_to'.bright} or #{'to_not'.bright}.",
157
- "Default: #{'not_to'.bright}"
156
+ 'Specify a negative form of `to` that is used in the `expect(...).to`',
157
+ 'syntax. Either *not_to* or *to_not*.',
158
+ 'Default: *not_to*'
158
159
  ],
159
160
  '-b' => [
160
- "Specify a matcher type that #{'be_true'.underline} and",
161
- "#{'be_false'.underline} will be converted to.",
162
- " #{'truthy,falsey'.bright} (conditional semantics)",
163
- " #{'truthy,falsy'.bright} (alias of #{'falsey'.underline})",
164
- " #{'true,false'.bright} (exact equality)",
165
- "Default: #{'truthy,falsey'.bright}"
161
+ 'Specify a matcher type that `be_true` and `be_false` will be',
162
+ 'converted to.',
163
+ ' *truthy,falsey* (conditional semantics)',
164
+ ' *truthy,falsy* (alias of `falsey`)',
165
+ ' *true,false* (exact equality)',
166
+ 'Default: *truthy,falsey*'
166
167
  ],
167
168
  '-a' => [
168
- 'Suppress yielding receiver instances to',
169
- "#{'any_instance'.underline} implementation blocks as the",
170
- 'first block argument.'
171
- ],
172
- '-t' => [
173
- "Enable conversion of #{'obj.stub(:msg => val)'.underline} to",
174
- "#{'allow(obj).to receive(:msg).and_return(val)'.underline}",
175
- "when #{'receive_messages(:msg => val)'.underline} is",
176
- 'unavailable (prior to RSpec 3.0). It will be',
177
- 'converted to multiple statements if the hash',
178
- 'includes multiple pairs. This conversion is',
179
- 'disabled by default.'
169
+ 'Suppress yielding receiver instances to `any_instance`',
170
+ 'implementation blocks as the first block argument.'
180
171
  ],
181
172
  '-p' => [
182
- 'Suppress parenthesizing arguments of matchers',
183
- "when converting #{'should'.underline} with operator matcher",
184
- "to #{'expect'.underline} with non-operator matcher. Note",
185
- 'that it will be parenthesized even if this',
186
- 'option is specified when parentheses are',
187
- 'necessary to keep the meaning of the',
188
- 'expression. By default, arguments of the',
189
- 'following operator matchers will be',
190
- 'parenthesized.',
191
- " #{'== 10'.underline} to #{'eq(10)'.underline}",
192
- " #{'=~ /pattern/'.underline} to #{'match(/pattern/)'.underline}",
193
- " #{'=~ [1, 2]'.underline} to #{'match_array([1, 2])'.underline}"
173
+ 'Suppress parenthesizing arguments of matchers when converting',
174
+ '`should` with operator matcher to `expect` with non-operator matcher.',
175
+ 'Note that it will be parenthesized even if this option is',
176
+ 'specified when parentheses are necessary to keep the meaning of',
177
+ 'the expression. By default, arguments of the following operator',
178
+ 'matchers will be parenthesized.',
179
+ ' `== 10` to `eq(10)`',
180
+ ' `=~ /pattern/` to `match(/pattern/)`',
181
+ ' `=~ [1, 2]` to `match_array([1, 2])`'
194
182
  ],
195
183
  '--no-color' => [
196
184
  'Disable color in the output.'
@@ -200,19 +188,39 @@ module Transpec
200
188
  ]
201
189
  }
202
190
  end
203
- # rubocop:enable MethodLength, AlignHash
191
+ # rubocop:enable AlignHash
192
+
193
+ def highlight_text(text)
194
+ text.gsub(/`.+?`/) { |code| code.gsub('`', '').underline }
195
+ .gsub(/\*.+?\*/) { |code| code.gsub('*', '').bright }
196
+ end
204
197
 
205
- def exclude_deprecated_options(args)
206
- args.reject do |arg|
198
+ def convert_deprecated_options(raw_args)
199
+ raw_args.each_with_object([]) do |arg, args|
207
200
  case arg
208
201
  when '-m', '--generate-commit-message'
209
- warn 'DEPRECATION: The -m/--generate-commit-message option is deprecated. ' \
210
- 'Now Transpec always generates the commit message.'
211
- true
202
+ deprecate('-m/--generate-commit-message option')
203
+ when '-t', '--convert-stub-with-hash'
204
+ deprecate('-t/--convert-stub-with-hash', '`--convert stub_with_hash`')
205
+ args.concat(%w(--convert stub_with_hash))
212
206
  else
213
- false
207
+ args << arg
214
208
  end
215
209
  end
216
210
  end
211
+
212
+ def deprecate(subject, alternative = nil)
213
+ message = "DEPRECATION: #{subject} is deprecated."
214
+ message << " Please use #{alternative} instead." if alternative
215
+ warn message
216
+ end
217
+
218
+ def configure_conversion(type_to_attr_map, inputted_types, boolean)
219
+ inputted_types.split(',').each do |type|
220
+ config_attr = type_to_attr_map[type.to_sym]
221
+ fail ArgumentError, "Unknown syntax type #{type.inspect}" unless config_attr
222
+ configuration.send(config_attr, boolean)
223
+ end
224
+ end
217
225
  end
218
226
  end
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+
3
+ begin
4
+ require 'parser/current'
5
+ rescue NotImplementedError
6
+ warn 'Falling back to Ruby 2.1 parser.'
7
+ require 'parser/ruby21'
8
+ Parser::CurrentRuby = Parser::Ruby21 # rubocop:disable ConstantName
9
+ end
10
+
11
+ require 'transpec/ast/builder'
12
+
13
+ module Transpec
14
+ class ProcessedSource
15
+ attr_reader :buffer, :ast, :path, :syntax_error
16
+
17
+ def self.parse_file(path)
18
+ source = File.read(path)
19
+ parse(source, path)
20
+ end
21
+
22
+ def self.parse(source, path = nil)
23
+ buffer = Parser::Source::Buffer.new(path || '(string)')
24
+ buffer.source = source
25
+
26
+ builder = AST::Builder.new
27
+ parser = Parser::CurrentRuby.new(builder)
28
+
29
+ begin
30
+ ast = parser.parse(buffer)
31
+ new(buffer, ast, path)
32
+ rescue Parser::SyntaxError => error
33
+ new(buffer, nil, path, error)
34
+ end
35
+ end
36
+
37
+ def initialize(buffer, ast, path = nil, syntax_error = nil)
38
+ @buffer = buffer
39
+ @ast = ast
40
+ @path = path
41
+ @syntax_error = syntax_error
42
+ end
43
+
44
+ def to_s
45
+ buffer.source
46
+ end
47
+ end
48
+ end
@@ -4,7 +4,7 @@ require 'transpec/annotatable'
4
4
 
5
5
  module Transpec
6
6
  class Record
7
- OVERRIDDE_FORBIDDEN_METHODS = [
7
+ OVERRIDE_FORBIDDEN_METHODS = [
8
8
  :original_syntax,
9
9
  :original_syntax_type,
10
10
  :converted_syntax,
@@ -62,7 +62,7 @@ module Transpec
62
62
  end
63
63
 
64
64
  def self.method_added(method_name)
65
- return unless OVERRIDDE_FORBIDDEN_METHODS.include?(method_name)
65
+ return unless OVERRIDE_FORBIDDEN_METHODS.include?(method_name)
66
66
  fail "Do not override Record##{method_name}."
67
67
  end
68
68
  end
@@ -26,7 +26,11 @@ module Transpec
26
26
  record_counts[record] += 1
27
27
  end
28
28
 
29
- Hash[record_counts.sort_by { |record, count| -count }]
29
+ sorted_record_counts = record_counts.sort_by do |record, count|
30
+ [-count, record.original_syntax_type, record.converted_syntax_type]
31
+ end
32
+
33
+ Hash[sorted_record_counts]
30
34
  end
31
35
 
32
36
  def colored_summary(options = nil)
@@ -3,18 +3,28 @@
3
3
  # Aliases by Capybara:
4
4
  # https://github.com/jnicklas/capybara/blob/2.2.0/lib/capybara/rspec/features.rb
5
5
 
6
+ # rubocop:disable LineLength
7
+
6
8
  module Transpec
7
9
  module RSpecDSL
10
+ # https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/example_group.rb#L239-L265
11
+ # https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/shared_example_group.rb#L50-L61
8
12
  EXAMPLE_GROUP_METHODS = [
13
+ :example_group,
9
14
  :describe, :context,
15
+ :xdescribe, :xcontext,
16
+ :fdescribe, :fcontext,
10
17
  :shared_examples, :shared_context, :share_examples_for, :shared_examples_for,
11
18
  :feature # Capybara
12
19
  ].freeze
13
20
 
21
+ # https://github.com/rspec/rspec-core/blob/77cc21e/lib/rspec/core/example_group.rb#L130-L171
14
22
  EXAMPLE_METHODS = [
15
23
  :example, :it, :specify,
16
- :focus, :focused, :fit,
17
- :pending, :xexample, :xit, :xspecify,
24
+ :focus, :fexample, :fit, :fspecify,
25
+ :focused, # TODO: Support conversion
26
+ :xexample, :xit, :xspecify,
27
+ :skip, :pending,
18
28
  :scenario, :xscenario # Capybara
19
29
  ].freeze
20
30
 
@@ -41,14 +41,15 @@ module Transpec
41
41
  gem_version.to_s
42
42
  end
43
43
 
44
- define_feature :be_truthy, '2.99.0.beta1'
45
- define_feature :yielded_example, '2.99.0.beta1'
44
+ define_feature :be_truthy, '2.99.0.beta1'
45
+ define_feature :yielded_example, '2.99.0.beta1'
46
46
  define_feature :yielding_receiver_to_any_instance_implementation_block, '2.99.0.beta1'
47
- define_feature :oneliner_is_expected, '2.99.0.beta2', except: '3.0.0.beta1'
48
- define_feature :skip, '2.99.0.beta2', except: '3.0.0.beta1'
49
- define_feature :receive_messages, '3.0.0.beta1'
50
- define_feature :receive_message_chain, '3.0.0.beta2'
51
- define_feature :non_should_matcher_protocol, '3.0.0.beta2'
47
+ define_feature :oneliner_is_expected, '2.99.0.beta2', except: '3.0.0.beta1'
48
+ define_feature :skip, '2.99.0.beta2', except: '3.0.0.beta1'
49
+ define_feature :receive_messages, '3.0.0.beta1'
50
+ define_feature :receive_message_chain, '3.0.0.beta2'
51
+ define_feature :non_should_matcher_protocol, '3.0.0.beta2'
52
+ define_feature :non_monkey_patch_example_group, '3.0.0.beta2'
52
53
 
53
54
  RSPEC_2_99 = new('2.99.0.beta1')
54
55
  RSPEC_3_0 = new('3.0.0.beta1')
@@ -0,0 +1,114 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/file_finder'
4
+ require 'transpec/processed_source'
5
+ require 'transpec/syntax'
6
+
7
+ Transpec::Syntax.require_all
8
+
9
+ module Transpec
10
+ class SpecSuite
11
+ ANALYSIS_TARGET_CLASSES = [Syntax::Mixin::AnyInstanceBlock]
12
+
13
+ attr_reader :runtime_data
14
+
15
+ def initialize(base_paths = [], runtime_data = nil)
16
+ @base_paths = base_paths
17
+ @runtime_data = runtime_data
18
+ @analyzed = false
19
+ end
20
+
21
+ def specs
22
+ @specs ||= begin
23
+ FileFinder.find(@base_paths).map do |path|
24
+ ProcessedSource.parse_file(path)
25
+ end
26
+ end
27
+ end
28
+
29
+ def analyze
30
+ return if @analyzed
31
+
32
+ specs.each do |spec|
33
+ next unless spec.ast
34
+ spec.ast.each_node do |node|
35
+ dispatch_node(node)
36
+ end
37
+ end
38
+
39
+ @analyzed = true
40
+ end
41
+
42
+ def need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config?
43
+ analyze
44
+ @need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config
45
+ end
46
+
47
+ def main_rspec_configure_node?(node)
48
+ analyze
49
+
50
+ if @main_rspec_configure
51
+ @main_rspec_configure.node.equal?(node)
52
+ else
53
+ true
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def dispatch_node(node)
60
+ Syntax.standalone_syntaxes.each do |syntax_class|
61
+ syntax = syntax_class.new(node, nil, runtime_data)
62
+ next unless syntax.conversion_target?
63
+ dispatch_syntax(syntax)
64
+ break
65
+ end
66
+ end
67
+
68
+ def dispatch_syntax(syntax)
69
+ invoke_handler(syntax.class, syntax)
70
+
71
+ syntax_mixins.each do |mixin|
72
+ next unless syntax.class.ancestors.include?(mixin)
73
+ invoke_handler(mixin, syntax)
74
+ end
75
+
76
+ syntax.dependent_syntaxes.each do |dependent_syntax|
77
+ next unless dependent_syntax.conversion_target?
78
+ dispatch_syntax(dependent_syntax)
79
+ end
80
+ end
81
+
82
+ def syntax_mixins
83
+ Syntax::Mixin.constants.map do |const_name|
84
+ Syntax::Mixin.const_get(const_name, false)
85
+ end
86
+ end
87
+
88
+ def invoke_handler(klass, syntax)
89
+ class_name = klass.name.split('::').last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
90
+ handler_name = "process_#{class_name}"
91
+ send(handler_name, syntax) if respond_to?(handler_name, true)
92
+ end
93
+
94
+ def process_any_instance_block(syntax)
95
+ @need_to_modify_yield_receiver_to_any_instance_implementation_blocks_config ||=
96
+ syntax.need_to_add_receiver_arg_to_any_instance_implementation_block?
97
+ end
98
+
99
+ def process_rspec_configure(rspec_configure)
100
+ return unless runtime_data
101
+ run_order = runtime_data[rspec_configure.node, :run_order]
102
+ return unless run_order
103
+
104
+ unless @main_rspec_configure
105
+ @main_rspec_configure = rspec_configure
106
+ return
107
+ end
108
+
109
+ if run_order < runtime_data[@main_rspec_configure.node, :run_order]
110
+ @main_rspec_configure = rspec_configure
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,36 +1,20 @@
1
1
  # coding: utf-8
2
2
 
3
3
  require 'transpec/syntax'
4
- require 'transpec/syntax/mixin/send'
4
+ require 'transpec/syntax/mixin/context_sensitive'
5
5
  require 'transpec/rspec_dsl'
6
6
 
7
7
  module Transpec
8
8
  class Syntax
9
9
  class Example < Syntax
10
- include Mixin::Send, RSpecDSL
11
-
12
- define_dynamic_analysis do |rewriter|
13
- code = "is_a?(Class) && ancestors.any? { |a| a.name == 'RSpec::Core::ExampleGroup' }"
14
- rewriter.register_request(node, :example_group_context?, code, :context)
15
- end
10
+ include Mixin::ContextSensitive, RSpecDSL
16
11
 
17
12
  def dynamic_analysis_target?
18
13
  super && receiver_node.nil? && EXAMPLE_METHODS.include?(method_name)
19
14
  end
20
15
 
21
- def conversion_target?
22
- return false unless dynamic_analysis_target?
23
-
24
- # Check whether the context is example group to differenciate
25
- # RSpec::Core::ExampleGroup.pending (a relative of #it) and
26
- # RSpec::Core::ExampleGroup#pending (marks the example as pending in #it block).
27
- if runtime_data.run?(node)
28
- # If we have runtime data, check with it.
29
- runtime_data[node, :example_group_context?]
30
- else
31
- # Otherwise check statically.
32
- static_context_inspector.scopes.last == :example_group
33
- end
16
+ def should_be_in_example_group_context?
17
+ true
34
18
  end
35
19
 
36
20
  def convert_pending_to_skip!
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/syntax'
4
+ require 'transpec/syntax/mixin/context_sensitive'
5
+ require 'transpec/syntax/mixin/monkey_patch'
6
+ require 'transpec/rspec_dsl'
7
+
8
+ module Transpec
9
+ class Syntax
10
+ class ExampleGroup < Syntax
11
+ include Mixin::ContextSensitive, Mixin::MonkeyPatch, RSpecDSL
12
+
13
+ def dynamic_analysis_target?
14
+ super && receiver_node.nil? && EXAMPLE_GROUP_METHODS.include?(method_name)
15
+ end
16
+
17
+ def should_be_in_example_group_context?
18
+ false
19
+ end
20
+
21
+ def convert_to_non_monkey_patch!
22
+ insert_before(expression_range, 'RSpec.')
23
+ register_record
24
+ end
25
+
26
+ def register_record
27
+ original_syntax = method_name.to_s
28
+ converted_syntax = "RSpec.#{method_name}"
29
+
30
+ [original_syntax, converted_syntax].each do |syntax|
31
+ syntax << " 'something' { }"
32
+ end
33
+
34
+ @report.records << Record.new(original_syntax, converted_syntax)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -28,12 +28,13 @@ module Transpec
28
28
  [:have, :have_exactly, :have_at_least, :have_at_most].include?(method_name)
29
29
  end
30
30
 
31
- def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
32
- return if project_requires_collection_matcher?
31
+ def conversion_target?
32
+ super && !runtime_subject_data(:project_requires_collection_matcher?)
33
+ end
33
34
 
35
+ def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
34
36
  replace(expectation.subject_range, replacement_subject_source) if explicit_subject?
35
37
  replace(matcher_range, source_builder.replacement_matcher_source(parenthesize_matcher_arg))
36
-
37
38
  register_record if explicit_subject?
38
39
  end
39
40
 
@@ -56,10 +57,6 @@ module Transpec
56
57
  items_node.children[1]
57
58
  end
58
59
 
59
- def project_requires_collection_matcher?
60
- runtime_subject_data(:project_requires_collection_matcher?)
61
- end
62
-
63
60
  def collection_accessor
64
61
  if runtime_subject_data(:collection_accessor)
65
62
  runtime_subject_data(:collection_accessor).to_sym
@@ -70,7 +67,7 @@ module Transpec
70
67
 
71
68
  def subject_is_owner_of_collection?
72
69
  return true if items_method_has_arguments?
73
- !runtime_subject_data(:collection_accessor).nil?
70
+ runtime_subject_data(:collection_accessor)
74
71
  end
75
72
 
76
73
  def collection_accessor_is_private?
@@ -99,7 +96,7 @@ module Transpec
99
96
  end
100
97
 
101
98
  def accurate_conversion?
102
- !runtime_subject_data.nil?
99
+ runtime_subject_data
103
100
  end
104
101
 
105
102
  def matcher_range
@@ -19,9 +19,11 @@ module Transpec
19
19
  super && receiver_node.nil? && method_name == :its
20
20
  end
21
21
 
22
- def convert_to_describe_subject_it!
23
- return if project_requires_its?
22
+ def conversion_target?
23
+ super && !runtime_data[node, :project_requires_its?]
24
+ end
24
25
 
26
+ def convert_to_describe_subject_it!
25
27
  front, rear = build_wrapper_codes
26
28
 
27
29
  insert_before(beginning_of_line_range(block_node), front)
@@ -45,10 +47,6 @@ module Transpec
45
47
  node.parent_node
46
48
  end
47
49
 
48
- def project_requires_its?
49
- runtime_data[node, :project_requires_its?]
50
- end
51
-
52
50
  private
53
51
 
54
52
  def build_wrapper_codes
@@ -74,7 +72,7 @@ module Transpec
74
72
 
75
73
  def previous_line_is_blank?
76
74
  return false unless previous_line_source
77
- previous_line_source.empty? || !previous_line_source.match(/\A\s*\Z/).nil?
75
+ previous_line_source.empty? || previous_line_source.match(/\A\s*\Z/)
78
76
  end
79
77
 
80
78
  def previous_and_current_line_are_same_indentation_level?