Class: RailsErrorDashboard::Services::SourceCodeReader

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_error_dashboard/services/source_code_reader.rb

Overview

Reads source code files from disk with security validation and context lines around a target line number

Examples:

reader = SourceCodeReader.new("/path/to/app/models/user.rb", 42)
lines = reader.read_lines(context: 5)
# Returns array of hashes with line numbers and content

Constant Summary collapse

MAX_FILE_SIZE =

10 MB

10 * 1024 * 1024
MAX_CONTEXT_LINES =
50

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path, line_number) ⇒ SourceCodeReader

Initialize a new source code reader

Parameters:

  • file_path (String)

    Path to the source file

  • line_number (Integer)

    Target line number



22
23
24
25
26
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 22

def initialize(file_path, line_number)
  @file_path = file_path
  @line_number = line_number.to_i
  @error = nil
end

Instance Attribute Details

#errorObject (readonly)

Returns the value of attribute error.



16
17
18
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 16

def error
  @error
end

#file_pathObject (readonly)

Returns the value of attribute file_path.



16
17
18
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 16

def file_path
  @file_path
end

#line_numberObject (readonly)

Returns the value of attribute line_number.



16
17
18
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 16

def line_number
  @line_number
end

Instance Method Details

#file_exists?Boolean

Check if file exists on disk

Returns:

  • (Boolean)


75
76
77
78
79
80
81
82
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 75

def file_exists?
  absolute_path = resolve_absolute_path
  return false unless absolute_path

  File.exist?(absolute_path) && File.readable?(absolute_path)
rescue StandardError
  false
end

#read_lines(context: 5) ⇒ Array<Hash>?

Read source code lines with context around the target line

Parameters:

  • context (Integer) (defaults to: 5)

    Number of lines before and after target line

Returns:

  • (Array<Hash>, nil)

    Array of line data hashes or nil if unavailable



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rails_error_dashboard/services/source_code_reader.rb', line 32

def read_lines(context: 5)
  context = [ [ context, MAX_CONTEXT_LINES ].min, 1 ].max

  # Validate and resolve path
  absolute_path = resolve_absolute_path
  return nil unless absolute_path

  # Validate path is safe
  unless validate_path!(absolute_path)
    @error = "Invalid or unsafe file path"
    return nil
  end

  # Check file exists and is readable
  unless File.exist?(absolute_path) && File.readable?(absolute_path)
    @error = "File not found or not readable"
    return nil
  end

  # Check file size
  file_size = File.size(absolute_path)
  if file_size > MAX_FILE_SIZE
    @error = "File too large (#{file_size} bytes, max #{MAX_FILE_SIZE})"
    return nil
  end

  # Check if file is binary
  if binary_file?(absolute_path)
    @error = "Binary file cannot be displayed"
    return nil
  end

  # Read the specific lines
  read_specific_lines(absolute_path, line_number - context, line_number + context)
rescue StandardError => e
  @error = "Error reading file: #{e.message}"
  RailsErrorDashboard::Logger.error("SourceCodeReader error for #{file_path}:#{line_number} - #{e.message}")
  nil
end