transpec 0.0.1

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rubocop.yml +13 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +9 -0
  6. data/Guardfile +14 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +37 -0
  9. data/Rakefile +27 -0
  10. data/bin/transpec +8 -0
  11. data/lib/transpec/ast/scanner.rb +51 -0
  12. data/lib/transpec/ast/scope_stack.rb +76 -0
  13. data/lib/transpec/cli.rb +162 -0
  14. data/lib/transpec/configuration.rb +40 -0
  15. data/lib/transpec/git.rb +24 -0
  16. data/lib/transpec/rewriter.rb +109 -0
  17. data/lib/transpec/syntax/double.rb +21 -0
  18. data/lib/transpec/syntax/matcher.rb +60 -0
  19. data/lib/transpec/syntax/method_stub.rb +142 -0
  20. data/lib/transpec/syntax/send_node_syntax.rb +39 -0
  21. data/lib/transpec/syntax/should.rb +49 -0
  22. data/lib/transpec/syntax/should_receive.rb +120 -0
  23. data/lib/transpec/syntax.rb +58 -0
  24. data/lib/transpec/util.rb +50 -0
  25. data/lib/transpec/version.rb +14 -0
  26. data/lib/transpec.rb +17 -0
  27. data/spec/.rubocop.yml +19 -0
  28. data/spec/spec_helper.rb +33 -0
  29. data/spec/spec_spec.rb +54 -0
  30. data/spec/support/file_helper.rb +25 -0
  31. data/spec/support/shared_context.rb +63 -0
  32. data/spec/transpec/ast/scanner_spec.rb +177 -0
  33. data/spec/transpec/ast/scope_stack_spec.rb +94 -0
  34. data/spec/transpec/cli_spec.rb +290 -0
  35. data/spec/transpec/configuration_spec.rb +52 -0
  36. data/spec/transpec/git_spec.rb +85 -0
  37. data/spec/transpec/rewriter_spec.rb +203 -0
  38. data/spec/transpec/syntax/double_spec.rb +88 -0
  39. data/spec/transpec/syntax/matcher_spec.rb +407 -0
  40. data/spec/transpec/syntax/method_stub_spec.rb +386 -0
  41. data/spec/transpec/syntax/should_receive_spec.rb +286 -0
  42. data/spec/transpec/syntax/should_spec.rb +262 -0
  43. data/spec/transpec/util_spec.rb +48 -0
  44. data/transpec.gemspec +32 -0
  45. metadata +233 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c98c4880892b0c69338d5a58b145fb47e6efbc14
4
+ data.tar.gz: 27d7e201e95c233d9e13887a9d22642e99034952
5
+ SHA512:
6
+ metadata.gz: 265a268cf2858c97c587c561c58cb0b129951d6f8b69acec44ac652457ad9eb11ff01e25d506914f34661239e792c37b0e9443448a92f5587a219b43af37e4cd
7
+ data.tar.gz: 901f0dcc5290387aec5377f3514c61134199351000621520090d35aecd46be78063239a47c66f7ca3cd414ef6182f7aba845b067761cce0ee6774e999bc4aef3
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/bundle
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+
2
+ LineLength:
3
+ Max: 100
4
+
5
+ MethodLength:
6
+ Max: 20
7
+
8
+ WordArray:
9
+ Enabled: false
10
+
11
+ # TODO
12
+ Documentation:
13
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode
6
+ script: bundle exec rake all
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'coveralls', '~> 0.6'
7
+ gem 'simplecov-rcov', '~> 0.2'
8
+ gem 'ci_reporter', '~> 1.8'
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, all_after_pass: true, all_on_start: true, keep_failed: true do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ watch(%r{^spec/support/.+\.rb$}) { "spec" }
9
+ end
10
+
11
+ guard :rubocop do
12
+ watch(%r{.+\.rb$})
13
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Yuji Nakayama
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Transpec
2
+
3
+ **Transpec** automatically converts your specs into latest [RSpec](http://rspec.info/) syntax with static analysis.
4
+
5
+ See the following pages for new RSpec syntax:
6
+
7
+ * [Myron Marston » RSpec's New Expectation Syntax](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)
8
+ * [RSpec's new message expectation syntax - Tea is awesome.](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)
9
+ * [Myron Marston » The Plan for RSpec 3](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ $ gem install transpec
15
+ ```
16
+
17
+ ## Basic Usage
18
+
19
+ Run `transpec` with no arguments in your project directory:
20
+
21
+ ```bash
22
+ $ transpec
23
+ ```
24
+
25
+ This will inspect and overwrite all spec files in the `spec` directory.
26
+
27
+ For more information, please see the help with `--help` option.
28
+
29
+ **TODO:** Add more description
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ namespace :ci do
7
+ desc "#{Rake::Task['spec'].comment} for CI environment"
8
+ task :spec do
9
+ ENV['CI'] = 'true'
10
+
11
+ ENV['CI_REPORTS'] = 'spec/reports'
12
+ require 'ci/reporter/rake/rspec'
13
+ Rake::Task['ci:setup:rspec'].invoke
14
+
15
+ Rake::Task['spec'].invoke
16
+ end
17
+ end
18
+
19
+ desc 'Check code style with RuboCop'
20
+ task :style do
21
+ sh('rubocop')
22
+ end
23
+
24
+ desc 'Run RSpec and RuboCop'
25
+ task all: [:spec, :style]
26
+
27
+ task default: :all
data/bin/transpec ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
+ require 'transpec'
6
+
7
+ result = Transpec::CLI.run
8
+ exit(result ? 0 : 1)
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+
3
+ module Transpec
4
+ module AST
5
+ class Scanner
6
+ SCOPE_TYPES = [:module, :class, :sclass, :def, :defs, :block].freeze
7
+
8
+ attr_reader :scope_stack
9
+
10
+ def self.scan(origin_node, &block)
11
+ instance = new(&block)
12
+ instance.scan(origin_node, true)
13
+ end
14
+
15
+ def initialize(&block)
16
+ @callback = block
17
+ @ancestor_nodes = []
18
+ @scope_stack = ScopeStack.new
19
+ end
20
+
21
+ def scan(origin_node, yield_origin_node = false)
22
+ return unless origin_node
23
+
24
+ yield_node(origin_node) if yield_origin_node
25
+
26
+ @ancestor_nodes.push(origin_node)
27
+ @scope_stack.push_scope(origin_node) if scope_node?(origin_node)
28
+
29
+ origin_node.children.each_with_index do |child, index|
30
+ next unless child.is_a?(Parser::AST::Node)
31
+ node = child
32
+ yield_node(node)
33
+ scan(node)
34
+ end
35
+
36
+ @scope_stack.pop_scope if scope_node?(origin_node)
37
+ @ancestor_nodes.pop
38
+ end
39
+
40
+ private
41
+
42
+ def yield_node(node)
43
+ @callback.call(node, @ancestor_nodes, @scope_stack.in_example_group_context?)
44
+ end
45
+
46
+ def scope_node?(node)
47
+ SCOPE_TYPES.include?(node.type)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+
3
+ module Transpec
4
+ module AST
5
+ class ScopeStack < Array
6
+ EXAMPLE_GROUP_METHOD_NAMES = [
7
+ :describe, :context,
8
+ :shared_examples, :shared_context, :share_examples_for, :shared_examples_for
9
+ ].freeze
10
+
11
+ def push_scope(node)
12
+ push(scope_type(node))
13
+ end
14
+
15
+ def pop_scope
16
+ pop
17
+ end
18
+
19
+ def in_example_group_context?
20
+ if include?(:example_group)
21
+ scopes_in_example_group = inner_scopes_in_scope(:example_group)
22
+ return false if include_class_scope?(scopes_in_example_group)
23
+ include_method_or_block_scope?(scopes_in_example_group)
24
+ elsif include?(:rspec_configure)
25
+ scopes_in_rspec_configure = inner_scopes_in_scope(:rspec_configure)
26
+ return false if include_class_scope?(scopes_in_rspec_configure)
27
+ include_method_or_block_scope?(scopes_in_rspec_configure)
28
+ elsif first == :def
29
+ scopes_in_method = self[1..-1]
30
+ !include_class_scope?(scopes_in_method)
31
+ elsif include?(:module)
32
+ scopes_in_module = inner_scopes_in_scope(:module)
33
+ return false if include_class_scope?(scopes_in_module)
34
+ scopes_in_module.include?(:def)
35
+ else
36
+ false
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def scope_type(node)
43
+ return node.type unless node.type == :block
44
+
45
+ send_node = node.children.first
46
+ receiver_node, method_name, *_ = *send_node
47
+
48
+ if Util.const_name(receiver_node) == 'RSpec' && method_name == :configure
49
+ :rspec_configure
50
+ elsif receiver_node
51
+ node.type
52
+ elsif EXAMPLE_GROUP_METHOD_NAMES.include?(method_name)
53
+ :example_group
54
+ else
55
+ node.type
56
+ end
57
+ end
58
+
59
+ def inner_scopes_in_scope(scope_type)
60
+ index = rindex(scope_type)
61
+ return nil unless index
62
+ self[Range.new(index + 1, -1)]
63
+ end
64
+
65
+ def include_class_scope?(scopes)
66
+ !(scopes & [:class, :sclass]).empty?
67
+ end
68
+
69
+ def include_method_or_block_scope?(scopes)
70
+ # TODO: Should validate whether the method taking the block is RSpec's
71
+ # special method. (e.g. #subject, #let, #before, #after)
72
+ !(scopes & [:def, :block]).empty?
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,162 @@
1
+ # coding: utf-8
2
+
3
+ require 'optparse'
4
+ require 'find'
5
+
6
+ module Transpec
7
+ class CLI
8
+ CONFIG_ATTRS_FOR_CLI_TYPES = {
9
+ expect_to_matcher: :convert_to_expect_to_matcher=,
10
+ expect_to_receive: :convert_to_expect_to_receive=,
11
+ allow_to_receive: :convert_to_allow_to_receive=,
12
+ deprecated: :replace_deprecated_method=
13
+ }
14
+
15
+ attr_reader :configuration, :forced
16
+ alias_method :forced?, :forced
17
+
18
+ def self.run(args = ARGV)
19
+ new.run(args)
20
+ end
21
+
22
+ def initialize
23
+ @configuration = Configuration.new
24
+ @forced = false
25
+ end
26
+
27
+ def run(args)
28
+ non_option_args = parse_options(args)
29
+
30
+ fail_if_should_not_continue!
31
+
32
+ paths = non_option_args
33
+
34
+ if paths.empty?
35
+ if Dir.exists?('spec')
36
+ paths = ['spec']
37
+ else
38
+ fail ArgumentError, 'Specify target files or directories.'
39
+ end
40
+ end
41
+
42
+ target_files(paths).each do |file_path|
43
+ puts "Processing #{file_path}"
44
+ rewriter = Rewriter.new(@configuration)
45
+ rewriter.rewrite_file!(file_path)
46
+ end
47
+
48
+ # TODO: Print summary
49
+
50
+ true
51
+ rescue => error
52
+ warn error.message
53
+ false
54
+ end
55
+
56
+ # rubocop:disable MethodLength
57
+ def parse_options(args)
58
+ parser = OptionParser.new
59
+ parser.banner = "Usage: transpec [options] [files or directories]\n\n"
60
+
61
+ parser.on(
62
+ '--force',
63
+ 'Force processing even if the current Git',
64
+ 'repository is not clean.'
65
+ ) do
66
+ @forced = true
67
+ end
68
+
69
+ parser.on(
70
+ '-d', '--disable TYPE[,TYPE...]',
71
+ 'Disable specific conversions.',
72
+ 'Available conversion types:',
73
+ ' expect_to_matcher (from `should`)',
74
+ ' expect_to_receive (from `should_receive`)',
75
+ ' allow_to_receive (from `stub`)',
76
+ ' deprecated (e.g. from `stub!` to `stub`)',
77
+ 'These are all enabled by default.'
78
+ ) do |types|
79
+ types.split(',').each do |type|
80
+ config_attr = CONFIG_ATTRS_FOR_CLI_TYPES[type.to_sym]
81
+ fail ArgumentError, "Unknown conversion type #{type.inspect}" unless config_attr
82
+ @configuration.send(config_attr, false)
83
+ end
84
+ end
85
+
86
+ parser.on(
87
+ '-n', '--negative-form FORM',
88
+ 'Specify negative form of `to` that is used',
89
+ 'in `expect(...).to` syntax.',
90
+ 'Either `not_to` or `to_not`.',
91
+ 'Default: not_to'
92
+ ) do |form|
93
+ @configuration.negative_form_of_to = form
94
+ end
95
+
96
+ parser.on(
97
+ '-p', '--no-parentheses-matcher-arg',
98
+ 'Suppress parenthesizing argument of matcher',
99
+ 'when converting operator to non-operator',
100
+ 'in `expect` syntax. Note that it will be',
101
+ 'parenthesized even if this option is',
102
+ 'specified when parentheses are necessary to',
103
+ 'keep the meaning of the expression.',
104
+ 'By default, arguments of the following',
105
+ 'operator matchers will be parenthesized.',
106
+ ' `== 10` to `eq(10)`',
107
+ ' `=~ /pattern/` to `match(/pattern/)`',
108
+ ' `=~ [1, 2]` to `match_array([1, 2])`'
109
+ ) do
110
+ @configuration.parenthesize_matcher_arg = false
111
+ end
112
+
113
+ parser.on('--version', 'Show Transpec version.') do
114
+ puts Version.to_s
115
+ exit
116
+ end
117
+
118
+ args = args.dup
119
+ parser.parse!(args)
120
+ args
121
+ end
122
+ # rubocop:enable MethodLength
123
+
124
+ def target_files(paths)
125
+ paths.reduce([]) do |file_paths, path|
126
+ if File.directory?(path)
127
+ file_paths.concat(ruby_files_in_directory(path))
128
+ elsif File.file?(path)
129
+ file_paths << path
130
+ elsif !File.exists?(path) # rubocop:disable FavorUnlessOverNegatedIf
131
+ fail ArgumentError, "No such file or directory #{path.inspect}"
132
+ end
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def ruby_files_in_directory(directory_path)
139
+ ruby_file_paths = []
140
+
141
+ Find.find(directory_path) do |path|
142
+ next unless File.file?(path)
143
+ next unless File.extname(path) == '.rb'
144
+ ruby_file_paths << path
145
+ end
146
+
147
+ ruby_file_paths
148
+ end
149
+
150
+ def fail_if_should_not_continue!
151
+ return if forced?
152
+
153
+ # TODO: Check each repository of target files / directories,
154
+ # not only the current working directory.
155
+ return unless Git.command_available?
156
+ return unless Git.inside_of_repository?
157
+ return if Git.clean?
158
+
159
+ fail 'The current Git repository is not clean. Aborting.'
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ module Transpec
4
+ class Configuration
5
+ NEGATIVE_FORMS_OF_TO = ['not_to', 'to_not'].freeze
6
+
7
+ PREDICATES = [
8
+ :convert_to_expect_to_matcher,
9
+ :convert_to_expect_to_receive,
10
+ :convert_to_allow_to_receive,
11
+ :replace_deprecated_method,
12
+ :parenthesize_matcher_arg
13
+ ].freeze
14
+
15
+ PREDICATES.each do |predicate|
16
+ attr_accessor predicate
17
+ alias_method predicate.to_s + '?', predicate
18
+ end
19
+
20
+ attr_accessor :negative_form_of_to
21
+
22
+ def initialize
23
+ PREDICATES.each do |predicate|
24
+ instance_variable_set('@' + predicate.to_s, true)
25
+ end
26
+
27
+ self.negative_form_of_to = 'not_to'
28
+ end
29
+
30
+ def negative_form_of_to=(form)
31
+ unless NEGATIVE_FORMS_OF_TO.include?(form.to_s)
32
+ message = 'Negative form of "to" must be either '
33
+ message << NEGATIVE_FORMS_OF_TO.map(&:inspect).join(' or ')
34
+ fail ArgumentError, message
35
+ end
36
+
37
+ @negative_form_of_to = form.to_s.freeze
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+
3
+ module Transpec
4
+ module Git
5
+ GIT = 'git'
6
+
7
+ module_function
8
+
9
+ def command_available?
10
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
11
+ git_path = File.join(path, GIT)
12
+ File.exists?(git_path)
13
+ end
14
+ end
15
+
16
+ def inside_of_repository?
17
+ system("#{GIT} rev-parse --is-inside-work-tree > /dev/null 2> /dev/null")
18
+ end
19
+
20
+ def clean?
21
+ `#{GIT} status --porcelain`.empty?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,109 @@
1
+ # coding: utf-8
2
+
3
+ require 'parser'
4
+ require 'parser/current'
5
+
6
+ module Transpec
7
+ class Rewriter
8
+ def initialize(configuration = Configuration.new)
9
+ @configuration = configuration
10
+ end
11
+
12
+ def rewrite_file!(file_path)
13
+ source = File.read(file_path)
14
+ rewritten_source = rewrite(source, file_path)
15
+ File.write(file_path, rewritten_source)
16
+ end
17
+
18
+ def rewrite(source, name = '(string)')
19
+ source_buffer = create_source_buffer(source, name)
20
+ ast = parse(source_buffer)
21
+
22
+ @source_rewriter = Parser::Source::Rewriter.new(source_buffer)
23
+ failed_overlapping_rewrite = false
24
+ @source_rewriter.diagnostics.consumer = proc { failed_overlapping_rewrite = true }
25
+
26
+ AST::Scanner.scan(ast) do |node, ancestor_nodes, in_example_group_context|
27
+ dispatch_node(node, ancestor_nodes, in_example_group_context)
28
+ end
29
+
30
+ rewritten_source = @source_rewriter.process
31
+
32
+ if failed_overlapping_rewrite
33
+ rewriter = self.class.new(@configuration)
34
+ rewritten_source = rewriter.rewrite(rewritten_source, name)
35
+ end
36
+
37
+ rewritten_source
38
+ end
39
+
40
+ def create_source_buffer(source, name)
41
+ source_buffer = Parser::Source::Buffer.new(name)
42
+ source_buffer.source = source
43
+ source_buffer
44
+ end
45
+
46
+ def parse(source_buffer)
47
+ parser = Parser::CurrentRuby.new
48
+ ast = parser.parse(source_buffer)
49
+ ast
50
+ end
51
+
52
+ def dispatch_node(node, ancestor_nodes, in_example_group_context)
53
+ Syntax.all.each do |syntax_class|
54
+ next unless syntax_class.target_node?(node)
55
+
56
+ syntax = syntax_class.new(
57
+ node,
58
+ ancestor_nodes,
59
+ in_example_group_context,
60
+ @source_rewriter
61
+ )
62
+
63
+ handler_name = "process_#{syntax_class.snake_case_name}"
64
+ send(handler_name, syntax)
65
+
66
+ break
67
+ end
68
+ rescue Syntax::NotInExampleGroupContextError => error
69
+ warn_not_in_example_group_context_error(error)
70
+ end
71
+
72
+ def warn_not_in_example_group_context_error(error)
73
+ warn error.message
74
+ warn format(
75
+ '%s:%d:%s',
76
+ error.source_buffer.name,
77
+ error.source_range.line,
78
+ error.source_range.source_line
79
+ )
80
+ end
81
+
82
+ def process_should(should)
83
+ if @configuration.convert_to_expect_to_matcher?
84
+ should.expectize!(
85
+ @configuration.negative_form_of_to,
86
+ @configuration.parenthesize_matcher_arg?
87
+ )
88
+ end
89
+ end
90
+
91
+ def process_should_receive(should_receive)
92
+ if @configuration.convert_to_expect_to_receive?
93
+ should_receive.expectize!(@configuration.negative_form_of_to)
94
+ end
95
+ end
96
+
97
+ def process_double(double)
98
+ double.replace_deprecated_method! if @configuration.replace_deprecated_method?
99
+ end
100
+
101
+ def process_method_stub(method_stub)
102
+ if @configuration.convert_to_allow_to_receive?
103
+ method_stub.allowize!
104
+ elsif @configuration.replace_deprecated_method?
105
+ method_stub.replace_deprecated_method!
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+
3
+ module Transpec
4
+ class Syntax
5
+ class Double < Syntax
6
+ include SendNodeSyntax
7
+
8
+ def self.target_node?(node)
9
+ return false unless node.type == :send
10
+ receiver_node, method_name, *_ = *node
11
+ return false if receiver_node
12
+ [:double, :mock, :stub].include?(method_name)
13
+ end
14
+
15
+ def replace_deprecated_method!
16
+ return if method_name == :double
17
+ @source_rewriter.replace(selector_range, 'double')
18
+ end
19
+ end
20
+ end
21
+ end