transpec 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Guardfile +18 -14
  4. data/README.md +1 -1
  5. data/README.md.erb +1 -1
  6. data/lib/transpec/cli.rb +2 -2
  7. data/lib/transpec/context_error.rb +23 -0
  8. data/lib/transpec/converter.rb +5 -5
  9. data/lib/transpec/dynamic_analyzer/rewriter.rb +1 -2
  10. data/lib/transpec/report.rb +5 -5
  11. data/lib/transpec/rspec_version.rb +10 -7
  12. data/lib/transpec/syntax.rb +76 -49
  13. data/lib/transpec/syntax/expect.rb +2 -6
  14. data/lib/transpec/syntax/have.rb +1 -2
  15. data/lib/transpec/syntax/its.rb +1 -1
  16. data/lib/transpec/syntax/method_stub.rb +29 -3
  17. data/lib/transpec/syntax/mixin/any_instance.rb +19 -18
  18. data/lib/transpec/syntax/mixin/have_matcher_owner.rb +35 -0
  19. data/lib/transpec/syntax/mixin/send.rb +33 -33
  20. data/lib/transpec/syntax/mixin/should_base.rb +22 -8
  21. data/lib/transpec/syntax/oneliner_should.rb +2 -7
  22. data/lib/transpec/syntax/operator_matcher.rb +4 -3
  23. data/lib/transpec/syntax/should.rb +4 -9
  24. data/lib/transpec/syntax/should_receive.rb +3 -5
  25. data/lib/transpec/version.rb +1 -1
  26. data/spec/support/shared_context.rb +2 -3
  27. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +1 -1
  28. data/spec/transpec/report_spec.rb +1 -2
  29. data/spec/transpec/rspec_version_spec.rb +7 -1
  30. data/spec/transpec/syntax/double_spec.rb +4 -4
  31. data/spec/transpec/syntax/example_spec.rb +1 -1
  32. data/spec/transpec/syntax/method_stub_spec.rb +65 -11
  33. data/spec/transpec/syntax/should_receive_spec.rb +14 -14
  34. data/spec/transpec/syntax/should_spec.rb +10 -10
  35. data/transpec.gemspec +2 -2
  36. metadata +6 -5
  37. data/lib/transpec/syntax/mixin/have_matcher.rb +0 -23
@@ -13,7 +13,7 @@ module Transpec
13
13
  receiver_node.nil? && method_name == :its
14
14
  end
15
15
 
16
- def register_request_for_dynamic_analysis(rewriter)
16
+ add_dynamic_analysis_request do |rewriter|
17
17
  key = :project_requires_its?
18
18
  code = 'defined?(RSpec::Its)'
19
19
  rewriter.register_request(@node, key, code, :context)
@@ -13,17 +13,43 @@ module Transpec
13
13
  class MethodStub < Syntax
14
14
  include Mixin::Send, Mixin::MonkeyPatch, Mixin::AllowNoMessage, Mixin::AnyInstance, Util
15
15
 
16
+ # rubocop:disable LineLength
17
+ CLASSES_DEFINING_OWN_STUB_METHOD = [
18
+ 'Typhoeus', # https://github.com/typhoeus/typhoeus/blob/6a59c62/lib/typhoeus.rb#L66-L85
19
+ 'Excon', # https://github.com/geemus/excon/blob/6af4f9c/lib/excon.rb#L143-L178
20
+ 'Factory' # https://github.com/thoughtbot/factory_girl/blob/v3.6.2/lib/factory_girl/syntax/vintage.rb#L112
21
+ ]
22
+ # rubocop:enable LineLength
23
+
24
+ def self.dynamic_analysis_target_node?(node)
25
+ target_node?(node)
26
+ end
27
+
28
+ def self.conversion_target_node?(node, runtime_data = nil)
29
+ return false unless check_target_node_statically(node)
30
+
31
+ # Check if the method is RSpec's one or not.
32
+ if source_location(node, runtime_data)
33
+ # If we have a source location runtime data, check with it.
34
+ check_target_node_dynamically(node, runtime_data)
35
+ else
36
+ # Otherwise check with a static whitelist.
37
+ receiver_node = node.children.first
38
+ const_name = Util.const_name(receiver_node)
39
+ !CLASSES_DEFINING_OWN_STUB_METHOD.include?(const_name)
40
+ end
41
+ end
42
+
16
43
  def self.target_method?(receiver_node, method_name)
17
44
  !receiver_node.nil? && [:stub, :stub!, :stub_chain, :unstub, :unstub!].include?(method_name)
18
45
  end
19
46
 
20
- def register_request_for_dynamic_analysis(rewriter)
47
+ add_dynamic_analysis_request do |rewriter|
21
48
  register_request_of_syntax_availability_inspection(
22
49
  rewriter,
23
50
  :allow_to_receive_available?,
24
51
  [:allow, :receive]
25
52
  )
26
- register_request_of_any_instance_inspection(rewriter)
27
53
  end
28
54
 
29
55
  def allow_to_receive_available?
@@ -36,7 +62,7 @@ module Transpec
36
62
  return if method_name == :stub_chain && !rspec_version.receive_message_chain_available?
37
63
 
38
64
  unless allow_to_receive_available?
39
- fail InvalidContextError.new(selector_range, "##{method_name}", '#allow')
65
+ fail ContextError.new(selector_range, "##{method_name}", '#allow')
40
66
  end
41
67
 
42
68
  source, type = replacement_source_and_conversion_type(rspec_version)
@@ -8,29 +8,30 @@ module Transpec
8
8
  module AnyInstance
9
9
  include ::AST::Sexp
10
10
 
11
- def register_request_of_any_instance_inspection(rewriter)
12
- key = :any_instance_target_class_name
13
- code = <<-END.gsub(/^\s+\|/, '').chomp
14
- |if self.class.name == 'RSpec::Mocks::AnyInstance::Recorder'
15
- | if respond_to?(:klass)
16
- | klass.name
17
- | elsif instance_variable_defined?(:@klass)
18
- | instance_variable_get(:@klass).name
19
- | else
20
- | nil
21
- | end
22
- |else
23
- | nil
24
- |end
25
- END
26
- rewriter.register_request(subject_node, key, code)
11
+ def self.included(syntax)
12
+ syntax.add_dynamic_analysis_request do |rewriter|
13
+ key = :any_instance_target_class_name
14
+ code = <<-END.gsub(/^\s+\|/, '').chomp
15
+ |if self.class.name == 'RSpec::Mocks::AnyInstance::Recorder'
16
+ | if respond_to?(:klass)
17
+ | klass.name
18
+ | elsif instance_variable_defined?(:@klass)
19
+ | instance_variable_get(:@klass).name
20
+ | else
21
+ | nil
22
+ | end
23
+ |else
24
+ | nil
25
+ |end
26
+ END
27
+ rewriter.register_request(subject_node, key, code)
28
+ end
27
29
  end
28
30
 
29
31
  def any_instance?
30
32
  return true unless any_instance_target_node.nil?
31
33
  node_data = runtime_node_data(subject_node)
32
- return false unless node_data
33
- return false unless node_data[:any_instance_target_class_name]
34
+ return false unless node_data && node_data[:any_instance_target_class_name]
34
35
  !node_data[:any_instance_target_class_name].result.nil?
35
36
  end
36
37
 
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+
3
+ require 'transpec/syntax/have'
4
+
5
+ module Transpec
6
+ class Syntax
7
+ module Mixin
8
+ module HaveMatcherOwner
9
+ def self.included(syntax)
10
+ syntax.add_dynamic_analysis_request do |rewriter|
11
+ if Have.dynamic_analysis_target_node?(matcher_node)
12
+ create_have_matcher.register_request_for_dynamic_analysis(rewriter)
13
+ end
14
+ end
15
+ end
16
+
17
+ def have_matcher
18
+ return @have_matcher if instance_variable_defined?(:@have_matcher)
19
+
20
+ @have_matcher ||= if Have.conversion_target_node?(matcher_node, @runtime_data)
21
+ create_have_matcher
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def create_have_matcher
30
+ Have.new(matcher_node, self, @source_rewriter, @runtime_data, @report)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,58 +1,42 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'active_support/concern'
4
+
3
5
  module Transpec
4
6
  class Syntax
5
7
  module Mixin
6
8
  module Send
7
- def self.included(klass)
8
- klass.extend(ClassMethods)
9
- end
9
+ extend ActiveSupport::Concern
10
10
 
11
11
  module ClassMethods
12
- def register_request_for_dynamic_analysis(node, rewriter)
13
- return unless target_node?(node)
14
-
15
- receiver_node, method_name, *_ = *node
16
-
17
- if receiver_node
18
- target_node = receiver_node
19
- target_object_type = :object
20
- else
21
- target_node = node
22
- target_object_type = :context
23
- end
24
-
25
- key = source_location_key(method_name)
26
- code = "method(#{method_name.inspect}).source_location"
27
- rewriter.register_request(target_node, key, code, target_object_type)
28
- end
29
-
30
12
  def target_node?(node, runtime_data = nil)
31
13
  check_target_node_statically(node) && check_target_node_dynamically(node, runtime_data)
32
14
  end
33
15
 
34
- def target_method?(receiver_node, method_name)
35
- false
36
- end
37
-
38
16
  def check_target_node_statically(node)
39
17
  return false unless node && node.type == :send
40
18
  receiver_node, method_name, *_ = *node
41
19
  target_method?(receiver_node, method_name)
42
20
  end
43
21
 
22
+ def target_method?(receiver_node, method_name)
23
+ NotImplementedError
24
+ end
25
+
44
26
  def check_target_node_dynamically(node, runtime_data)
45
- return true unless runtime_data
27
+ source_location = source_location(node, runtime_data)
28
+ return true unless source_location
29
+ file_path = source_location.first
30
+ !file_path.match(%r{/gems/rspec\-[^/]+/lib/rspec/}).nil?
31
+ end
46
32
 
33
+ def source_location(node, runtime_data)
34
+ return unless runtime_data
47
35
  receiver_node, method_name, *_ = *node
48
36
  target_node = receiver_node ? receiver_node : node
49
-
50
- return true unless (node_data = runtime_data[target_node])
51
- return true unless (eval_data = node_data[source_location_key(method_name)])
52
- return true unless (source_location = eval_data.result)
53
-
54
- file_path = source_location.first
55
- !file_path.match(%r{/gems/rspec\-[^/]+/lib/rspec/}).nil?
37
+ return unless (node_data = runtime_data[target_node])
38
+ return unless (eval_data = node_data[source_location_key(method_name)])
39
+ eval_data.result
56
40
  end
57
41
 
58
42
  def source_location_key(method_name)
@@ -60,6 +44,22 @@ module Transpec
60
44
  end
61
45
  end
62
46
 
47
+ def self.included(syntax)
48
+ syntax.add_dynamic_analysis_request do |rewriter|
49
+ if receiver_node
50
+ target_node = receiver_node
51
+ target_object_type = :object
52
+ else
53
+ target_node = @node
54
+ target_object_type = :context
55
+ end
56
+
57
+ key = self.class.source_location_key(method_name)
58
+ code = "method(#{method_name.inspect}).source_location"
59
+ rewriter.register_request(target_node, key, code, target_object_type)
60
+ end
61
+ end
62
+
63
63
  def receiver_node
64
64
  @node.children[0]
65
65
  end
@@ -6,6 +6,14 @@ module Transpec
6
6
  class Syntax
7
7
  module Mixin
8
8
  module ShouldBase
9
+ def self.included(syntax)
10
+ syntax.add_dynamic_analysis_request do |rewriter|
11
+ if OperatorMatcher.dynamic_analysis_target_node?(matcher_node)
12
+ create_operator_matcher.register_request_for_dynamic_analysis(rewriter)
13
+ end
14
+ end
15
+ end
16
+
9
17
  def positive?
10
18
  method_name == :should
11
19
  end
@@ -14,24 +22,30 @@ module Transpec
14
22
  arg_node || parent_node
15
23
  end
16
24
 
25
+ def should_range
26
+ if arg_node
27
+ selector_range
28
+ else
29
+ selector_range.join(expression_range.end)
30
+ end
31
+ end
32
+
17
33
  def operator_matcher
18
34
  return @operator_matcher if instance_variable_defined?(:@operator_matcher)
19
35
 
20
36
  @operator_matcher ||= begin
21
- if OperatorMatcher.target_node?(matcher_node, @runtime_data)
22
- OperatorMatcher.new(matcher_node, @source_rewriter, @runtime_data, @report)
37
+ if OperatorMatcher.conversion_target_node?(matcher_node, @runtime_data)
38
+ create_operator_matcher
23
39
  else
24
40
  nil
25
41
  end
26
42
  end
27
43
  end
28
44
 
29
- def should_range
30
- if arg_node
31
- selector_range
32
- else
33
- selector_range.join(expression_range.end)
34
- end
45
+ private
46
+
47
+ def create_operator_matcher
48
+ OperatorMatcher.new(matcher_node, @source_rewriter, @runtime_data, @report)
35
49
  end
36
50
  end
37
51
  end
@@ -3,7 +3,7 @@
3
3
  require 'transpec/syntax'
4
4
  require 'transpec/syntax/mixin/should_base'
5
5
  require 'transpec/syntax/mixin/send'
6
- require 'transpec/syntax/mixin/have_matcher'
6
+ require 'transpec/syntax/mixin/have_matcher_owner'
7
7
  require 'transpec/rspec_dsl'
8
8
  require 'transpec/util'
9
9
  require 'active_support/inflector/methods'
@@ -11,7 +11,7 @@ require 'active_support/inflector/methods'
11
11
  module Transpec
12
12
  class Syntax
13
13
  class OnelinerShould < Syntax
14
- include Mixin::ShouldBase, Mixin::Send, Mixin::HaveMatcher, RSpecDSL, Util
14
+ include Mixin::ShouldBase, Mixin::Send, Mixin::HaveMatcherOwner, RSpecDSL, Util
15
15
 
16
16
  attr_reader :current_syntax_type
17
17
 
@@ -24,11 +24,6 @@ module Transpec
24
24
  @current_syntax_type = :should
25
25
  end
26
26
 
27
- def register_request_for_dynamic_analysis(rewriter)
28
- operator_matcher.register_request_for_dynamic_analysis(rewriter) if operator_matcher
29
- have_matcher.register_request_for_dynamic_analysis(rewriter) if have_matcher
30
- end
31
-
32
27
  def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
33
28
  replacement = 'is_expected.'
34
29
  replacement << (positive? ? 'to' : negative_form)
@@ -20,9 +20,10 @@ module Transpec
20
20
  !receiver_node.nil? && OPERATORS.include?(method_name)
21
21
  end
22
22
 
23
- def register_request_for_dynamic_analysis(rewriter)
24
- return unless method_name == :=~
25
- rewriter.register_request(arg_node, :arg_is_enumerable?, 'is_a?(Enumerable)')
23
+ add_dynamic_analysis_request do |rewriter|
24
+ if method_name == :=~
25
+ rewriter.register_request(arg_node, :arg_is_enumerable?, 'is_a?(Enumerable)')
26
+ end
26
27
  end
27
28
 
28
29
  def convert_operator!(parenthesize_arg = true)
@@ -5,14 +5,14 @@ require 'transpec/syntax/mixin/should_base'
5
5
  require 'transpec/syntax/mixin/send'
6
6
  require 'transpec/syntax/mixin/monkey_patch'
7
7
  require 'transpec/syntax/mixin/expectizable'
8
- require 'transpec/syntax/mixin/have_matcher'
8
+ require 'transpec/syntax/mixin/have_matcher_owner'
9
9
  require 'transpec/util'
10
10
 
11
11
  module Transpec
12
12
  class Syntax
13
13
  class Should < Syntax
14
14
  include Mixin::ShouldBase, Mixin::Send, Mixin::MonkeyPatch, Mixin::Expectizable,
15
- Mixin::HaveMatcher, Util
15
+ Mixin::HaveMatcherOwner, Util
16
16
 
17
17
  attr_reader :current_syntax_type
18
18
 
@@ -25,15 +25,12 @@ module Transpec
25
25
  @current_syntax_type = :should
26
26
  end
27
27
 
28
- def register_request_for_dynamic_analysis(rewriter)
28
+ add_dynamic_analysis_request do |rewriter|
29
29
  register_request_of_syntax_availability_inspection(
30
30
  rewriter,
31
31
  :expect_available?,
32
32
  [:expect]
33
33
  )
34
-
35
- operator_matcher.register_request_for_dynamic_analysis(rewriter) if operator_matcher
36
- have_matcher.register_request_for_dynamic_analysis(rewriter) if have_matcher
37
34
  end
38
35
 
39
36
  def expect_available?
@@ -41,9 +38,7 @@ module Transpec
41
38
  end
42
39
 
43
40
  def expectize!(negative_form = 'not_to', parenthesize_matcher_arg = true)
44
- unless expect_available?
45
- fail InvalidContextError.new(selector_range, "##{method_name}", '#expect')
46
- end
41
+ fail ContextError.new(selector_range, "##{method_name}", '#expect') unless expect_available?
47
42
 
48
43
  if proc_literal?(subject_node)
49
44
  replace(range_of_subject_method_taking_block, 'expect')
@@ -19,7 +19,7 @@ module Transpec
19
19
  !receiver_node.nil? && [:should_receive, :should_not_receive].include?(method_name)
20
20
  end
21
21
 
22
- def register_request_for_dynamic_analysis(rewriter)
22
+ add_dynamic_analysis_request do |rewriter|
23
23
  register_request_of_syntax_availability_inspection(
24
24
  rewriter,
25
25
  :expect_to_receive_available?,
@@ -31,8 +31,6 @@ module Transpec
31
31
  :allow_to_receive_available?,
32
32
  [:allow, :receive]
33
33
  )
34
-
35
- register_request_of_any_instance_inspection(rewriter)
36
34
  end
37
35
 
38
36
  def expect_to_receive_available?
@@ -49,7 +47,7 @@ module Transpec
49
47
 
50
48
  def expectize!(negative_form = 'not_to')
51
49
  unless expect_to_receive_available?
52
- fail InvalidContextError.new(selector_range, "##{method_name}", '#expect')
50
+ fail ContextError.new(selector_range, "##{method_name}", '#expect')
53
51
  end
54
52
 
55
53
  convert_to_syntax!('expect', negative_form)
@@ -60,7 +58,7 @@ module Transpec
60
58
  return unless useless_expectation?
61
59
 
62
60
  unless allow_to_receive_available?
63
- fail InvalidContextError.new(selector_range, "##{method_name}", '#allow')
61
+ fail ContextError.new(selector_range, "##{method_name}", '#allow')
64
62
  end
65
63
 
66
64
  convert_to_syntax!('allow', negative_form)
@@ -5,7 +5,7 @@ module Transpec
5
5
  module Version
6
6
  MAJOR = 1
7
7
  MINOR = 5
8
- PATCH = 0
8
+ PATCH = 1
9
9
 
10
10
  def self.to_s
11
11
  [MAJOR, MINOR, PATCH].join('.')
@@ -60,7 +60,7 @@ end
60
60
  shared_context 'syntax object' do |syntax_class, name|
61
61
  let(name) do
62
62
  ast.each_node do |node|
63
- next unless syntax_class.target_node?(node)
63
+ next unless syntax_class.conversion_target_node?(node)
64
64
  return syntax_class.new(node, source_rewriter, runtime_data)
65
65
  end
66
66
 
@@ -70,6 +70,7 @@ end
70
70
 
71
71
  shared_context 'isolated environment' do
72
72
  around do |example|
73
+ require 'tmpdir'
73
74
  Dir.mktmpdir do |tmpdir|
74
75
  Dir.chdir(tmpdir) do
75
76
  example.run
@@ -79,8 +80,6 @@ shared_context 'isolated environment' do
79
80
  end
80
81
 
81
82
  shared_context 'inside of git repository' do
82
- require 'tmpdir'
83
-
84
83
  around do |example|
85
84
  Dir.mkdir('repo')
86
85
  Dir.chdir('repo') do