transpec 1.5.0 → 1.5.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 (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