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
99
100
101
102
# File 'lib/sugar_utils/file.rb', line 93

def self.read_json(filename, options = {})
  options[:value_on_missing] = :missing

  read_result = read(filename, options)
  return {} if read_result == :missing

  MultiJson.load(read_result)
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)


113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/sugar_utils/file.rb', line 113

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:



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
169
170
171
172
# File 'lib/sugar_utils/file.rb', line 140

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:



184
185
186
# File 'lib/sugar_utils/file.rb', line 184

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