Class: Pinspec::Analyzer::Discovery
- Inherits:
-
Object
- Object
- Pinspec::Analyzer::Discovery
- Defined in:
- lib/pinspec/analyzer/discovery.rb
Defined Under Namespace
Classes: Choice
Constant Summary collapse
- CONVENTIONAL =
%w[call perform run execute process].freeze
- NON_TARGETS =
Never a target: these are object protocol, not behaviour anyone pins.
%w[initialize to_s to_str inspect hash eql? == <=> to_proc].freeze
- LAST_RESORT =
Conversion methods, which are USUALLY protocol but are sometimes the whole public surface of a service object - OFN's AvailablePaymentMethodsService exposes exactly
to_aand nothing else. Excluding them outright meant such a class had no candidates at all and was refused as ambiguous. They are ranked last instead, so they win only when nothing else is offered. %w[to_a to_h each].freeze
Class Method Summary collapse
-
.convention_for(files) ⇒ Object
conventionis the method name this application uses most, counted once over the directory being pinned.
Instance Method Summary collapse
- #choose(convention: nil) ⇒ Object
-
#initialize(file_path) ⇒ Discovery
constructor
A new instance of Discovery.
- #surface ⇒ Object
Constructor Details
#initialize(file_path) ⇒ Discovery
Returns a new instance of Discovery.
51 52 53 |
# File 'lib/pinspec/analyzer/discovery.rb', line 51 def initialize(file_path) @file_path = file_path end |
Class Method Details
.convention_for(files) ⇒ Object
convention is the method name this application uses most, counted once over
the directory being pinned. Hardcoding call reads 2% of a codebase whose
services are named perform.
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/pinspec/analyzer/discovery.rb', line 37 def self.convention_for(files) counts = Hash.new(0) files.each do |file| surface = new(file).surface surface[:instance].each { |name| counts[name] += 1 } end best = counts.max_by { |name, count| [count, CONVENTIONAL.index(name) ? 1 : 0] } return nil if best.nil? || best.last < 2 best.first end |
Instance Method Details
#choose(convention: nil) ⇒ Object
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 |
# File 'lib/pinspec/analyzer/discovery.rb', line 59 def choose(convention: nil) instance = surface[:instance] singleton = surface[:singleton] if instance.empty? && singleton.empty? return Choice.new(method_name: nil, reason: :no_public_methods, candidates: [], owner: nil) end preferred = [convention].compact + CONVENTIONAL preferred.each do |name| return chosen(name, reason_for(name, convention), instance) if instance.include?(name) end preferred.each do |name| return Choice.new(method_name: name, reason: :class_method, candidates: singleton, owner: nil) if singleton.include?(name) end # A class with exactly one public method has only one thing it can mean. return chosen(instance.first, :sole_method, instance) if instance.size == 1 # Nothing conventional, and what remains is a single conversion method: that is # this class's whole public surface, so it is the target. # Conversion methods rank last, so a real entry point wins - but a class whose # only ordinary method is one of them still has something to pin. ordinary = instance - LAST_RESORT return chosen(ordinary.first, :sole_method, instance) if ordinary.size == 1 return Choice.new(method_name: singleton.first, reason: :sole_method, candidates: singleton, owner: nil) if instance.empty? && singleton.size == 1 Choice.new(method_name: nil, reason: :ambiguous, candidates: (instance + singleton).first(8), owner: nil) end |
#surface ⇒ Object
55 56 57 |
# File 'lib/pinspec/analyzer/discovery.rb', line 55 def surface @surface ||= read_surface end |