Class: Prdigest::State

Inherits:
Object
  • Object
show all
Defined in:
lib/prdigest/state.rb

Defined Under Namespace

Classes: Record

Constant Summary collapse

VERSION =
1

Instance Method Summary collapse

Constructor Details

#initialize(path:, timezone:, rename: File.method(:rename), directory_sync: nil) ⇒ State

Returns a new instance of State.



13
14
15
16
17
18
# File 'lib/prdigest/state.rb', line 13

def initialize(path:, timezone:, rename: File.method(:rename), directory_sync: nil)
  @path = File.expand_path(path)
  @timezone = timezone
  @rename = rename
  @directory_sync = directory_sync || method(:sync_directory)
end

Instance Method Details

#read(yesterday: nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/prdigest/state.rb', line 20

def read(yesterday: nil)
  return Record.new(nil, nil) unless File.exist?(@path)

  payload = JSON.parse(File.binread(@path))
  validate_payload!(payload, yesterday: yesterday)
rescue StateError
  raise
rescue StandardError => e
  raise StateError, "cannot read state: #{e.class}"
end

#write(last_digested_date:, last_skip: nil) ⇒ Object



31
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
# File 'lib/prdigest/state.rb', line 31

def write(last_digested_date:, last_skip: nil)
  directory = File.dirname(@path)
  FileUtils.mkdir_p(directory, mode: 0o700)
  temp_path = File.join(directory, ".#{File.basename(@path)}.tmp-#{SecureRandom.hex(8)}")
  payload = JSON.generate(serialize(last_digested_date, last_skip)) << "\n"

  File.open(temp_path, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |file|
    file.write(payload)
    file.chmod(0o600)
    file.flush
    file.fsync
  end
  rollback_path = preserve_previous_checkpoint(directory)
  @rename.call(temp_path, @path)
  begin
    @directory_sync.call(directory)
  rescue StandardError
    restore_previous_checkpoint(rollback_path)
    raise
  end
  Record.new(Date.iso8601(last_digested_date.to_s), normalize_skip(last_skip))
rescue StandardError => e
  raise StateError, "cannot write state: #{e.class}"
ensure
  File.unlink(temp_path) if defined?(temp_path) && temp_path && File.exist?(temp_path)
  discard_rollback(rollback_path) if defined?(rollback_path) && rollback_path
end