transpec 1.10.4 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
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?