dry-files 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +37 -0
- data/LICENSE +20 -0
- data/README.md +29 -0
- data/dry-files.gemspec +34 -0
- data/lib/dry-files.rb +3 -0
- data/lib/dry/files.rb +860 -0
- data/lib/dry/files/adapter.rb +21 -0
- data/lib/dry/files/error.rb +120 -0
- data/lib/dry/files/file_system.rb +377 -0
- data/lib/dry/files/memory_file_system.rb +429 -0
- data/lib/dry/files/memory_file_system/node.rb +246 -0
- data/lib/dry/files/path.rb +112 -0
- data/lib/dry/files/version.rb +7 -0
- metadata +102 -0
@@ -0,0 +1,429 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/files/path"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
class Files
|
7
|
+
# Memory File System abstraction to support `Dry::Files`
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
10
|
+
# @api private
|
11
|
+
class MemoryFileSystem
|
12
|
+
# @since 0.1.0
|
13
|
+
# @api private
|
14
|
+
EMPTY_CONTENT = nil
|
15
|
+
private_constant :EMPTY_CONTENT
|
16
|
+
|
17
|
+
require_relative "./memory_file_system/node"
|
18
|
+
|
19
|
+
# Creates a new instance
|
20
|
+
#
|
21
|
+
# @param root [Dry::Files::MemoryFileSystem::Node] the root node of the
|
22
|
+
# in-memory file system
|
23
|
+
#
|
24
|
+
# @return [Dry::Files::MemoryFileSystem]
|
25
|
+
#
|
26
|
+
# @since 0.1.0
|
27
|
+
# @api private
|
28
|
+
def initialize(root: Node.root)
|
29
|
+
@root = root
|
30
|
+
end
|
31
|
+
|
32
|
+
# Opens (or creates) a new file for read/write operations.
|
33
|
+
#
|
34
|
+
# @param path [String] the target file
|
35
|
+
# @yieldparam [Dry::Files::MemoryFileSystem::Node]
|
36
|
+
#
|
37
|
+
# @since 0.1.0
|
38
|
+
# @api private
|
39
|
+
def open(path, *, &blk)
|
40
|
+
file = touch(path)
|
41
|
+
blk.call(file)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Read file contents
|
45
|
+
#
|
46
|
+
# @param path [String, Array<String>] the target path
|
47
|
+
# @return [String] the file contents
|
48
|
+
#
|
49
|
+
# @raise [Dry::Files::IOError] in case the target path is a directory or
|
50
|
+
# if the file cannot be found
|
51
|
+
#
|
52
|
+
# @since 0.1.0
|
53
|
+
# @api private
|
54
|
+
def read(path)
|
55
|
+
path = Path[path]
|
56
|
+
raise IOError, Errno::EISDIR.new(path.to_s) if directory?(path)
|
57
|
+
|
58
|
+
file = find_file(path)
|
59
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if file.nil?
|
60
|
+
|
61
|
+
file.read
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reads the entire file specified by path as individual lines,
|
65
|
+
# and returns those lines in an array
|
66
|
+
#
|
67
|
+
# @param path [String, Array<String>] the target path
|
68
|
+
# @return [Array<String>] the file contents
|
69
|
+
#
|
70
|
+
# @raise [Dry::Files::IOError] in case the target path is a directory or
|
71
|
+
# if the file cannot be found
|
72
|
+
#
|
73
|
+
# @since 0.1.0
|
74
|
+
# @api private
|
75
|
+
def readlines(path)
|
76
|
+
path = Path[path]
|
77
|
+
node = find(path)
|
78
|
+
|
79
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if node.nil?
|
80
|
+
raise IOError, Errno::EISDIR.new(path.to_s) if node.directory?
|
81
|
+
|
82
|
+
node.readlines
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a file, if it doesn't exist, and set empty content.
|
86
|
+
#
|
87
|
+
# If the file was already existing, it's a no-op.
|
88
|
+
#
|
89
|
+
# @param path [String, Array<String>] the target path
|
90
|
+
#
|
91
|
+
# @raise [Dry::Files::IOError] in case the target path is a directory
|
92
|
+
#
|
93
|
+
# @since 0.1.0
|
94
|
+
# @api private
|
95
|
+
def touch(path)
|
96
|
+
path = Path[path]
|
97
|
+
raise IOError, Errno::EISDIR.new(path.to_s) if directory?(path)
|
98
|
+
|
99
|
+
content = read(path) if exist?(path)
|
100
|
+
write(path, content || EMPTY_CONTENT)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates a new file or rewrites the contents
|
104
|
+
# of an existing file for the given path and content
|
105
|
+
# All the intermediate directories are created.
|
106
|
+
#
|
107
|
+
# @param path [String, Array<String>] the target path
|
108
|
+
# @param content [String, Array<String>] the content to write
|
109
|
+
#
|
110
|
+
# @since 0.1.0
|
111
|
+
# @api private
|
112
|
+
def write(path, *content)
|
113
|
+
path = Path[path]
|
114
|
+
node = @root
|
115
|
+
|
116
|
+
for_each_segment(path) do |segment|
|
117
|
+
node = node.set(segment)
|
118
|
+
end
|
119
|
+
|
120
|
+
node.write(*content)
|
121
|
+
node
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns a new string formed by joining the strings using Operating
|
125
|
+
# System path separator
|
126
|
+
#
|
127
|
+
# @param path [String,Array<String>] path tokens
|
128
|
+
#
|
129
|
+
# @return [String] the joined path
|
130
|
+
#
|
131
|
+
# @since 0.1.0
|
132
|
+
# @api private
|
133
|
+
def join(*path)
|
134
|
+
Path[path]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Converts a path to an absolute path.
|
138
|
+
#
|
139
|
+
# @param path [String,Array<String>] the path to the file
|
140
|
+
# @param dir [String,Array<String>] the base directory
|
141
|
+
#
|
142
|
+
# @since 0.1.0
|
143
|
+
# @api private
|
144
|
+
def expand_path(path, dir)
|
145
|
+
return path if Path.absolute?(path)
|
146
|
+
|
147
|
+
join(dir, path)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the name of the current working directory.
|
151
|
+
#
|
152
|
+
# @return [String] the current working directory.
|
153
|
+
#
|
154
|
+
# @since 0.1.0
|
155
|
+
# @api private
|
156
|
+
def pwd
|
157
|
+
@root.segment
|
158
|
+
end
|
159
|
+
|
160
|
+
# Temporary changes the current working directory of the process to the
|
161
|
+
# given path and yield the given block.
|
162
|
+
#
|
163
|
+
# The argument `path` is intended to be a **directory**.
|
164
|
+
#
|
165
|
+
# @param path [String] the target directory
|
166
|
+
# @param blk [Proc] the code to execute with the target directory
|
167
|
+
#
|
168
|
+
# @raise [Dry::Files::IOError] if path cannot be found or it isn't a
|
169
|
+
# directory
|
170
|
+
#
|
171
|
+
# @since 0.1.0
|
172
|
+
# @api private
|
173
|
+
def chdir(path, &blk)
|
174
|
+
path = Path[path]
|
175
|
+
directory = find(path)
|
176
|
+
|
177
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if directory.nil?
|
178
|
+
raise IOError, Errno::ENOTDIR.new(path.to_s) unless directory.directory?
|
179
|
+
|
180
|
+
current_root = @root
|
181
|
+
@root = directory
|
182
|
+
blk.call
|
183
|
+
ensure
|
184
|
+
@root = current_root
|
185
|
+
end
|
186
|
+
|
187
|
+
# Creates a directory and all its parent directories.
|
188
|
+
#
|
189
|
+
# The argument `path` is intended to be a **directory** that you want to
|
190
|
+
# explicitly create.
|
191
|
+
#
|
192
|
+
# @see #mkdir_p
|
193
|
+
#
|
194
|
+
# @param path [String,Array<String>] the directory to create
|
195
|
+
#
|
196
|
+
# @raise [Dry::Files::IOError] in case path is an already existing file
|
197
|
+
#
|
198
|
+
# @since 0.1.0
|
199
|
+
# @api private
|
200
|
+
def mkdir(path)
|
201
|
+
path = Path[path]
|
202
|
+
node = @root
|
203
|
+
|
204
|
+
for_each_segment(path) do |segment|
|
205
|
+
node = node.set(segment)
|
206
|
+
raise IOError, Errno::EEXIST.new(path.to_s) if node.file?
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Creates a directory and all its parent directories.
|
211
|
+
#
|
212
|
+
# The argument `path` is intended to be a **file**, where its
|
213
|
+
# directory ancestors will be implicitly created.
|
214
|
+
#
|
215
|
+
# @see #mkdir
|
216
|
+
#
|
217
|
+
# @param path [String,Array<String>] the file that will be in the
|
218
|
+
# directories that this method creates
|
219
|
+
#
|
220
|
+
# @raise [Dry::Files::IOError] in case of I/O error
|
221
|
+
#
|
222
|
+
# @since 0.1.0
|
223
|
+
# @api private
|
224
|
+
def mkdir_p(path)
|
225
|
+
path = Path[path]
|
226
|
+
|
227
|
+
mkdir(
|
228
|
+
Path.dirname(path)
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Copies file content from `source` to `destination`
|
233
|
+
# All the intermediate `destination` directories are created.
|
234
|
+
#
|
235
|
+
# @param source [String,Array<String>] the file(s) or directory to copy
|
236
|
+
# @param destination [String,Array<String>] the directory destination
|
237
|
+
#
|
238
|
+
# @raise [Dry::Files::IOError] if source cannot be found
|
239
|
+
#
|
240
|
+
# @since 0.1.0
|
241
|
+
# @api private
|
242
|
+
def cp(source, destination)
|
243
|
+
content = read(source)
|
244
|
+
write(destination, content)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Removes (deletes) a file
|
248
|
+
#
|
249
|
+
# @param path [String,Array<String>] the file to remove
|
250
|
+
#
|
251
|
+
# @raise [Dry::Files::IOError] if path cannot be found or it's a directory
|
252
|
+
#
|
253
|
+
# @see #rm_rf
|
254
|
+
#
|
255
|
+
# @since 0.1.0
|
256
|
+
# @api private
|
257
|
+
def rm(path)
|
258
|
+
path = Path[path]
|
259
|
+
file = nil
|
260
|
+
parent = @root
|
261
|
+
node = @root
|
262
|
+
|
263
|
+
for_each_segment(path) do |segment|
|
264
|
+
break unless node
|
265
|
+
|
266
|
+
file = segment
|
267
|
+
parent = node
|
268
|
+
node = node.get(segment)
|
269
|
+
end
|
270
|
+
|
271
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if node.nil?
|
272
|
+
raise IOError, Errno::EPERM.new(path.to_s) if node.directory?
|
273
|
+
|
274
|
+
parent.unset(file)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Removes (deletes) a directory
|
278
|
+
#
|
279
|
+
# @param path [String,Array<String>] the directory to remove
|
280
|
+
#
|
281
|
+
# @raise [Dry::Files::IOError] if path cannot be found
|
282
|
+
#
|
283
|
+
# @see #rm
|
284
|
+
#
|
285
|
+
# @since 0.1.0
|
286
|
+
# @api private
|
287
|
+
def rm_rf(path)
|
288
|
+
path = Path[path]
|
289
|
+
file = nil
|
290
|
+
parent = @root
|
291
|
+
node = @root
|
292
|
+
|
293
|
+
for_each_segment(path) do |segment|
|
294
|
+
break unless node
|
295
|
+
|
296
|
+
file = segment
|
297
|
+
parent = node
|
298
|
+
node = node.get(segment)
|
299
|
+
end
|
300
|
+
|
301
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if node.nil?
|
302
|
+
|
303
|
+
parent.unset(file)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Sets node UNIX mode
|
307
|
+
#
|
308
|
+
# @param path [String,Array<String>] the path to the node
|
309
|
+
# @param mode [Integer] a UNIX mode, in base 2, 8, 10, or 16
|
310
|
+
#
|
311
|
+
# @raise [Dry::Files::IOError] if path cannot be found
|
312
|
+
#
|
313
|
+
# @since 0.1.0
|
314
|
+
# @api private
|
315
|
+
def chmod(path, mode)
|
316
|
+
path = Path[path]
|
317
|
+
node = find(path)
|
318
|
+
|
319
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if node.nil?
|
320
|
+
|
321
|
+
node.chmod = mode
|
322
|
+
end
|
323
|
+
|
324
|
+
# Gets node UNIX mode
|
325
|
+
#
|
326
|
+
# @param path [String,Array<String>] the path to the node
|
327
|
+
# @return [Integer] the UNIX mode
|
328
|
+
#
|
329
|
+
# @raise [Dry::Files::IOError] if path cannot be found
|
330
|
+
#
|
331
|
+
# @since 0.1.0
|
332
|
+
# @api private
|
333
|
+
def mode(path)
|
334
|
+
path = Path[path]
|
335
|
+
node = find(path)
|
336
|
+
|
337
|
+
raise IOError, Errno::ENOENT.new(path.to_s) if node.nil?
|
338
|
+
|
339
|
+
node.mode
|
340
|
+
end
|
341
|
+
|
342
|
+
# Check if the given path exist.
|
343
|
+
#
|
344
|
+
# @param path [String,Array<String>] the path to the node
|
345
|
+
# @return [TrueClass,FalseClass] the result of the check
|
346
|
+
#
|
347
|
+
# @since 0.1.0
|
348
|
+
# @api private
|
349
|
+
def exist?(path)
|
350
|
+
path = Path[path]
|
351
|
+
|
352
|
+
!find(path).nil?
|
353
|
+
end
|
354
|
+
|
355
|
+
# Check if the given path corresponds to a directory.
|
356
|
+
#
|
357
|
+
# @param path [String,Array<String>] the path to the directory
|
358
|
+
# @return [TrueClass,FalseClass] the result of the check
|
359
|
+
#
|
360
|
+
# @since 0.1.0
|
361
|
+
# @api private
|
362
|
+
def directory?(path)
|
363
|
+
path = Path[path]
|
364
|
+
!find_directory(path).nil?
|
365
|
+
end
|
366
|
+
|
367
|
+
# Check if the given path is an executable.
|
368
|
+
#
|
369
|
+
# @param path [String,Array<String>] the path to the node
|
370
|
+
# @return [TrueClass,FalseClass] the result of the check
|
371
|
+
#
|
372
|
+
# @since 0.1.0
|
373
|
+
# @api private
|
374
|
+
def executable?(path)
|
375
|
+
path = Path[path]
|
376
|
+
|
377
|
+
node = find(path)
|
378
|
+
return false if node.nil?
|
379
|
+
|
380
|
+
node.executable?
|
381
|
+
end
|
382
|
+
|
383
|
+
private
|
384
|
+
|
385
|
+
# @since 0.1.0
|
386
|
+
# @api private
|
387
|
+
def for_each_segment(path, &blk)
|
388
|
+
segments = Path.split(path)
|
389
|
+
segments.each(&blk)
|
390
|
+
end
|
391
|
+
|
392
|
+
# @since 0.1.0
|
393
|
+
# @api private
|
394
|
+
def find_directory(path)
|
395
|
+
node = find(path)
|
396
|
+
|
397
|
+
return if node.nil?
|
398
|
+
return unless node.directory?
|
399
|
+
|
400
|
+
node
|
401
|
+
end
|
402
|
+
|
403
|
+
# @since 0.1.0
|
404
|
+
# @api private
|
405
|
+
def find_file(path)
|
406
|
+
node = find(path)
|
407
|
+
|
408
|
+
return if node.nil?
|
409
|
+
return unless node.file?
|
410
|
+
|
411
|
+
node
|
412
|
+
end
|
413
|
+
|
414
|
+
# @since 0.1.0
|
415
|
+
# @api private
|
416
|
+
def find(path)
|
417
|
+
node = @root
|
418
|
+
|
419
|
+
for_each_segment(path) do |segment|
|
420
|
+
break unless node
|
421
|
+
|
422
|
+
node = node.get(segment)
|
423
|
+
end
|
424
|
+
|
425
|
+
node
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "English"
|
4
|
+
require "stringio"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
class Files
|
8
|
+
class MemoryFileSystem
|
9
|
+
# Memory file system node (directory or file)
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# File modes implementation inspired by https://www.calleluks.com/flags-bitmasks-and-unix-file-system-permissions-in-ruby/
|
15
|
+
class Node
|
16
|
+
# @since 0.1.0
|
17
|
+
# @api private
|
18
|
+
MODE_USER_READ = 0b100000000
|
19
|
+
private_constant :MODE_USER_READ
|
20
|
+
|
21
|
+
# @since 0.1.0
|
22
|
+
# @api private
|
23
|
+
MODE_USER_WRITE = 0b010000000
|
24
|
+
private_constant :MODE_USER_WRITE
|
25
|
+
|
26
|
+
# @since 0.1.0
|
27
|
+
# @api private
|
28
|
+
MODE_USER_EXECUTE = 0b001000000
|
29
|
+
private_constant :MODE_USER_EXECUTE
|
30
|
+
|
31
|
+
# @since 0.1.0
|
32
|
+
# @api private
|
33
|
+
MODE_GROUP_READ = 0b000100000
|
34
|
+
private_constant :MODE_GROUP_READ
|
35
|
+
|
36
|
+
# @since 0.1.0
|
37
|
+
# @api private
|
38
|
+
MODE_GROUP_WRITE = 0b000010000
|
39
|
+
private_constant :MODE_GROUP_WRITE
|
40
|
+
|
41
|
+
# @since 0.1.0
|
42
|
+
# @api private
|
43
|
+
MODE_GROUP_EXECUTE = 0b000001000
|
44
|
+
private_constant :MODE_GROUP_EXECUTE
|
45
|
+
|
46
|
+
# @since 0.1.0
|
47
|
+
# @api private
|
48
|
+
MODE_OTHERS_READ = 0b000000100
|
49
|
+
private_constant :MODE_OTHERS_READ
|
50
|
+
|
51
|
+
# @since 0.1.0
|
52
|
+
# @api private
|
53
|
+
MODE_OTHERS_WRITE = 0b000000010
|
54
|
+
private_constant :MODE_OTHERS_WRITE
|
55
|
+
|
56
|
+
# @since 0.1.0
|
57
|
+
# @api private
|
58
|
+
MODE_OTHERS_EXECUTE = 0b000000001
|
59
|
+
private_constant :MODE_OTHERS_EXECUTE
|
60
|
+
|
61
|
+
# Default directory mode: 0755
|
62
|
+
#
|
63
|
+
# @since 0.1.0
|
64
|
+
# @api private
|
65
|
+
DEFAULT_DIRECTORY_MODE = MODE_USER_READ | MODE_USER_WRITE | MODE_USER_EXECUTE |
|
66
|
+
MODE_GROUP_READ | MODE_GROUP_EXECUTE |
|
67
|
+
MODE_OTHERS_READ | MODE_GROUP_EXECUTE
|
68
|
+
private_constant :DEFAULT_DIRECTORY_MODE
|
69
|
+
|
70
|
+
# Default file mode: 0644
|
71
|
+
#
|
72
|
+
# @since 0.1.0
|
73
|
+
# @api private
|
74
|
+
DEFAULT_FILE_MODE = MODE_USER_READ | MODE_USER_WRITE | MODE_GROUP_READ | MODE_OTHERS_READ
|
75
|
+
private_constant :DEFAULT_FILE_MODE
|
76
|
+
|
77
|
+
# @since 0.1.0
|
78
|
+
# @api private
|
79
|
+
MODE_BASE = 16
|
80
|
+
private_constant :MODE_BASE
|
81
|
+
|
82
|
+
# @since 0.1.0
|
83
|
+
# @api private
|
84
|
+
ROOT_PATH = "/"
|
85
|
+
private_constant :ROOT_PATH
|
86
|
+
|
87
|
+
# Instantiate a root node
|
88
|
+
#
|
89
|
+
# @return [Dry::Files::MemoryFileSystem::Node] the root node
|
90
|
+
#
|
91
|
+
# @since 0.1.0
|
92
|
+
# @api private
|
93
|
+
def self.root
|
94
|
+
new(ROOT_PATH)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @since 0.1.0
|
98
|
+
# @api private
|
99
|
+
attr_reader :segment, :mode
|
100
|
+
|
101
|
+
# Instantiate a new node.
|
102
|
+
# It's a directory node by default.
|
103
|
+
#
|
104
|
+
# @param segment [String] the path segment of the node
|
105
|
+
# @param mode [Integer] the UNIX mode
|
106
|
+
#
|
107
|
+
# @return [Dry::Files::MemoryFileSystem::Node] the new node
|
108
|
+
#
|
109
|
+
# @see #mode=
|
110
|
+
#
|
111
|
+
# @since 0.1.0
|
112
|
+
# @api private
|
113
|
+
def initialize(segment, mode = DEFAULT_DIRECTORY_MODE)
|
114
|
+
@segment = segment
|
115
|
+
@children = nil
|
116
|
+
@content = nil
|
117
|
+
|
118
|
+
self.chmod = mode
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get a node child
|
122
|
+
#
|
123
|
+
# @param segment [String] the child path segment
|
124
|
+
#
|
125
|
+
# @return [Dry::Files::MemoryFileSystem::Node,NilClass] the child node, if found
|
126
|
+
#
|
127
|
+
# @since 0.1.0
|
128
|
+
# @api private
|
129
|
+
def get(segment)
|
130
|
+
@children&.fetch(segment, nil)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Set a node child
|
134
|
+
#
|
135
|
+
# @param segment [String] the child path segment
|
136
|
+
#
|
137
|
+
# @since 0.1.0
|
138
|
+
# @api private
|
139
|
+
def set(segment)
|
140
|
+
@children ||= {}
|
141
|
+
@children[segment] ||= self.class.new(segment)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Unset a node child
|
145
|
+
#
|
146
|
+
# @param segment [String] the child path segment
|
147
|
+
#
|
148
|
+
# @raise [Dry::Files::UnknownMemoryNodeError] if the child node cannot be found
|
149
|
+
#
|
150
|
+
# @since 0.1.0
|
151
|
+
# @api private
|
152
|
+
def unset(segment)
|
153
|
+
@children ||= {}
|
154
|
+
raise UnknownMemoryNodeError, segment unless @children.key?(segment)
|
155
|
+
|
156
|
+
@children.delete(segment)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Check if node is a directory
|
160
|
+
#
|
161
|
+
# @return [TrueClass,FalseClass] the result of the check
|
162
|
+
#
|
163
|
+
# @since 0.1.0
|
164
|
+
# @api private
|
165
|
+
def directory?
|
166
|
+
!file?
|
167
|
+
end
|
168
|
+
|
169
|
+
# Check if node is a file
|
170
|
+
#
|
171
|
+
# @return [TrueClass,FalseClass] the result of the check
|
172
|
+
#
|
173
|
+
# @since 0.1.0
|
174
|
+
# @api private
|
175
|
+
def file?
|
176
|
+
!@content.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
# Read file contents
|
180
|
+
#
|
181
|
+
# @return [String] the file contents
|
182
|
+
#
|
183
|
+
# @raise [Dry::Files::NotMemoryFileError] if node isn't a file
|
184
|
+
#
|
185
|
+
# @since 0.1.0
|
186
|
+
# @api private
|
187
|
+
def read
|
188
|
+
raise NotMemoryFileError, segment unless file?
|
189
|
+
|
190
|
+
@content.rewind
|
191
|
+
@content.read
|
192
|
+
end
|
193
|
+
|
194
|
+
# Read file content lines
|
195
|
+
#
|
196
|
+
# @return [Array<String>] the file content lines
|
197
|
+
#
|
198
|
+
# @raise [Dry::Files::NotMemoryFileError] if node isn't a file
|
199
|
+
#
|
200
|
+
# @since 0.1.0
|
201
|
+
# @api private
|
202
|
+
def readlines
|
203
|
+
raise NotMemoryFileError, segment unless file?
|
204
|
+
|
205
|
+
@content.rewind
|
206
|
+
@content.readlines
|
207
|
+
end
|
208
|
+
|
209
|
+
# Write file contents
|
210
|
+
# IMPORTANT: This operation turns a node into a file
|
211
|
+
#
|
212
|
+
# @param content [String, Array<String>] the file content
|
213
|
+
#
|
214
|
+
# @raise [Dry::Files::NotMemoryFileError] if node isn't a file
|
215
|
+
#
|
216
|
+
# @since 0.1.0
|
217
|
+
# @api private
|
218
|
+
def write(*content)
|
219
|
+
@content = StringIO.new(content.join($RS))
|
220
|
+
@mode = DEFAULT_FILE_MODE
|
221
|
+
end
|
222
|
+
|
223
|
+
# Set UNIX mode
|
224
|
+
# It accepts base 2, 8, 10, and 16 numbers
|
225
|
+
#
|
226
|
+
# @param mode [Integer] the file mode
|
227
|
+
#
|
228
|
+
# @since 0.1.0
|
229
|
+
# @api private
|
230
|
+
def chmod=(mode)
|
231
|
+
@mode = mode.to_s(MODE_BASE).hex
|
232
|
+
end
|
233
|
+
|
234
|
+
# Check if node is executable for user
|
235
|
+
#
|
236
|
+
# @return [TrueClass,FalseClass] the result of the check
|
237
|
+
#
|
238
|
+
# @since 0.1.0
|
239
|
+
# @api private
|
240
|
+
def executable?
|
241
|
+
(mode & MODE_USER_EXECUTE).positive?
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|