transpec 1.10.3 → 1.10.4

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +2 -0
  5. data/README.md +11 -5
  6. data/README.md.erb +11 -5
  7. data/lib/transpec.rb +1 -19
  8. data/lib/transpec/base_rewriter.rb +1 -1
  9. data/lib/transpec/converter.rb +2 -3
  10. data/lib/transpec/dynamic_analyzer/rewriter.rb +2 -2
  11. data/lib/transpec/option_parser.rb +2 -2
  12. data/lib/transpec/parser.rb +9 -0
  13. data/lib/transpec/record.rb +45 -7
  14. data/lib/transpec/syntax.rb +15 -21
  15. data/lib/transpec/syntax/allow.rb +2 -2
  16. data/lib/transpec/syntax/be_boolean.rb +2 -2
  17. data/lib/transpec/syntax/be_close.rb +2 -2
  18. data/lib/transpec/syntax/current_example.rb +5 -6
  19. data/lib/transpec/syntax/double.rb +2 -2
  20. data/lib/transpec/syntax/example.rb +13 -14
  21. data/lib/transpec/syntax/expect.rb +2 -2
  22. data/lib/transpec/syntax/have.rb +5 -8
  23. data/lib/transpec/syntax/have/{dynamic_inspector.rb → dynamic_analysis.rb} +25 -47
  24. data/lib/transpec/syntax/have/have_record.rb +12 -16
  25. data/lib/transpec/syntax/its.rb +5 -5
  26. data/lib/transpec/syntax/matcher_definition.rb +2 -2
  27. data/lib/transpec/syntax/method_stub.rb +29 -28
  28. data/lib/transpec/syntax/mixin/any_instance_block.rb +2 -2
  29. data/lib/transpec/syntax/mixin/matcher_owner.rb +16 -13
  30. data/lib/transpec/syntax/mixin/monkey_patch.rb +2 -2
  31. data/lib/transpec/syntax/mixin/monkey_patch_any_instance.rb +1 -1
  32. data/lib/transpec/syntax/mixin/send.rb +42 -30
  33. data/lib/transpec/syntax/mixin/useless_and_return.rb +2 -4
  34. data/lib/transpec/syntax/oneliner_should.rb +17 -19
  35. data/lib/transpec/syntax/operator.rb +9 -14
  36. data/lib/transpec/syntax/pending.rb +27 -18
  37. data/lib/transpec/syntax/raise_error.rb +2 -2
  38. data/lib/transpec/syntax/receive.rb +2 -2
  39. data/lib/transpec/syntax/rspec_configure.rb +2 -2
  40. data/lib/transpec/syntax/should.rb +9 -9
  41. data/lib/transpec/syntax/should_receive.rb +18 -16
  42. data/lib/transpec/version.rb +1 -1
  43. data/spec/spec_helper.rb +7 -0
  44. data/spec/support/shared_context.rb +4 -4
  45. data/spec/transpec/cli_spec.rb +1 -1
  46. data/spec/transpec/converter_spec.rb +1 -1
  47. data/spec/transpec/dynamic_analyzer/rewriter_spec.rb +8 -8
  48. data/spec/transpec/syntax/current_example_spec.rb +75 -19
  49. data/spec/transpec/syntax/double_spec.rb +11 -11
  50. data/spec/transpec/syntax/example_spec.rb +8 -4
  51. data/spec/transpec/syntax/have_spec.rb +1 -1
  52. data/spec/transpec/syntax/method_stub_spec.rb +14 -20
  53. data/spec/transpec/syntax/oneliner_should_spec.rb +51 -0
  54. data/spec/transpec/syntax/pending_spec.rb +8 -4
  55. data/spec/transpec_spec.rb +4 -6
  56. data/tasks/demo.rake +2 -2
  57. data/tasks/lib/transpec_demo.rb +1 -1
  58. data/tasks/lib/transpec_test.rb +1 -1
  59. data/tasks/test.rake +2 -2
  60. metadata +4 -3
@@ -13,8 +13,8 @@ module Transpec
13
13
  add_matcher Have
14
14
  add_matcher RaiseError
15
15
 
16
- def self.target_method?(receiver_node, method_name)
17
- receiver_node.nil? && [:expect, :expect_any_instance_of].include?(method_name)
16
+ def dynamic_analysis_target?
17
+ super && receiver_node.nil? && [:expect, :expect_any_instance_of].include?(method_name)
18
18
  end
19
19
 
20
20
  def method_name_for_instance
@@ -7,11 +7,11 @@ require 'transpec/syntax/mixin/owned_matcher'
7
7
  module Transpec
8
8
  class Syntax
9
9
  class Have < Syntax
10
- require 'transpec/syntax/have/dynamic_inspector'
10
+ require 'transpec/syntax/have/dynamic_analysis'
11
11
  require 'transpec/syntax/have/source_builder'
12
12
  require 'transpec/syntax/have/have_record'
13
13
 
14
- include Mixin::Send, Mixin::OwnedMatcher
14
+ include Mixin::Send, Mixin::OwnedMatcher, DynamicAnalysis
15
15
 
16
16
  # String#count is not query method, and there's no way to determine
17
17
  # whether a method is query method.
@@ -22,15 +22,12 @@ module Transpec
22
22
  # for String (String responds to #size).
23
23
  QUERY_METHOD_PRIORITIES = [:size, :count, :length].freeze
24
24
 
25
- def self.target_method?(receiver_node, method_name)
26
- receiver_node.nil? &&
25
+ def dynamic_analysis_target?
26
+ super &&
27
+ receiver_node.nil? &&
27
28
  [:have, :have_exactly, :have_at_least, :have_at_most].include?(method_name)
28
29
  end
29
30
 
30
- define_dynamic_analysis_request do |rewriter|
31
- DynamicInspector.register_request(self, rewriter)
32
- end
33
-
34
31
  def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
35
32
  return if project_requires_collection_matcher?
36
33
 
@@ -5,62 +5,41 @@ require 'transpec/syntax/have'
5
5
  module Transpec
6
6
  class Syntax
7
7
  class Have
8
- class DynamicInspector
9
- def self.register_request(have, rewriter)
10
- new(have, rewriter).register_request
11
- end
12
-
13
- attr_reader :have, :rewriter
14
-
15
- def initialize(have, rewriter)
16
- @have = have
17
- @rewriter = rewriter
18
- end
8
+ module DynamicAnalysis
9
+ extend ActiveSupport::Concern
19
10
 
20
- def target_node
21
- if have.explicit_subject?
22
- have.expectation.subject_node
23
- else
24
- have.expectation.node
25
- end
26
- end
11
+ included do
12
+ define_dynamic_analysis do |rewriter|
13
+ target_node = explicit_subject? ? expectation.subject_node : expectation.node
14
+ target_type = explicit_subject? ? :object : :context
27
15
 
28
- def target_type
29
- if have.explicit_subject?
30
- :object
31
- else
32
- :context
33
- end
34
- end
16
+ key = :collection_accessor
17
+ code = collection_accessor_inspection_code
18
+ rewriter.register_request(target_node, key, code, target_type)
35
19
 
36
- def register_request
37
- key = :collection_accessor
38
- code = collection_accessor_inspection_code
39
- rewriter.register_request(target_node, key, code, target_type)
20
+ # Give up inspecting query methods of collection accessor with arguments
21
+ # (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
22
+ unless items_method_has_arguments?
23
+ key = :available_query_methods
24
+ code = available_query_methods_inspection_code
25
+ rewriter.register_request(target_node, key, code, target_type)
26
+ end
40
27
 
41
- # Give up inspecting query methods of collection accessor with arguments
42
- # (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
43
- unless have.items_method_has_arguments?
44
- key = :available_query_methods
45
- code = available_query_methods_inspection_code
28
+ key = :collection_accessor_is_private?
29
+ code = "#{subject_code}.private_methods.include?(#{items_name.inspect})"
46
30
  rewriter.register_request(target_node, key, code, target_type)
47
- end
48
-
49
- key = :collection_accessor_is_private?
50
- code = "#{subject_code}.private_methods.include?(#{have.items_name.inspect})"
51
- rewriter.register_request(target_node, key, code, target_type)
52
31
 
53
- key = :project_requires_collection_matcher?
54
- code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
55
- rewriter.register_request(target_node, key, code, :context)
32
+ key = :project_requires_collection_matcher?
33
+ code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
34
+ rewriter.register_request(target_node, key, code, :context)
35
+ end
56
36
  end
57
37
 
58
38
  def subject_code
59
- have.explicit_subject? ? 'self' : 'subject'
39
+ explicit_subject? ? 'self' : 'subject'
60
40
  end
61
41
 
62
- # rubocop:disable MethodLength
63
- def collection_accessor_inspection_code
42
+ def collection_accessor_inspection_code # rubocop:disable MethodLength
64
43
  # `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
65
44
  # if the owner does not respond to any of #size, #count and #length.
66
45
  #
@@ -69,7 +48,7 @@ module Transpec
69
48
  # rubocop:enable LineLength
70
49
  @collection_accessor_inspection_code ||= <<-END.gsub(/^\s+\|/, '').chomp
71
50
  |begin
72
- | exact_name = #{have.items_name.inspect}
51
+ | exact_name = #{items_name.inspect}
73
52
  |
74
53
  | inflector = if defined?(ActiveSupport::Inflector) &&
75
54
  | ActiveSupport::Inflector.respond_to?(:pluralize)
@@ -100,7 +79,6 @@ module Transpec
100
79
  |end
101
80
  END
102
81
  end
103
- # rubocop:enable MethodLength
104
82
 
105
83
  def available_query_methods_inspection_code
106
84
  <<-END.gsub(/^\s+\|/, '').chomp
@@ -12,22 +12,6 @@ module Transpec
12
12
  @have = have
13
13
  end
14
14
 
15
- def original_syntax
16
- @original_syntax ||= begin
17
- type = have.expectation.class.snake_case_name.to_sym
18
- syntax = build_expectation(original_subject, type)
19
- syntax << " #{have.method_name}(n).#{original_items}"
20
- end
21
- end
22
-
23
- def converted_syntax
24
- @converted_syntax ||= begin
25
- type = have.expectation.current_syntax_type
26
- syntax = build_expectation(converted_subject, type)
27
- syntax << " #{source_builder.replacement_matcher_source}"
28
- end
29
- end
30
-
31
15
  def annotation
32
16
  return @annotation if instance_variable_defined?(:@annotation)
33
17
 
@@ -40,6 +24,18 @@ module Transpec
40
24
 
41
25
  private
42
26
 
27
+ def build_original_syntax
28
+ type = have.expectation.class.snake_case_name.to_sym
29
+ syntax = build_expectation(original_subject, type)
30
+ syntax << " #{have.method_name}(n).#{original_items}"
31
+ end
32
+
33
+ def build_converted_syntax
34
+ type = have.expectation.current_syntax_type
35
+ syntax = build_expectation(converted_subject, type)
36
+ syntax << " #{source_builder.replacement_matcher_source}"
37
+ end
38
+
43
39
  def build_expectation(subject, type)
44
40
  case type
45
41
  when :should
@@ -9,16 +9,16 @@ module Transpec
9
9
  class Its < Syntax
10
10
  include Mixin::Send, Util
11
11
 
12
- def self.target_method?(receiver_node, method_name)
13
- receiver_node.nil? && method_name == :its
14
- end
15
-
16
- define_dynamic_analysis_request do |rewriter|
12
+ define_dynamic_analysis do |rewriter|
17
13
  key = :project_requires_its?
18
14
  code = 'defined?(RSpec::Its)'
19
15
  rewriter.register_request(node, key, code, :context)
20
16
  end
21
17
 
18
+ def dynamic_analysis_target?
19
+ super && receiver_node.nil? && method_name == :its
20
+ end
21
+
22
22
  def convert_to_describe_subject_it!
23
23
  return if project_requires_its?
24
24
 
@@ -15,8 +15,8 @@ module Transpec
15
15
  failure_message_for_should_not: :failure_message_when_negated
16
16
  }
17
17
 
18
- def self.target_method?(receiver_node, method_name)
19
- receiver_node.nil? && CONVERSION_CORRESPONDENCE.keys.include?(method_name)
18
+ def dynamic_analysis_target?
19
+ super && receiver_node.nil? && CONVERSION_CORRESPONDENCE.keys.include?(method_name)
20
20
  end
21
21
 
22
22
  def convert_deprecated_method!
@@ -19,35 +19,36 @@ module Transpec
19
19
  ]
20
20
  # rubocop:enable LineLength
21
21
 
22
- def self.conversion_target_node?(node, runtime_data = nil)
23
- return false unless check_target_node_statically(node)
22
+ define_dynamic_analysis do |rewriter|
23
+ register_syntax_availability_analysis_request(
24
+ rewriter,
25
+ :allow_to_receive_available?,
26
+ [:allow, :receive]
27
+ )
28
+ end
29
+
30
+ def dynamic_analysis_target?
31
+ super &&
32
+ !receiver_node.nil? &&
33
+ [:stub, :stub!, :stub_chain, :unstub, :unstub!].include?(method_name)
34
+ end
35
+
36
+ def conversion_target?
37
+ return false unless dynamic_analysis_target?
24
38
 
25
39
  # Check if the method is RSpec's one or not.
26
- if source_location(node, runtime_data)
27
- # If we have a source location runtime data, check with it.
28
- check_target_node_dynamically(node, runtime_data)
40
+ if runtime_data.run?(send_analysis_target_node)
41
+ # If we have runtime data, check with it.
42
+ defined_by_rspec?
29
43
  else
30
44
  # Otherwise check with a static whitelist.
31
- receiver_node = node.children.first
32
- const_name = Util.const_name(receiver_node)
45
+ const_name = const_name(receiver_node)
33
46
  !CLASSES_DEFINING_OWN_STUB_METHOD.include?(const_name)
34
47
  end
35
48
  end
36
49
 
37
- def self.target_method?(receiver_node, method_name)
38
- !receiver_node.nil? && [:stub, :stub!, :stub_chain, :unstub, :unstub!].include?(method_name)
39
- end
40
-
41
- define_dynamic_analysis_request do |rewriter|
42
- register_request_of_syntax_availability_inspection(
43
- rewriter,
44
- :allow_to_receive_available?,
45
- [:allow, :receive]
46
- )
47
- end
48
-
49
50
  def allow_to_receive_available?
50
- check_syntax_availability(__method__)
51
+ syntax_available?(__method__)
51
52
  end
52
53
 
53
54
  def hash_arg?
@@ -187,7 +188,7 @@ module Transpec
187
188
  @conversion_type = conversion_type
188
189
  end
189
190
 
190
- def original_syntax
191
+ def build_original_syntax
191
192
  syntax = @method_stub.any_instance? ? 'Klass.any_instance' : 'obj'
192
193
  syntax << ".#{@method_stub.method_name}"
193
194
 
@@ -198,7 +199,7 @@ module Transpec
198
199
  end
199
200
  end
200
201
 
201
- def converted_syntax
202
+ def build_converted_syntax
202
203
  syntax = @method_stub.any_instance? ? 'allow_any_instance_of(Klass)' : 'allow(obj)'
203
204
  syntax << '.to '
204
205
 
@@ -221,12 +222,12 @@ module Transpec
221
222
  @method_stub = method_stub
222
223
  end
223
224
 
224
- def original_syntax
225
+ def build_original_syntax
225
226
  syntax = @method_stub.any_instance? ? 'Klass.any_instance' : 'obj'
226
227
  syntax << ".#{@method_stub.method_name}(:message)"
227
228
  end
228
229
 
229
- def converted_syntax
230
+ def build_converted_syntax
230
231
  syntax = 'obj.'
231
232
  syntax << @method_stub.send(:replacement_method_for_deprecated_method)
232
233
  syntax << '(:message)'
@@ -238,19 +239,19 @@ module Transpec
238
239
  @method_stub = method_stub
239
240
  end
240
241
 
241
- def original_syntax
242
+ private
243
+
244
+ def build_original_syntax
242
245
  syntax = base_syntax
243
246
  syntax << '.any_number_of_times' if @method_stub.any_number_of_times?
244
247
  syntax << '.at_least(0)' if @method_stub.at_least_zero?
245
248
  syntax
246
249
  end
247
250
 
248
- def converted_syntax
251
+ def build_converted_syntax
249
252
  base_syntax
250
253
  end
251
254
 
252
- private
253
-
254
255
  def base_syntax
255
256
  "obj.#{@method_stub.method_name}(:message)"
256
257
  end
@@ -33,11 +33,11 @@ module Transpec
33
33
  @host = host
34
34
  end
35
35
 
36
- def original_syntax
36
+ def build_original_syntax
37
37
  "#{base_syntax} { |arg| }"
38
38
  end
39
39
 
40
- def converted_syntax
40
+ def build_converted_syntax
41
41
  "#{base_syntax} { |instance, arg| }"
42
42
  end
43
43
 
@@ -10,29 +10,32 @@ module Transpec
10
10
 
11
11
  module ClassMethods
12
12
  def add_matcher(matcher_class) # rubocop:disable MethodLength
13
- matcher_accessor_name = "#{matcher_class.snake_case_name}_matcher"
14
- matcher_ivar_name = "@#{matcher_accessor_name}"
15
- matcher_creator_name = "create_#{matcher_class.snake_case_name}"
13
+ matcher_accessor = "#{matcher_class.snake_case_name}_matcher"
14
+ matcher_ivar = "@#{matcher_accessor}"
15
+ matcher_creator = "create_#{matcher_class.snake_case_name}"
16
16
 
17
- define_dynamic_analysis_request do |rewriter|
18
- if matcher_class.dynamic_analysis_target_node?(matcher_node)
19
- send(matcher_creator_name).register_request_for_dynamic_analysis(rewriter)
17
+ define_dynamic_analysis do |rewriter|
18
+ matcher = send(matcher_creator)
19
+ if matcher.dynamic_analysis_target?
20
+ matcher.register_dynamic_analysis_request(rewriter)
20
21
  end
21
22
  end
22
23
 
23
- define_method(matcher_accessor_name) do
24
- if instance_variable_defined?(matcher_ivar_name)
25
- return instance_variable_get(matcher_ivar_name)
24
+ define_method(matcher_accessor) do
25
+ if instance_variable_defined?(matcher_ivar)
26
+ return instance_variable_get(matcher_ivar)
26
27
  end
27
28
 
28
- if matcher_class.conversion_target_node?(matcher_node, runtime_data)
29
- instance_variable_set(matcher_ivar_name, send(matcher_creator_name))
29
+ matcher = send(matcher_creator)
30
+
31
+ if matcher.conversion_target?
32
+ instance_variable_set(matcher_ivar, matcher)
30
33
  else
31
- instance_variable_set(matcher_ivar_name, nil)
34
+ instance_variable_set(matcher_ivar, nil)
32
35
  end
33
36
  end
34
37
 
35
- define_method(matcher_creator_name) do
38
+ define_method(matcher_creator) do
36
39
  matcher_class.new(matcher_node, self, source_rewriter, runtime_data, report)
37
40
  end
38
41
  end
@@ -10,7 +10,7 @@ module Transpec
10
10
  extend ActiveSupport::Concern
11
11
  include Send
12
12
 
13
- def register_request_of_syntax_availability_inspection(rewriter, key, methods)
13
+ def register_syntax_availability_analysis_request(rewriter, key, methods)
14
14
  code = "self.class.ancestors.any? { |a| a.name.start_with?('RSpec::') }"
15
15
 
16
16
  methods.each do |method|
@@ -20,7 +20,7 @@ module Transpec
20
20
  rewriter.register_request(node, key, code, :context)
21
21
  end
22
22
 
23
- def check_syntax_availability(key)
23
+ def syntax_available?(key)
24
24
  if runtime_data.present?(node, key)
25
25
  runtime_data[node, key]
26
26
  else
@@ -12,7 +12,7 @@ module Transpec
12
12
  include MonkeyPatch, ::AST::Sexp
13
13
 
14
14
  included do
15
- define_dynamic_analysis_request do |rewriter|
15
+ define_dynamic_analysis do |rewriter|
16
16
  code = <<-END.gsub(/^\s+\|/, '').chomp
17
17
  |if self.class.name == 'RSpec::Mocks::AnyInstance::Recorder'
18
18
  | if respond_to?(:klass)
@@ -8,53 +8,65 @@ module Transpec
8
8
  module Send
9
9
  extend ActiveSupport::Concern
10
10
 
11
- module ClassMethods
12
- def target_node?(node, runtime_data = nil)
13
- check_target_node_statically(node) && check_target_node_dynamically(node, runtime_data)
11
+ module TargetDetection
12
+ def dynamic_analysis_target?
13
+ node && node.send_type?
14
14
  end
15
15
 
16
- def check_target_node_statically(node)
17
- return false unless node && node.send_type?
18
- receiver_node, method_name, *_ = *node
19
- target_method?(receiver_node, method_name)
16
+ def conversion_target?
17
+ return false unless dynamic_analysis_target?
18
+ return true unless runtime_data.run?(send_analysis_target_node)
19
+ defined_by_rspec?
20
20
  end
21
21
 
22
- def target_method?(receiver_node, method_name)
23
- fail NotImplementedError
22
+ private
23
+
24
+ def defined_by_rspec?
25
+ defined_in_rspec_source? && !example_method_defined_by_user?
24
26
  end
25
27
 
26
- def check_target_node_dynamically(node, runtime_data)
27
- source_location = source_location(node, runtime_data)
28
+ def defined_in_rspec_source?
29
+ source_location = runtime_data[send_analysis_target_node, source_location_key]
28
30
  return true unless source_location
29
- file_path = source_location.first
30
- !file_path.match(%r{/rspec\-[^/]+/lib/rspec/}).nil?
31
+ source_path = source_location.first
32
+ return false unless source_path
33
+ source_path.match(%r{/rspec\-[^/]+/lib/rspec/})
34
+ end
35
+
36
+ def example_method_defined_by_user?
37
+ runtime_data[send_analysis_target_node, example_method_defined_by_user_key]
31
38
  end
32
39
 
33
- def source_location(node, runtime_data)
34
- return unless runtime_data
35
- receiver_node, method_name, *_ = *node
36
- target_node = receiver_node ? receiver_node : node
37
- runtime_data[target_node, source_location_key(method_name)]
40
+ def send_analysis_target_node
41
+ receiver_node || node
38
42
  end
39
43
 
40
- def source_location_key(method_name)
44
+ def source_location_key
41
45
  "#{method_name}_source_location".to_sym
42
46
  end
47
+
48
+ def example_method_defined_by_user_key
49
+ "#{method_name}_example_method_defined_by_user?".to_sym
50
+ end
43
51
  end
44
52
 
53
+ include TargetDetection
54
+
45
55
  included do
46
- define_dynamic_analysis_request do |rewriter|
47
- if receiver_node
48
- target_node = receiver_node
49
- target_object_type = :object
50
- else
51
- target_node = node
52
- target_object_type = :context
53
- end
54
-
55
- key = self.class.source_location_key(method_name)
56
+ define_dynamic_analysis do |rewriter|
57
+ target_type = receiver_node ? :object : :context
58
+
59
+ key = source_location_key
56
60
  code = "method(#{method_name.inspect}).source_location"
57
- rewriter.register_request(target_node, key, code, target_object_type)
61
+ rewriter.register_request(send_analysis_target_node, key, code, target_type)
62
+
63
+ key = example_method_defined_by_user_key
64
+ code = <<-END.gsub(/^\s+\|/, '').chomp
65
+ |owner = method(#{method_name.inspect}).owner
66
+ |owner != RSpec::Core::ExampleGroup &&
67
+ | owner.ancestors.include?(RSpec::Core::ExampleGroup)
68
+ END
69
+ rewriter.register_request(send_analysis_target_node, key, code, target_type)
58
70
  end
59
71
  end
60
72