Class: Coat::Ledger

Inherits:
Object
  • Object
show all
Defined in:
lib/coat/ledger.rb

Overview

Append-only record of collected pieces, one JSON object per line. Each entry carries the hash of the entry before it, so editing, reordering, or deleting history breaks the chain and first_break says where.

A coat is evidence of what agents did. Its provenance has to be at least as hard to quietly rewrite as the work it describes.

Constant Summary collapse

GENESIS =
"0" * 64

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Ledger

Returns a new instance of Ledger.



19
20
21
22
# File 'lib/coat/ledger.rb', line 19

def initialize(path)
  @path = path
  FileUtils.mkdir_p(File.dirname(path))
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



17
18
19
# File 'lib/coat/ledger.rb', line 17

def path
  @path
end

Instance Method Details

#append(fields) ⇒ Object

Holds an exclusive lock across both the read of the tail and the write, so two agents adding pieces at once cannot fork the chain.



26
27
28
29
30
31
32
33
# File 'lib/coat/ledger.rb', line 26

def append(fields)
  File.open(@path, File::RDWR | File::CREAT, 0o644) do |file|
    file.flock(File::LOCK_EX)
    entry = seal(fields, parse(file.read).last)
    file.write("#{JSON.generate(entry)}\n")
    entry
  end
end

#entriesObject



35
36
37
38
39
# File 'lib/coat/ledger.rb', line 35

def entries
  return [] unless File.exist?(@path)

  parse(File.read(@path))
end

#first_breakObject

The sequence number of the first entry whose link does not hold, or nil when the chain is intact end to end.



43
44
45
46
47
48
49
50
51
# File 'lib/coat/ledger.rb', line 43

def first_break
  previous = nil
  entries.each do |entry|
    return entry["seq"] unless intact?(entry, previous)

    previous = entry
  end
  nil
end