Module: SugarUtils::File
- Defined in:
- lib/sugar_utils/file.rb,
lib/sugar_utils/file/write_options.rb
Defined Under Namespace
Classes: Error, WriteOptions
Class Method Summary collapse
-
.append(filename, data, options = {}) ⇒ void
Append to an existing file, or create the file if it does not exist.
-
.atomic_write(filename, data, options = {}) ⇒ void
Atomically write to an existing file, overwriting it, or create the file if it does not exist.
-
.change_access(filename, owner, group, permission) ⇒ void
Change all of the access values for the specified file including: * owner * group * permissions.
- .flock_exclusive(file, options = {}) ⇒ void
- .flock_shared(file, options = {}) ⇒ void
- .read(filename, options = {}) ⇒ String
- .read_json(filename, options = {}) ⇒ Object
-
.touch(filename, options = {}) ⇒ void
Touch the specified file.
-
.write(filename, data, options = {}) ⇒ void
Write to an existing file, overwriting it, or create the file if it does not exist.
-
.write_json(filename, data, options = {}) ⇒ void
Write the data parameter as JSON to the filename path.
Class Method Details
.append(filename, data, options = {}) ⇒ void
Either option :mode or :perm can be used to specific the permissions
This method returns an undefined value.
Append to an existing file, or create the file if it does not exist.
on the file being written to. This aliasing is used because both these names are used in the standard library, File.open uses :perm and FileUtils uses :mode. The user can choose whichever alias makes their code most readable.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/sugar_utils/file.rb', line 305 def self.append(filename, data, = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize = WriteOptions.new(filename, ) FileUtils.mkdir_p(::File.dirname(filename)) ::File.open(filename, 'a', .perm) do |file| flock_exclusive(file, ) 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 end change_access( filename, .owner, .group, .perm ) rescue Timeout::Error raise(Error, "Unable to write #{filename} because it is locked") rescue SystemCallError, IOError => e raise(Error, "Unable to write #{filename} with #{e}") end |
.atomic_write(filename, data, options = {}) ⇒ void
Either option :mode or :perm can be used to specific the permissions
This method returns an undefined value.
Atomically write to an existing file, overwriting it, or create the file if it does not exist.
on the file being written to. This aliasing is used because both these names are used in the standard library, File.open uses :perm and FileUtils uses :mode. The user can choose whichever alias makes their code most readable.
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/sugar_utils/file.rb', line 217 def self.atomic_write(filename, data, = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize = WriteOptions.new(filename, ) # @note This method is similar to the atomic_write which is implemented in # ActiveSupport. We re-implemented the method because of the following: # * we needed the method, but wanted to avoid pulling in the entire # ActiveSupport gem. # * we wnated to keep the behaviour and interface consistent with the other # SugarUtils write methods # # @see https://apidock.com/rails/File/atomic_write/class FileUtils.mkdir_p(::File.dirname(filename)) Tempfile.open(::File.basename(filename, '.*'), ::File.dirname(filename)) do |temp_file| temp_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? temp_file.flush temp_file.fsync end temp_file.close ::File.open(filename, 'w+', .perm) do |file| flock_exclusive(file, ) FileUtils.move(temp_file.path, filename) end end change_access( filename, .owner, .group, .perm ) rescue Timeout::Error raise(Error, "Unable to write #{filename} because it is locked") rescue SystemCallError, IOError => e raise(Error, "Unable to write #{filename} with #{e}") end |
.change_access(filename, owner, group, permission) ⇒ void
Although the are all required, nil can be passed to any of them and
This method returns an undefined value.
Change all of the access values for the specified file including:
-
owner
-
group
-
permissions
those nils will be skipped. Hopefully, this will avoid conditions in the calling code because the optional parameters will just be passed in and skipped when they are missing.
58 59 60 61 62 63 64 |
# File 'lib/sugar_utils/file.rb', line 58 def self.change_access(filename, owner, group, ) FileUtils.chown(owner, group, filename) FileUtils.chmod(, filename) if nil rescue SystemCallError, IOError raise(Error, "Unable to change access on #{filename}") end |
.flock_exclusive(file, options = {}) ⇒ void
This method returns an undefined value.
35 36 37 38 |
# File 'lib/sugar_utils/file.rb', line 35 def self.flock_exclusive(file, = {}) timeout = [:timeout] || 10 Timeout.timeout(timeout) { file.flock(::File::LOCK_EX) } end |
.flock_shared(file, options = {}) ⇒ void
This method returns an undefined value.
23 24 25 26 |
# File 'lib/sugar_utils/file.rb', line 23 def self.flock_shared(file, = {}) timeout = [:timeout] || 10 Timeout.timeout(timeout) { file.flock(::File::LOCK_SH) } end |
.read(filename, options = {}) ⇒ String
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/sugar_utils/file.rb', line 78 def self.read(filename, = {}) # rubocop:disable Metrics/MethodLength [:value_on_missing] ||= '' [:raise_on_missing] = true if [:raise_on_missing].nil? result = ::File.open(filename, ::File::RDONLY) do |file| flock_shared(file, ) file.read end return result unless [:scrub_encoding] SugarUtils.scrub_encoding(result, [:scrub_encoding]) rescue SystemCallError, IOError raise(Error, "Cannot read #{filename}") if [:raise_on_missing] [:value_on_missing] rescue Timeout::Error raise(Error, "Cannot read #{filename} because it is locked") end |
.read_json(filename, options = {}) ⇒ Object
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/sugar_utils/file.rb', line 107 def self.read_json(filename, = {}) [:value_on_missing] = :missing read_result = read(filename, ) 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.
Touch the specified file.
129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/sugar_utils/file.rb', line 129 def self.touch(filename, = {}) = WriteOptions.new(filename, ) FileUtils.mkdir_p(::File.dirname(filename)) FileUtils.touch(filename, **.slice(:mtime)) change_access( filename, .owner, .group, .perm(nil) ) end |
.write(filename, data, options = {}) ⇒ void
Either option :mode or :perm can be used to specific the permissions
This method returns an undefined value.
Write to an existing file, overwriting it, or create the file if it does not exist.
on the file being written to. This aliasing is used because both these names are used in the standard library, File.open uses :perm and FileUtils uses :mode. The user can choose whichever alias makes their code most readable.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/sugar_utils/file.rb', line 164 def self.write(filename, data, = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize = WriteOptions.new(filename, ) FileUtils.mkdir_p(::File.dirname(filename)) ::File.open(filename, 'w+', .perm) do |file| flock_exclusive(file, ) 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 end change_access( filename, .owner, .group, .perm ) rescue Timeout::Error raise(Error, "Unable to write #{filename} because it is locked") rescue SystemCallError, IOError => e raise(Error, "Unable to write #{filename} with #{e}") end |
.write_json(filename, data, options = {}) ⇒ void
Either option :mode or :perm can be used to specific the permissions
This method returns an undefined value.
Write the data parameter as JSON to the filename path.
on the file being written to. This aliasing is used because both these names are used in the standard library, File.open uses :perm and FileUtils uses :mode. The user can choose whichever alias makes their code most readable.
280 281 282 |
# File 'lib/sugar_utils/file.rb', line 280 def self.write_json(filename, data, = {}) atomic_write(filename, MultiJson.dump(data, pretty: true), ) end |