Module: Flexr::ArtifactWriter

Defined in:
lib/flexr/artifact_writer.rb

Class Method Summary collapse

Class Method Details

.atomic_replace(path, content) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/flexr/artifact_writer.rb', line 32

def atomic_replace(path, content)
  expanded = File.expand_path(path)
  directory = File.dirname(expanded)
  basename = File.basename(expanded)
  mode = File.exist?(expanded) ? File.stat(expanded).mode & 0o7777 : 0o666 & ~File.umask
  temporary_path = nil

  Tempfile.create([".#{basename}.", ".tmp"], directory, binmode: true) do |temporary|
    temporary_path = temporary.path
    temporary.write(content)
    temporary.flush
    temporary.fsync
    temporary.chmod(mode)
    temporary.close
    File.rename(temporary_path, expanded)
    temporary_path = nil
  end
  sync_directory(directory)
ensure
  File.unlink(temporary_path) if temporary_path && File.exist?(temporary_path)
end

.default_generated_path(source_path) ⇒ Object



9
10
11
12
13
14
# File 'lib/flexr/artifact_writer.rb', line 9

def default_generated_path(source_path)
  return source_path.sub(/\.flexr\.rb\z/, ".rb") if source_path.end_with?(".flexr.rb")
  return source_path.sub(/\.rb\z/, ".generated.rb") if source_path.end_with?(".rb")

  "#{source_path}.generated.rb"
end

.ensure_distinct!(source_path, output_path) ⇒ Object

Raises:

  • (ArgumentError)


24
25
26
27
28
29
30
# File 'lib/flexr/artifact_writer.rb', line 24

def ensure_distinct!(source_path, output_path)
  same_path = File.expand_path(source_path) == File.expand_path(output_path)
  same_file = File.exist?(source_path) && File.exist?(output_path) && File.identical?(source_path, output_path)
  return unless same_path || same_file

  raise ArgumentError, "refusing to overwrite input file: #{source_path}"
end

.sync_directory(directory) ⇒ Object



54
55
56
57
58
59
# File 'lib/flexr/artifact_writer.rb', line 54

def sync_directory(directory)
  File.open(directory, File::RDONLY, &:fsync)
rescue SystemCallError, IOError
  # Some supported filesystems do not allow fsync on directories. The file
  # itself has already been synced before the atomic rename.
end

.write!(path, content, source_path: nil) ⇒ Object



16
17
18
19
20
21
22
# File 'lib/flexr/artifact_writer.rb', line 16

def write!(path, content, source_path: nil)
  ensure_distinct!(source_path, path) if source_path
  return :unchanged if File.file?(path) && File.binread(path) == content.b

  atomic_replace(path, content)
  :written
end