Class: Pinspec::Analyzer::TargetParser

Inherits:
Object
  • Object
show all
Includes:
Source
Defined in:
lib/pinspec/analyzer/target_parser.rb

Defined Under Namespace

Classes: Delegation, Descriptor, MethodDef, Scope

Constant Summary collapse

MODEL_BASES =
["ApplicationRecord", "ActiveRecord::Base"].freeze
INERT_BASES =
%w[Object BasicObject].freeze
CLOCK_CALLS =
{
  "Time"     => %i[now new],
  "Date"     => %i[today],
  "DateTime" => %i[now]
}.freeze
VISIBILITIES =
%i[public private protected].freeze
SCALARISH_NAMES =
%w[
  amount total subtotal price cost quantity qty count index number num
  name email phone title body text message description reason code type
  kind status state value key data payload options opts args params attrs
  attributes config settings id token flag mode format scope limit offset
  date time now today percent rate ratio sum size length label url path
].freeze
DI_PATTERNS =
[
  /\ARails\.application\.config\b/,
  /\ARails\.configuration\b/,
  /Container\b.*\.resolve\b/,
  /\AContainer\./
].freeze
PRUNE_AT =
[
  Prism::DefNode,
  Prism::ClassNode,
  Prism::ModuleNode,
  Prism::SingletonClassNode
].freeze
DEFAULT_METHOD =
"call"

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Source

read

Constructor Details

#initialize(file_path, method_name) ⇒ TargetParser

Returns a new instance of TargetParser.



79
80
81
82
# File 'lib/pinspec/analyzer/target_parser.rb', line 79

def initialize(file_path, method_name)
  @file_path  = file_path
  @descriptor = parse_descriptor(method_name)
end

Class Method Details

.parse(file_path, method_name) ⇒ Object



59
60
61
# File 'lib/pinspec/analyzer/target_parser.rb', line 59

def parse(file_path, method_name)
  new(file_path, method_name).parse
end

.split_target(target) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/pinspec/analyzer/target_parser.rb', line 65

def split_target(target)
  text = target.to_s
  return text.split("#", 2) if text.include?("#")

  unless text.end_with?(".rb")
    raise ArgumentError,
          "target must be a Ruby file, optionally with #METHOD " \
          "(got #{target.inspect}); e.g. app/services/invoice_calculator.rb#call"
  end

  [text, DEFAULT_METHOD]
end

Instance Method Details

#parseObject



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
# File 'lib/pinspec/analyzer/target_parser.rb', line 84

def parse
  read_source
  build_index

  mdef  = select_definition
  scope = @scopes[mdef.scope_name]

  unless scope
    raise TargetNotFound,
          "`#{@descriptor.method_name}` is defined at the top level of " \
          "#{@file_path}, not inside a class or module; pinspec has no " \
          "receiver to build."
  end

  guard_block!(mdef, scope)

  construction_kind, initializer_params = resolve_construction(scope, mdef)
  init_node = find_initialize(scope.name)

  scan = [mdef.node, init_node].compact

  TargetProfile.new(
    file_path:            @file_path,
    class_name:           scope.name,
    method_name:          mdef.name,
    params:               params_of(mdef.node),
    initializer_params:   initializer_params,
    construction_kind:    construction_kind,
    visibility:           mdef.visibility,
    takes_block:          false,
    source_range:         [mdef.node.location.start_line, mdef.node.location.end_line],
    referenced_constants: referenced_constants(scan),
    clock_sites:          clock_sites(scan)
  )
end