Module: SugarUtils::File

Defined in:
lib/sugar_utils/file.rb

Defined Under Namespace

Classes: Error

Class Method Summary collapse

Class Method Details

.flock_exclusive(file, options = {}) ⇒ void

This method returns an undefined value.

Parameters:

  • file (File)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10

Raises:

  • (Timeout::Error)


32
33
34
35
# File 'lib/sugar_utils/file.rb', line 32

def self.flock_exclusive(file, options = {})
  timeout = options[:timeout] || 10
  Timeout.timeout(timeout) { file.flock(::File::LOCK_EX) }
end

.flock_shared(file, options = {}) ⇒ void

This method returns an undefined value.

Parameters:

  • file (File)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10

Raises:

  • (Timeout::Error)


20
21
22
23
# File 'lib/sugar_utils/file.rb', line 20

def self.flock_shared(file, options = {})
  timeout = options[:timeout] || 10
  Timeout.timeout(timeout) { file.flock(::File::LOCK_SH) }
end

.read(filename, options = {}) ⇒ String

Parameters:

  • filename (String)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10
  • :raise_on_missing (Boolean) — default: true
  • :value_on_missing (String) — default: ''

    which specifies the value to return if the file is missing and raise_on_missing is false

  • :scrub_encoding (Boolean, String)

    scrub incorrectly encoded characters with this value, or with ” if the value is true

Returns:

  • (String)

Raises:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/sugar_utils/file.rb', line 49

def self.read(filename, options = {}) # rubocop:disable MethodLength, AbcSize, CyclomaticComplexity, PerceivedComplexity
  options[:value_on_missing] ||= ''
  options[:raise_on_missing] = true if options[:raise_on_missing].nil?

  result =
    ::File.open(filename, ::File::RDONLY) do |file|
      flock_shared(file, options)
      file.read
    end

  return result unless options[:scrub_encoding]

  replacement_character =
    if options[:scrub_encoding].is_a?(String)
      options[:scrub_encoding]
    else
      ''
    end
  if result.respond_to?(:scrub)
    result.scrub(replacement_character)
  else
    result.encode(
      result.encoding,
      'binary',
      invalid: :replace,
      undef:   :replace,
      replace: replacement_character
    )
  end
rescue SystemCallError, IOError
  raise(Error, "Cannot read #{filename}") if options[:raise_on_missing]
  options[:value_on_missing]
rescue Timeout::Error
  raise(Error, "Cannot read #{filename} because it is locked")
end

.read_json(filename, options = {}) ⇒ Object

Parameters:

  • filename (String)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10
  • :raise_on_missing (Boolean) — default: true

Returns:

  • (Object)

Raises:



93
94
95
96
97
98
# File 'lib/sugar_utils/file.rb', line 93

def self.read_json(filename, options = {})
  options[:value_on_missing] = {}
  MultiJson.load(read(filename, options))
rescue MultiJson::ParseError
  raise(Error, "Cannot parse #{filename}")
end

.touch(filename, options = {}) ⇒ void

This method returns an undefined value.

Parameters:

  • filename (String)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :owner (String, Integer)
  • :group (String, Integer)
  • :mode (Integer)
  • :perm (Integer)

    @deprecated

  • :mtime (Integer)


109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/sugar_utils/file.rb', line 109

def self.touch(filename, options = {})
  owner         = options[:owner]
  group         = options[:group]
  mode          = options[:mode] || options[:perm]
  touch_options = options.select { |k| %i[mtime].include?(k) }

  deprecate_option(:touch, :perm, :mode, 2017, 8) if options.key?(:perm)

  FileUtils.mkdir_p(::File.dirname(filename))
  FileUtils.touch(filename, touch_options)
  FileUtils.chown(owner, group, filename)
  FileUtils.chmod(mode, filename) if mode
end

.write(filename, data, options = {}) ⇒ void

This method returns an undefined value.

Parameters:

  • filename (String)
  • data (#to_s)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10
  • :flush (Boolean) — default: false
  • :owner (String, Integer)
  • :group (String, Integer)
  • :mode (Integer) — default: 0o644
  • :perm (Integer) — default: 0o644

    @deprecated

Raises:



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/sugar_utils/file.rb', line 136

def self.write(filename, data, options = {}) # rubocop:disable MethodLength, AbcSize, CyclomaticComplexity
  flush = options[:flush] || false
  owner = options[:owner]
  group = options[:group]
  mode  = options[:mode] || options[:perm] || 0o644

  deprecate_option(:touch, :perm, :mode, 2017, 8) if options.key?(:perm)

  FileUtils.mkdir_p(::File.dirname(filename))
  ::File.open(filename, ::File::RDWR | ::File::CREAT, mode) do |file|
    flock_exclusive(file, options)

    file.truncate(0) # Ensure file is empty before proceeding.
    file.puts(data.to_s)

    # Flush and fsync to be 100% sure we write this data out now because we
    # are often reading it immediately and if the OS is buffering, it is
    # possible we might read it before it is been physically written to
    # disk. We are not worried about speed here, so this should be OKAY.
    if flush
      file.flush
      file.fsync
    end

    # Ensure that the permissions are correct if the file already existed.
    file.chmod(mode)
  end
  FileUtils.chown(owner, group, filename)
rescue Timeout::Error
  raise(Error, "Unable to write #{filename} because it is locked")
rescue SystemCallError, IOError => boom
  raise(Error, "Unable to write #{filename} with #{boom}")
end

.write_json(filename, data, options = {}) ⇒ void

This method returns an undefined value.

Parameters:

  • filename (String)
  • data (#to_json)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :timeout (Integer) — default: 10
  • :flush (Boolean) — default: false
  • :perm (Integer) — default: 0644

Raises:



180
181
182
# File 'lib/sugar_utils/file.rb', line 180

def self.write_json(filename, data, options = {})
  write(filename, MultiJson.dump(data, pretty: true), options)
end