rubypath 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 132e274110300f64504251d3ff47c352bbfe15c8
4
- data.tar.gz: cc3628865507096713d6e783af1b24f1235e0ff1
3
+ metadata.gz: 5e7333efb5dc342354d834cbbca3ce7fd6bc4e77
4
+ data.tar.gz: fa82812dd3e0b30f7989b7677f2b5f3d9e02e041
5
5
  SHA512:
6
- metadata.gz: f52446607fddc79bd5da72f4e77eacfb282f3d817ae7331740513b694ccf6a905760897023801b9ce96d41119bc26f701f37ca12fb9400a53904b3586ed77f38
7
- data.tar.gz: 30bda5d8a6d2ae40e83c040d0ad95391ec6e59f858e0e9bb636c964b6a6298fb60aa29eb081ad33647c5630a37ff31400f2c10d8eedc7ae03ef0fa3188d68090
6
+ metadata.gz: c57d75be4a166f7ed70675668de9bb1a9284097b11006be7e5ebb0fd173e2f937582c4a2a974be25c360886ee764be5b7fc921d258a1273f54ff26b8f377dc21
7
+ data.tar.gz: 9d5ae45a83991d9feaf746d64fd8b5a8e908637fb6a6e1515eb6a440f7be09a5ac6acd378bac56c2750c1a844431311e5cbaa800ddcc26d099d45ee8651b64bd
@@ -0,0 +1,33 @@
1
+ # Changes
2
+
3
+ ## 1.0.1
4
+
5
+ * Fix issues with gem packaging
6
+
7
+ ## 1.0.0
8
+
9
+ * Dump to semantic versioning
10
+ * Internal code style changes
11
+
12
+ ## 0.3.2
13
+
14
+ * Fix infinite loop bug in #relative_from
15
+ * Fix handling of trailing slash in #cleanpath (and depended methods)
16
+
17
+ ## 0.3.1
18
+
19
+ * Fix missing require 'tmpdir' for using with mocked backend
20
+
21
+ ## 0.3.0
22
+
23
+ * Add #unlink
24
+ * Add #cleanpath
25
+ * Add #rmtree, #rmtree!, and safe variants
26
+
27
+ ## 0.2.0
28
+
29
+ * Add #relative_from
30
+
31
+ ## 0.1.0
32
+
33
+ * Initial release
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubypath/version'
4
+ require 'tmpdir'
5
+
6
+ #
7
+ class Path
8
+ require 'rubypath/identity'
9
+ require 'rubypath/construction'
10
+ require 'rubypath/comparison'
11
+ require 'rubypath/extensions'
12
+
13
+ require 'rubypath/path_operations'
14
+ require 'rubypath/path_predicates'
15
+ require 'rubypath/file_operations'
16
+ require 'rubypath/file_predicates'
17
+ require 'rubypath/dir_operations'
18
+ require 'rubypath/io_operations'
19
+
20
+ require 'rubypath/mock'
21
+ require 'rubypath/backend'
22
+ end
23
+
24
+ module Kernel
25
+ # rubocop:disable Style/MethodName
26
+ def Path(*args)
27
+ Path.new(*args)
28
+ end
29
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ class Backend
5
+ class << self
6
+ def instance
7
+ @instance ||= new
8
+ end
9
+
10
+ def delegate(mth)
11
+ define_method mth do |*args|
12
+ backend.send mth, *args
13
+ end
14
+ end
15
+
16
+ def mock(*args, &block)
17
+ instance.mock(*args, &block)
18
+ end
19
+ end
20
+
21
+ attr_accessor :backend
22
+ def initialize
23
+ self.backend = Backend::Sys.new
24
+ end
25
+
26
+ # rubocop:disable Metrics/MethodLength
27
+ def mock(opts = {}, &block)
28
+ if opts[:root]
29
+ # Use real file system scoped to given directory (chroot like)
30
+ if opts[:root] == :tmp
31
+ ::Dir.mktmpdir('rubypath') do |path|
32
+ use_backend Backend::Sys.new(path), &block
33
+ end
34
+ else
35
+ use_backend Backend::Sys.new(opts[:root]), &block
36
+ end
37
+ else
38
+ # Use mock FS
39
+ use_backend Backend::Mock.new, &block
40
+ end
41
+ end
42
+ # rubocop:enable Metrics/MethodLength
43
+
44
+ def use_backend(be)
45
+ old_backend = backend
46
+ self.backend = be
47
+ yield
48
+ backend.quit if backend.respond_to? :quit
49
+ self.backend = old_backend
50
+ end
51
+
52
+ delegate :expand_path
53
+ delegate :getwd
54
+ delegate :exists?
55
+ delegate :mkdir
56
+ delegate :mkpath
57
+ delegate :directory?
58
+ delegate :file?
59
+ delegate :touch
60
+ delegate :write
61
+ delegate :read
62
+ delegate :mtime
63
+ delegate :mtime=
64
+ delegate :entries
65
+ delegate :glob
66
+ delegate :atime
67
+ delegate :atime=
68
+ delegate :umask
69
+ delegate :umask=
70
+ delegate :mode
71
+ delegate :chmod
72
+ delegate :unlink
73
+ delegate :rmtree
74
+ delegate :rmtree!
75
+ delegate :safe_rmtree
76
+ delegate :safe_rmtree!
77
+ end
78
+
79
+ private
80
+
81
+ def invoke_backend(mth, *args)
82
+ args << self if args.empty?
83
+ self.class.send :invoke_backend, mth, *args
84
+ end
85
+
86
+ class << self
87
+ private
88
+
89
+ def invoke_backend(mth, *args)
90
+ Backend.instance.send mth, *args
91
+ end
92
+ end
93
+
94
+ require 'rubypath/backend/mock'
95
+ require 'rubypath/backend/sys'
96
+ end
@@ -0,0 +1,362 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Path
4
+ class Backend
5
+ # rubocop:disable Metrics/ClassLength
6
+ class Mock
7
+ attr_reader :user, :homes
8
+
9
+ # @!group Virtual File System Configuration
10
+
11
+ # Set user that owns the current process.
12
+ def current_user=(user)
13
+ @user = user.to_s
14
+ end
15
+
16
+ # Define new home directories. Must be given as has a hash that is
17
+ # interpreted as a user name to home directory mapping.
18
+ attr_writer :homes
19
+
20
+ # Set current working directory.
21
+ attr_writer :cwd
22
+
23
+ # @!group Internal Methods
24
+
25
+ def initialize
26
+ @user = 'root'
27
+ @homes = {'root' => '/root'}
28
+ @cwd = '/root'
29
+ @umask = 0o022
30
+ end
31
+
32
+ def home(user)
33
+ homes.fetch(user) do
34
+ raise ArgumentError.new "user #{user} doesn't exist"
35
+ end
36
+ end
37
+
38
+ # @!group Backend Operations
39
+
40
+ def expand_path(path, base = getwd)
41
+ # rubocop:disable RegexpMatch
42
+ if %r{^~(?<name>[^/]*)(/(?<rest>.*))?$} =~ path
43
+ ::File.expand_path rest.to_s, home(name.empty? ? user : name)
44
+ else
45
+ ::File.expand_path(path, base)
46
+ end
47
+ end
48
+ alias expand expand_path
49
+ # rubocop:enable all
50
+
51
+ def getwd
52
+ @cwd ||= '/'
53
+ end
54
+
55
+ def file?(path)
56
+ lookup(path).is_a?(File)
57
+ end
58
+
59
+ def directory?(path)
60
+ lookup(path).is_a?(Dir)
61
+ end
62
+
63
+ def exists?(path)
64
+ lookup(path) ? true : false
65
+ end
66
+
67
+ def mkdir(path)
68
+ return if path.to_s == '/'
69
+
70
+ node = lookup_parent! path
71
+ dir = node.lookup ::File.basename path
72
+ return if dir.is_a?(Dir)
73
+
74
+ if dir.nil?
75
+ node.add Dir.new(self, ::File.basename(path))
76
+ else
77
+ raise ArgumentError.new \
78
+ "Node #{dir.path} exists and is no directory."
79
+ end
80
+ end
81
+
82
+ def mkpath(path)
83
+ path = expand_path(path)
84
+ ::Pathname.new(path).descend do |p|
85
+ mkdir(p.to_s)
86
+ end
87
+ end
88
+
89
+ def touch(path)
90
+ node = lookup_parent! path
91
+ file = node.lookup ::File.basename path
92
+ if file
93
+ file.mtime = Time.now
94
+ else
95
+ node.add File.new(self, ::File.basename(path))
96
+ end
97
+ end
98
+
99
+ def write(path, content, *args) # rubocop:disable AbcSize, MethodLength
100
+ node = lookup_parent! path
101
+ file = node.lookup ::File.basename(path)
102
+ unless file
103
+ file = File.new self, ::File.basename(path)
104
+ node.add file
105
+ end
106
+
107
+ case file
108
+ when File
109
+ if args.empty?
110
+ file.content = String.new(content)
111
+ else
112
+ offset = args[0].to_i
113
+ file.content[offset, content.length] = content
114
+ end
115
+ file.mtime = Time.now
116
+ when Dir
117
+ raise Errno::EISDIR.new path
118
+ else
119
+ raise ArgumentError.new
120
+ end
121
+ end
122
+
123
+ def mtime(path)
124
+ lookup!(path).mtime
125
+ end
126
+
127
+ def mtime=(path, time)
128
+ lookup!(path).mtime = time
129
+ end
130
+
131
+ def atime(path)
132
+ lookup!(path).atime
133
+ end
134
+
135
+ def atime=(path, time)
136
+ lookup!(path).atime = time
137
+ end
138
+
139
+ def read(path, *args)
140
+ file = lookup_file!(path)
141
+ file.atime = Time.now
142
+ content = file.content
143
+ if args[0]
144
+ length = args[0].to_i
145
+ offset = args[1] ? args[1].to_i : 0
146
+ content = content.slice(offset, length)
147
+ end
148
+ content
149
+ end
150
+
151
+ def entries(path)
152
+ node = lookup_dir! path
153
+ node.children.map(&:name) + %w[. ..]
154
+ end
155
+
156
+ def glob(pattern, flags = 0)
157
+ root.all.select do |node|
158
+ ::File.fnmatch pattern, node.path, (flags | ::File::FNM_PATHNAME)
159
+ end
160
+ end
161
+
162
+ attr_reader :umask
163
+
164
+ def umask=(mask)
165
+ @umask = Integer(mask)
166
+ end
167
+
168
+ def mode(path)
169
+ lookup!(path).mode
170
+ end
171
+
172
+ def unlink(path) # rubocop:disable MethodLength
173
+ node = lookup_parent!(path)
174
+ file = node.lookup ::File.basename path
175
+ case file
176
+ when Dir
177
+ raise Errno::EISDIR.new path
178
+ when File
179
+ node.children.delete(file)
180
+ when nil
181
+ raise Errno::ENOENT.new path
182
+ else
183
+ raise ArgumentError.new "Unknown node #{node.inspect} for #unlink."
184
+ end
185
+ end
186
+
187
+ def rmtree(path)
188
+ node = lookup path
189
+ case node
190
+ when Dir, File
191
+ lookup_parent!(path).children.delete(node)
192
+ when nil
193
+ nil
194
+ else
195
+ raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
196
+ end
197
+ end
198
+ alias safe_rmtree rmtree
199
+
200
+ def rmtree!(path)
201
+ node = lookup path
202
+ case node
203
+ when Dir, File
204
+ lookup_parent!(path).children.delete(node)
205
+ when nil
206
+ raise Errno::ENOENT.new path
207
+ else
208
+ raise ArgumentError.new "Unknown node #{node.inspect} for #rmtree."
209
+ end
210
+ end
211
+ alias safe_rmtree! rmtree!
212
+
213
+ # @!group Internal Virtual File System
214
+
215
+ # Return root node.
216
+ def root
217
+ @root ||= Dir.new(self, '')
218
+ end
219
+
220
+ def to_lookup(path)
221
+ path = expand path
222
+ path.sub(%r{^/+}, '')
223
+ end
224
+
225
+ def lookup(path)
226
+ root.lookup to_lookup path
227
+ end
228
+
229
+ def lookup!(path)
230
+ node = lookup path
231
+ return node if node
232
+
233
+ raise Errno::ENOENT.new path
234
+ end
235
+
236
+ def lookup_file!(path)
237
+ node = lookup! path
238
+ case node
239
+ when File
240
+ node
241
+ when Dir
242
+ raise Errno::EISDIR.new path
243
+ else
244
+ raise ArgumentError.new "NOT A FILE: #{path}"
245
+ end
246
+ end
247
+
248
+ def lookup_dir!(path)
249
+ node = lookup! path
250
+ return node if node.is_a?(Dir)
251
+
252
+ raise Errno::ENOENT.new path
253
+ end
254
+
255
+ def lookup_parent!(path)
256
+ node = lookup ::File.dirname expand path
257
+
258
+ if node && node.is_a?(Dir)
259
+ node
260
+ elsif node
261
+ raise Errno::ENOTDIR.new path
262
+ else
263
+ raise Errno::ENOENT.new path
264
+ end
265
+ end
266
+
267
+ #
268
+ class Node
269
+ attr_reader :sys, :name, :parent
270
+ attr_accessor :mtime, :atime, :mode
271
+
272
+ def initialize(backend, name, _ops = {})
273
+ @sys = backend
274
+ @name = name
275
+ @mtime = Time.now
276
+ @atime = Time.now
277
+ end
278
+
279
+ def mtime=(time)
280
+ if time.is_a?(Time)
281
+ @mtime = time
282
+ else
283
+ raise "Not Time but `#{time.inspect}` " \
284
+ "of `#{time.class.name}` given."
285
+ end
286
+ end
287
+
288
+ def lookup(_path)
289
+ raise NotImplementError.new 'Subclass responsibility.'
290
+ end
291
+
292
+ def added(parent)
293
+ @parent = parent
294
+ end
295
+
296
+ def path
297
+ parent ? "#{parent.path}/#{name}" : name
298
+ end
299
+ end
300
+
301
+ #
302
+ class Dir < Node
303
+ def initialize(backend, name, opts = {})
304
+ super
305
+ self.mode = 0o777 - backend.umask
306
+ end
307
+
308
+ def lookup(path) # rubocop:disable MethodLength
309
+ name, rest = path.to_s.split('/', 2).map(&:to_s)
310
+
311
+ if name.nil?
312
+ if rest.nil?
313
+ self
314
+ else
315
+ lookup rest
316
+ end
317
+ else
318
+ child = children.find {|c| c.name == name }
319
+ if child
320
+ rest.nil? ? child : child.lookup(rest)
321
+ end
322
+ end
323
+ end
324
+
325
+ def add(node)
326
+ if children.any? {|c| c.name == node.name }
327
+ raise ArgumentError.new "Node #{path}/#{node.name} already exists."
328
+ end
329
+
330
+ children << node
331
+ node.added self
332
+ end
333
+
334
+ def all
335
+ children.reduce([]) do |memo, child|
336
+ memo << child
337
+ memo += child.all if child.is_a?(Dir)
338
+ memo
339
+ end
340
+ end
341
+
342
+ def children
343
+ @children ||= []
344
+ end
345
+ end
346
+
347
+ #
348
+ class File < Node
349
+ attr_accessor :content
350
+
351
+ def initialize(backend, name, opts = {})
352
+ super
353
+ self.mode = 0o666 - backend.umask
354
+ end
355
+
356
+ def lookup(_path)
357
+ nil
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end