Class: Dotenv::Merge::FileAnalysis

Inherits:
Object
  • Object
show all
Includes:
Ast::Merge::FileAnalyzable
Defined in:
lib/dotenv/merge/file_analysis.rb,
sig/dotenv/merge.rbs

Overview

File analysis for dotenv files. Parses dotenv source and extracts environment variable assignments, comments, and freeze blocks.

Dotenv files follow a simple format:

  • KEY=value - Environment variable assignment
  • export KEY=value - Assignment with export prefix
  • # comment - Comment line
  • Blank lines are preserved

Examples:

Basic usage

analysis = FileAnalysis.new(dotenv_source)
analysis.statements.each do |stmt|
  puts stmt.class
end

With custom freeze token

analysis = FileAnalysis.new(source, freeze_token: "my-merge")
# Looks for: # my-merge:freeze / # my-merge:unfreeze

Constant Summary collapse

DEFAULT_FREEZE_TOKEN =

Default freeze token for identifying freeze blocks

Returns:

  • (String)
'dotenv-merge'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, **_options) ⇒ FileAnalysis

Initialize file analysis with dotenv parser

Parameters:

  • source (String)

    Dotenv source code to analyze

  • freeze_token (String) (defaults to: DEFAULT_FREEZE_TOKEN)

    Token for freeze block markers (default: "dotenv-merge")

  • signature_generator (Proc, nil) (defaults to: nil)

    Custom signature generator

  • options (Hash)

    Additional options (forward compatibility - ignored by FileAnalysis)



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/dotenv/merge/file_analysis.rb', line 40

def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, **_options)
  @source = source
  @freeze_token = freeze_token
  @signature_generator = signature_generator
  @structural_diagnostics = []
  # **options captures any additional parameters (e.g., node_typing) for forward compatibility

  # Parse all lines
  @lines = parse_lines(source)

  # Initialize comment tracking before freeze block integration
  @comment_tracker = CommentTracker.new(@lines)

  # Extract and integrate freeze blocks
  @statements = extract_and_integrate_statements

  DebugLogger.debug('FileAnalysis initialized', {
                      signature_generator: signature_generator ? 'custom' : 'default',
                      lines_count: @lines.size,
                      statements_count: @statements.size,
                      freeze_blocks: freeze_blocks.size,
                      assignments: assignment_lines.size
                    })
end

Instance Attribute Details

#comment_trackerCommentTracker (readonly)

Returns Comment tracker for this file.

Returns:



32
33
34
# File 'lib/dotenv/merge/file_analysis.rb', line 32

def comment_tracker
  @comment_tracker
end

#structural_diagnosticsCommentTracker (readonly)

Returns Comment tracker for this file.

Returns:



32
33
34
# File 'lib/dotenv/merge/file_analysis.rb', line 32

def structural_diagnostics
  @structural_diagnostics
end

Instance Method Details

#all_assignmentsArray<EnvLine>

Get all assignment lines including those in freeze blocks

Returns:



182
183
184
# File 'lib/dotenv/merge/file_analysis.rb', line 182

def all_assignments
  @lines.select(&:assignment?)
end

#assignment_linesArray<EnvLine>

Get assignment lines (not in freeze blocks)

Returns:



165
166
167
# File 'lib/dotenv/merge/file_analysis.rb', line 165

def assignment_lines
  @statements.select { |stmt| stmt.is_a?(EnvLine) && stmt.assignment? }
end

#comment_attachment_for(owner, **options) ⇒ Ast::Merge::Comment::Attachment

Build a passive shared comment attachment for an owner.

Parameters:

  • owner (Object)

    Structural owner for the attachment

  • options (Hash)

    Additional metadata / lookup overrides

Returns:

  • (Ast::Merge::Comment::Attachment)


138
139
140
141
142
143
144
# File 'lib/dotenv/merge/file_analysis.rb', line 138

def comment_attachment_for(owner, **options)
  shared_comment_attachment_for(
    owner,
    tracker_attachment: comment_augmenter(**options).attachment_for(owner),
    **options
  )
end

#comment_attachment_strategySymbol

Returns:

  • (Symbol)


147
148
149
# File 'lib/dotenv/merge/file_analysis.rb', line 147

def comment_attachment_strategy
  :tracker_layout_merge
end

#comment_augmenter(owners: nil, **options) ⇒ Ast::Merge::Comment::Augmenter

Build a passive shared comment augmenter for this analysis.

Parameters:

  • owners (Array<#start_line,#end_line>, nil) (defaults to: nil)

    Owners used for attachment inference

  • options (Hash)

    Additional augmenter options

Returns:

  • (Ast::Merge::Comment::Augmenter)


126
127
128
129
130
131
# File 'lib/dotenv/merge/file_analysis.rb', line 126

def comment_augmenter(owners: nil, **options)
  comment_tracker.augment(
    owners: owners || comment_augmenter_default_owners,
    **options
  )
end

#comment_capabilityAst::Merge::Comment::Capability

Get shared comment capability information for this analysis.

Returns:

  • (Ast::Merge::Comment::Capability)


74
75
76
# File 'lib/dotenv/merge/file_analysis.rb', line 74

def comment_capability
  @comment_capability ||= comment_tracker.augment(owners: []).capability
end

#comment_node_at(line_num) ⇒ Ast::Merge::Comment::Line?

Get a shared Ast::Merge comment node at a specific line.

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Ast::Merge::Comment::Line, nil)


103
104
105
# File 'lib/dotenv/merge/file_analysis.rb', line 103

def comment_node_at(line_num)
  comment_tracker.comment_node_at(line_num)
end

#comment_nodesArray<Ast::Merge::Comment::Line>

Get all tracked comments converted to shared Ast::Merge comment nodes.

Returns:

  • (Array<Ast::Merge::Comment::Line>)


95
96
97
# File 'lib/dotenv/merge/file_analysis.rb', line 95

def comment_nodes
  comment_tracker.comment_nodes
end

#comment_region_for_range(range, kind:, full_line_only: false) ⇒ Ast::Merge::Comment::Region

Get comments in a line range converted to a shared comment region.

Parameters:

  • range (Range)

    Range of 1-based line numbers

  • kind (Symbol)

    Region kind (:leading, :inline, :orphan, etc.)

  • full_line_only (Boolean) (defaults to: false)

    Whether to keep only full-line comments

Returns:

  • (Ast::Merge::Comment::Region)


113
114
115
116
117
118
119
# File 'lib/dotenv/merge/file_analysis.rb', line 113

def comment_region_for_range(range, kind:, full_line_only: false)
  comment_tracker.comment_region_for_range(
    range,
    kind: kind,
    full_line_only: full_line_only
  )
end

#comment_support_styleAst::Merge::Comment::SupportStyle

Describe how dotenv merges currently own and emit comments.

Dotenv comment handling is source-augmented and emitted through the synthetic merge layer.

Returns:

  • (Ast::Merge::Comment::SupportStyle)


84
85
86
87
88
89
90
# File 'lib/dotenv/merge/file_analysis.rb', line 84

def comment_support_style
  @comment_support_style ||= shared_comment_support_style(
    source: :dotenv_source,
    style: :hash_comment,
    read_strategy: :source_augmented_portable_write
  )
end

#compute_node_signature(node) ⇒ Array?

Compute default signature for a node

Parameters:

Returns:

  • (Array, nil)

    Signature array



199
200
201
202
203
204
205
206
# File 'lib/dotenv/merge/file_analysis.rb', line 199

def compute_node_signature(node)
  case node
  when FreezeNode
    node.signature
  when EnvLine
    node.signature
  end
end

#env_var(key) ⇒ EnvLine?

Get environment variable by key

Parameters:

  • key (String)

    The environment variable key

Returns:

  • (EnvLine, nil)

    The assignment line or nil



215
216
217
# File 'lib/dotenv/merge/file_analysis.rb', line 215

def env_var(key)
  @lines.find { |line| line.assignment? && line.key == key }
end

#keysArray<String>

Get all environment variable keys

Returns:

  • (Array<String>)

    List of keys



221
222
223
# File 'lib/dotenv/merge/file_analysis.rb', line 221

def keys
  all_assignments.map(&:key)
end

#line_at(line_number) ⇒ EnvLine?

Get a specific line (1-indexed) Override base to return EnvLine objects instead of raw strings

Parameters:

  • line_number (Integer)

    Line number (1-indexed)

Returns:

  • (EnvLine, nil)

    The line object



190
191
192
193
194
# File 'lib/dotenv/merge/file_analysis.rb', line 190

def line_at(line_number)
  return if line_number < 1

  @lines[line_number - 1]
end

#ruleset_match_keyObject



155
156
157
# File 'lib/dotenv/merge/file_analysis.rb', line 155

def ruleset_match_key
  :env_key
end

#ruleset_owner_selectorObject



151
152
153
# File 'lib/dotenv/merge/file_analysis.rb', line 151

def ruleset_owner_selector
  :assignment_lines_plus_freeze_blocks
end

#ruleset_render_familyObject



159
160
161
# File 'lib/dotenv/merge/file_analysis.rb', line 159

def ruleset_render_family
  :dotenv_assignments
end

#structural_ownersArray<EnvLine, FreezeNode>

Get merge-relevant structural owners in source order. For dotenv this means assignment lines plus integrated freeze blocks, excluding standalone comments, blanks, and invalid lines.

Returns:



174
175
176
177
178
# File 'lib/dotenv/merge/file_analysis.rb', line 174

def structural_owners
  @structural_owners ||= @statements.select do |stmt|
    stmt.is_a?(FreezeNode) || (stmt.is_a?(EnvLine) && stmt.assignment?)
  end
end

#valid?Boolean

Check if parse was successful (dotenv always succeeds, may have invalid lines)

Returns:

  • (Boolean)


67
68
69
# File 'lib/dotenv/merge/file_analysis.rb', line 67

def valid?
  true
end