Class: Audition::Target

Inherits:
Object
  • Object
show all
Defined in:
lib/audition/target.rb

Overview

Figures out what the user pointed us at and what that means for scanning (which .rb files) and dynamic probing (which entry).

Detection precedence for directories: Rails beats Rack (a Rails root always has a config.ru), Rack beats gem (an app may vendor a gemspec), gem beats plain directory.

Constant Summary collapse

EXCLUDED_DIRS =
%w[
  vendor node_modules tmp log coverage pkg .git .bundle
].freeze
TEST_DIRS =

Directories holding the target's tests rather than the code a production boot loads. The default; test_dirs in .audition.yml replaces it for a project that files them elsewhere.

%w[test spec features].freeze
BUILD_DIRS =

Cargo and Zig build output: full of .so/.dylib artifacts that are not the extension require loads.

%w[target zig-out].freeze
COMPILED =

What Ruby loads as a native extension (RbConfig DLEXT is "bundle" on macOS, "so" everywhere else, Windows included).

"*.{bundle,so}"
STUBS =

Generated type stubs. The extension is the convention; where the generator writes them is not, so they are found by extension anywhere in the tree.

"*.rbi"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type:, root:, ruby_files:, entry:, compiled_files: [], stub_files: []) ⇒ Target

Returns a new instance of Target.



281
282
283
284
285
286
287
288
289
# File 'lib/audition/target.rb', line 281

def initialize(type:, root:, ruby_files:, entry:,
  compiled_files: [], stub_files: [])
  @type = type
  @root = root
  @ruby_files = ruby_files
  @entry = entry
  @compiled_files = compiled_files
  @stub_files = stub_files
end

Instance Attribute Details

#compiled_files ⇒ Array<String> (readonly)

Returns compiled extension files (.bundle/.so) shipped with the target; empty for scripts and file lists.

Returns:

  • (Array<String>) —

    compiled extension files (.bundle/.so) shipped with the target; empty for scripts and file lists



46
47
48
# File 'lib/audition/target.rb', line 46

def compiled_files
  @compiled_files
end

#entry ⇒ Hash? (readonly)

Returns dynamic probe entry (:mode plus mode-specific keys), nil for static-only targets.

Returns:

  • (Hash, nil) —

    dynamic probe entry (:mode plus mode-specific keys), nil for static-only targets



54
55
56
# File 'lib/audition/target.rb', line 54

def entry
  @entry
end

#root ⇒ String (readonly)

Returns the target's root directory.

Returns:

  • (String) —

    the target's root directory



39
40
41
# File 'lib/audition/target.rb', line 39

def root
  @root
end

#ruby_files ⇒ Array<String> (readonly)

Returns Ruby files to scan statically.

Returns:

  • (Array<String>) —

    Ruby files to scan statically



42
43
44
# File 'lib/audition/target.rb', line 42

def ruby_files
  @ruby_files
end

#stub_files ⇒ Array<String> (readonly)

Returns generated type stubs (.rbi) found anywhere in the target's tree.

Returns:

  • (Array<String>) —

    generated type stubs (.rbi) found anywhere in the target's tree



50
51
52
# File 'lib/audition/target.rb', line 50

def stub_files
  @stub_files
end

#type ⇒ Symbol (readonly)

Returns one of :script, :gem, :rack, :rails, :directory, :bundle.

Returns:

  • (Symbol) —

    one of :script, :gem, :rack, :rails, :directory, :bundle



36
37
38
# File 'lib/audition/target.rb', line 36

def type
  @type
end

Class Method Details

.compiled_for(spec) ⇒ Array<String>

Compiled extension files of an installed gem. RubyGems builds a source gem into its extension dir and also copies the result under lib/, so the same file shows up on two require paths; keep one per require-relative name.

Parameters:

  • spec (Gem::Specification)

Returns:

  • (Array<String>)


271
272
273
274
275
# File 'lib/audition/target.rb', line 271

def self.compiled_for(spec)
  spec.full_require_paths.flat_map do |rp|
    compiled(rp).map { |path| [path.delete_prefix("#{rp}/"), path] }
  end.uniq(&:first).map(&:last)
end

.detect(raw) ⇒ Target

Detects what raw points at and builds the target.

Parameters:

  • raw (String) —

    a .rb/.ru file, a directory, a Gemfile.lock, or an installed gem name

Returns:

Raises:



62
63
64
65
66
67
68
69
70
71
# File 'lib/audition/target.rb', line 62

def self.detect(raw)
  raw = normalize(raw)
  if File.file?(raw)
    from_file(raw)
  elsif File.directory?(raw)
    from_directory(raw)
  else
    from_gem_name(raw)
  end
end

.for_files(paths) ⇒ Target

Builds a static-only target from an explicit file list, the shape git hooks hand over (lefthook's staged_files, pre-commit's filename arguments). Config, pragmas, and the baseline resolve against the working directory, which is the repository root when a hook manager runs the command.

Parameters:

  • paths (Array<String>) —

    .rb/.ru files

Returns:

  • (Target) —

    type :files, no dynamic entry

Raises:



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/audition/target.rb', line 82

def self.for_files(paths)
  paths.each do |path|
    unless File.file?(path) && path.end_with?(".rb", ".ru")
      raise Error, "#{path} is not a Ruby file"
    end
  end

  new(
    type: :files,
    root: Dir.pwd,
    ruby_files: paths,
    entry: nil,
    stub_files: stubs(Dir.pwd)
  )
end

.stubs(dir) ⇒ Array<String>

Generated type stubs anywhere under a directory.

Parameters:

  • dir (String)

Returns:

  • (Array<String>)


252
253
254
# File 'lib/audition/target.rb', line 252

def self.stubs(dir)
  glob(dir, STUBS)
end

Instance Method Details

#test_file?(path, dirs: TEST_DIRS) ⇒ Boolean

Whether a path is test/spec code rather than code the production boot loads.

Parameters:

  • path (String) —

    absolute or root-relative

  • dirs (Array<String>) (defaults to: TEST_DIRS) —

    directory names that hold tests

Returns:

  • (Boolean)


297
298
299
300
301
302
303
# File 'lib/audition/target.rb', line 297

def test_file?(path, dirs: TEST_DIRS)
  relative = File.expand_path(path, root)
    .delete_prefix("#{root}/")
  parts = relative.split("/")
  parts[0..-2].any? { |p| dirs.include?(p) } ||
    parts.last.to_s.end_with?("_test.rb", "_spec.rb")
end