transpec 0.0.1

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