Class: RSpecLetAnalyzer::Visitors::ParserVisitor

Inherits:
Parser::AST::Processor
  • Object
show all
Defined in:
lib/rspec_let_analyzer/visitors/parser_visitor.rb

Overview

Visitor for the parser gem (uses AST::Processor)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_nesting_depth, track_factories) ⇒ ParserVisitor

Returns a new instance of ParserVisitor.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 16

def initialize(max_nesting_depth, track_factories)
  super()
  @root_lets = 0
  @total_its = 0
  @redefinitions = 0
  @depth = 0
  @let_names_by_depth = {}
  @max_nesting_depth = max_nesting_depth
  @nesting_counts = Array.new(max_nesting_depth, 0) if max_nesting_depth
  @track_factories = track_factories
  @factory_usage = Hash.new(0) if track_factories
  @before_creates = 0
  @in_before_block = false
end

Instance Attribute Details

#before_createsObject (readonly)

Returns the value of attribute before_creates.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def before_creates
  @before_creates
end

#factory_usageObject (readonly)

Returns the value of attribute factory_usage.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def factory_usage
  @factory_usage
end

#nesting_countsObject (readonly)

Returns the value of attribute nesting_counts.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def nesting_counts
  @nesting_counts
end

#redefinitionsObject (readonly)

Returns the value of attribute redefinitions.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def redefinitions
  @redefinitions
end

#root_letsObject (readonly)

Returns the value of attribute root_lets.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def root_lets
  @root_lets
end

#total_itsObject (readonly)

Returns the value of attribute total_its.



14
15
16
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 14

def total_its
  @total_its
end

Instance Method Details

#handler_missing(node) ⇒ Object



129
130
131
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 129

def handler_missing(node)
  node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
end

#on_block(node) ⇒ Object

For block nodes, we need to handle them specially Blocks in parser gem represent describe/context/let blocks



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 53

def on_block(node)
  send_node = node.children[0]
  return unless send_node.is_a?(::Parser::AST::Node)
  return unless send_node.type == :send

  method_name = send_node.children[1]

  case method_name
  when :describe, :context, :shared_examples, :shared_context
    @depth += 1
    # Process args
    send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
    # Process block body
    body = node.children[2]
    process(body) if body.is_a?(::Parser::AST::Node)
    @depth -= 1
    @let_names_by_depth.delete(@depth + 1)
  when :before
    # Check if this is before or before(:each)
    arg = send_node.children[2]
    is_before_each = arg.nil? || (arg.type == :sym && arg.children[0] == :each)

    if is_before_each
      @in_before_block = true
      # Process args
      send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
      # Process block body
      body = node.children[2]
      process(body) if body.is_a?(::Parser::AST::Node)
      @in_before_block = false
    else
      # Process args
      send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
      # Process block body
      body = node.children[2]
      process(body) if body.is_a?(::Parser::AST::Node)
    end
  when :it, :specify
    @total_its += 1
    # Process args
    send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
    # Process block body
    body = node.children[2]
    process(body) if body.is_a?(::Parser::AST::Node)
  when :let, :let!
    let_name = extract_let_name_from_block(node)

    if let_name
      @redefinitions += 1 if redefinition?(let_name)
      @let_names_by_depth[@depth] ||= []
      @let_names_by_depth[@depth] << let_name
    end

    if @depth == 1
      @root_lets += 1
    elsif @max_nesting_depth
      nesting_index = @depth - 2
      if nesting_index < @max_nesting_depth - 1
        @nesting_counts[nesting_index] += 1
      else
        @nesting_counts[@max_nesting_depth - 1] += 1
      end
    end

    # Process args and body
    send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
    body = node.children[2]
    process(body) if body.is_a?(::Parser::AST::Node)
  else
    # Process all children
    send_node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
    body = node.children[2]
    process(body) if body.is_a?(::Parser::AST::Node)
  end
end

#on_send(node) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rspec_let_analyzer/visitors/parser_visitor.rb', line 31

def on_send(node)
  method_name = node.children[1]

  case method_name
  when :create, :build, :build_stubbed
    # Track if create is inside a before block
    if @in_before_block && method_name == :create
      @before_creates += 1
    end

    if @track_factories
      factory_name = extract_factory_name(node)
      @factory_usage[factory_name] += 1 if factory_name
    end
    node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
  else
    node.children.each { |child| process(child) if child.is_a?(::Parser::AST::Node) }
  end
end