transpec 1.10.3 → 1.10.4

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