Class: Mailmate::IndexReader

Inherits:
Object
  • Object
show all
Defined in:
lib/mailmate/index_reader.rb

Constant Summary collapse

RECORD_SIZE =
12

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ IndexReader

Returns a new instance of IndexReader.

Raises:

  • (ArgumentError)


79
80
81
82
83
84
85
86
87
88
# File 'lib/mailmate/index_reader.rb', line 79

def initialize(name)
  @name = name
  base = "#{Mailmate.config.db_headers}/#{name}"
  raise ArgumentError, "Index not found: #{name} (looked at #{base}.{cache,offsets})" \
    unless File.exist?("#{base}.cache") && File.exist?("#{base}.offsets")

  @cache_bytes   = File.binread("#{base}.cache")
  @offsets_bytes = File.binread("#{base}.offsets")
  build_index!
end

Instance Attribute Details

#nameObject (readonly)



77
78
79
# File 'lib/mailmate/index_reader.rb', line 77

def name
  @name
end

Class Method Details

.for(name) ⇒ Object

Per-process cache of readers keyed by [name, db_headers]. Including db_headers means a Mailmate.config swap (e.g. a test pointing at a different tmpdir) doesn’t return stale readers built from the old path.



52
53
54
55
# File 'lib/mailmate/index_reader.rb', line 52

def for(name)
  @cache ||= {}
  @cache[cache_key(name)] ||= new(name)
end

.reset!(name = nil) ⇒ Object

Invalidate cached readers. With no argument, drops the entire cache (useful for tests or when MailMate’s database swaps out). With a name, invalidates only entries for that name across all db_headers — the common case (cache-bust after a write) doesn’t need to thread config through.



62
63
64
65
66
67
68
# File 'lib/mailmate/index_reader.rb', line 62

def reset!(name = nil)
  if name.nil?
    @cache = nil
  elsif @cache
    @cache.delete_if { |(n, _dir), _reader| n == name }
  end
end

Instance Method Details

#each_eml_id(&block) ⇒ Object

Iterate every recorded eml-id. Yields just the id; callers that also want the value should pair this with ‘value_for`. Exists so other gem modules don’t have to reach into ‘@index` directly.



134
135
136
137
# File 'lib/mailmate/index_reader.rb', line 134

def each_eml_id(&block)
  return enum_for(:each_eml_id) unless block
  @index.each_key(&block)
end

#each_recordObject

Iterate every (eml_id, raw_value) pair, once per on-disk record. Multi-record ids yield multiple times. The value comes back as the bare cache substring; callers that need parsed form (e.g. flag tokens) should massage it themselves.



143
144
145
146
147
148
# File 'lib/mailmate/index_reader.rb', line 143

def each_record
  return enum_for(:each_record) unless block_given?
  @index.each do |eml_id, pairs|
    pairs.each { |(s, e)| yield eml_id, @cache_bytes[s...e] }
  end
end

#flags_for(eml_id) ⇒ Object

‘#flags.flag` semantics: the cache stores a space-separated list of IMAP keywords. Split into individual flag tokens.



114
115
116
117
118
# File 'lib/mailmate/index_reader.rb', line 114

def flags_for(eml_id)
  v = value_for(eml_id)
  return [] if v.nil? || v.empty?
  v.split(/\s+/).reject(&:empty?)
end

#record_countObject

Total number of on-disk records (sum across all ids). Diagnostics.



127
128
129
# File 'lib/mailmate/index_reader.rb', line 127

def record_count
  @index.values.sum(&:size)
end

#sizeObject

Number of distinct ids in the index. For multi-record indexes this is smaller than the on-disk record count (use record_count for that).



122
123
124
# File 'lib/mailmate/index_reader.rb', line 122

def size
  @index.size
end

#value_for(eml_id) ⇒ Object

Returns the raw cached value for a given .eml body-part ID, or nil if the id isn’t in this index. Returns the LAST record for the id — for accumulator-style header indexes (‘#flags`, `#source`, `subject`, etc.) that’s the latest state; the older records are stale versions. For body indexes (‘#unquoted#lc`, `#quoted#lc`) last-alone is meaningless — use values_for to read every segment.



96
97
98
99
100
101
# File 'lib/mailmate/index_reader.rb', line 96

def value_for(eml_id)
  pairs = @index[eml_id.to_i]
  return nil if pairs.nil? || pairs.empty?
  s, e = pairs[-1]
  @cache_bytes[s...e]
end

#values_for(eml_id) ⇒ Object

Returns every recorded value for an id, in offsets-file order. Returns

if the id isn’t in the index. Use this for body indexes

(#unquoted#lc, #quoted#lc), which store one record per text segment.



106
107
108
109
110
# File 'lib/mailmate/index_reader.rb', line 106

def values_for(eml_id)
  pairs = @index[eml_id.to_i]
  return [] if pairs.nil?
  pairs.map { |(s, e)| @cache_bytes[s...e] }
end