fssm 0.0.8
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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.markdown +55 -0
- data/Rakefile +69 -0
- data/VERSION.yml +4 -0
- data/example.rb +9 -0
- data/fssm.gemspec +77 -0
- data/lib/fssm.rb +40 -0
- data/lib/fssm/backends/fsevents.rb +37 -0
- data/lib/fssm/backends/polling.rb +26 -0
- data/lib/fssm/fsevents.rb +129 -0
- data/lib/fssm/monitor.rb +25 -0
- data/lib/fssm/path.rb +91 -0
- data/lib/fssm/pathname.rb +350 -0
- data/lib/fssm/state.rb +54 -0
- data/lib/fssm/support.rb +22 -0
- data/lib/fssm/tree.rb +176 -0
- data/profile/prof-cache.rb +40 -0
- data/profile/prof-fssm-pathname.html +1119 -0
- data/profile/prof-pathname.rb +68 -0
- data/profile/prof-plain-pathname.html +909 -0
- data/profile/prof.html +2163 -0
- data/spec/path_spec.rb +75 -0
- data/spec/root/duck/quack.txt +0 -0
- data/spec/root/file.css +0 -0
- data/spec/root/file.rb +0 -0
- data/spec/root/file.yml +0 -0
- data/spec/root/moo/cow.txt +0 -0
- data/spec/spec_helper.rb +14 -0
- metadata +106 -0
data/lib/fssm/monitor.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class FSSM::Monitor
|
2
|
+
def initialize(options={})
|
3
|
+
@options = options
|
4
|
+
@backend = FSSM::Backends::Default.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def path(*args, &block)
|
8
|
+
path = FSSM::Path.new(*args)
|
9
|
+
|
10
|
+
if block_given?
|
11
|
+
if block.arity == 1
|
12
|
+
block.call(path)
|
13
|
+
else
|
14
|
+
path.instance_eval(&block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
@backend.add_path(path)
|
19
|
+
path
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
@backend.run
|
24
|
+
end
|
25
|
+
end
|
data/lib/fssm/path.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
class FSSM::Path
|
2
|
+
def initialize(path=nil, glob=nil, &block)
|
3
|
+
set_path(path || '.')
|
4
|
+
set_glob(glob || '**/*')
|
5
|
+
init_callbacks
|
6
|
+
|
7
|
+
if block_given?
|
8
|
+
if block.arity == 1
|
9
|
+
block.call(self)
|
10
|
+
else
|
11
|
+
self.instance_eval(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@path.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_pathname
|
21
|
+
@path
|
22
|
+
end
|
23
|
+
|
24
|
+
def glob(value=nil)
|
25
|
+
return @glob if value.nil?
|
26
|
+
set_glob(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(callback_or_path=nil, &block)
|
30
|
+
callback_action(:create, (block_given? ? block : callback_or_path))
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(callback_or_path=nil, &block)
|
34
|
+
callback_action(:update, (block_given? ? block : callback_or_path))
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(callback_or_path=nil, &block)
|
38
|
+
callback_action(:delete, (block_given? ? block : callback_or_path))
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def init_callbacks
|
44
|
+
do_nothing = lambda {|base, relative|}
|
45
|
+
@callbacks = Hash.new(do_nothing)
|
46
|
+
end
|
47
|
+
|
48
|
+
def callback_action(type, arg=nil)
|
49
|
+
if arg.is_a?(Proc)
|
50
|
+
set_callback(type, arg)
|
51
|
+
elsif arg.nil?
|
52
|
+
get_callback(type)
|
53
|
+
else
|
54
|
+
run_callback(type, arg)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_callback(type, arg)
|
59
|
+
raise ArgumentError, "Proc expected" unless arg.is_a?(Proc)
|
60
|
+
@callbacks[type] = arg
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_callback(type)
|
64
|
+
@callbacks[type]
|
65
|
+
end
|
66
|
+
|
67
|
+
def run_callback(type, arg)
|
68
|
+
base, relative = split_path(arg)
|
69
|
+
|
70
|
+
begin
|
71
|
+
@callbacks[type].call(base, relative)
|
72
|
+
rescue Exception => e
|
73
|
+
raise FSSM::CallbackError, "#{type} - #{base.join(relative)}: #{e.message}", e.backtrace
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def split_path(path)
|
78
|
+
path = FSSM::Pathname.for(path)
|
79
|
+
[@path, (path.relative? ? path : path.relative_path_from(@path))]
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_path(path)
|
83
|
+
path = FSSM::Pathname.for(path)
|
84
|
+
raise FSSM::FileNotFoundError, "#{path}" unless path.exist?
|
85
|
+
@path = path.expand_path
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_glob(glob)
|
89
|
+
@glob = glob.is_a?(Array) ? glob : [glob]
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
# The bundled ruby pathname library is a slow and hideous beast.
|
2
|
+
# There. I said it. This version is based on pathname3.
|
3
|
+
|
4
|
+
module FSSM
|
5
|
+
class Pathname < String
|
6
|
+
|
7
|
+
SEPARATOR = Regexp.quote(File::SEPARATOR)
|
8
|
+
|
9
|
+
if File::ALT_SEPARATOR
|
10
|
+
ALT_SEPARATOR = Regexp.quote(File::ALT_SEPARATOR)
|
11
|
+
SEPARATOR_PAT = Regexp.compile("[#{SEPARATOR}#{ALT_SEPARATOR}]")
|
12
|
+
else
|
13
|
+
SEPARATOR_PAT = Regexp.compile(SEPARATOR)
|
14
|
+
end
|
15
|
+
|
16
|
+
if RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/
|
17
|
+
PREFIX_PAT = Regexp.compile("^([A-Za-z]:#{SEPARATOR_PAT})")
|
18
|
+
else
|
19
|
+
PREFIX_PAT = Regexp.compile("^(#{SEPARATOR_PAT})")
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def for(path)
|
24
|
+
path = path.is_a?(::FSSM::Pathname) ? path : new(path)
|
25
|
+
path.dememo
|
26
|
+
path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(path)
|
31
|
+
if path =~ %r{\0}
|
32
|
+
raise ArgumentError, "path cannot contain ASCII NULLs"
|
33
|
+
end
|
34
|
+
|
35
|
+
dememo
|
36
|
+
|
37
|
+
super(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_path
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"#{self}"
|
46
|
+
end
|
47
|
+
alias to_str to_s
|
48
|
+
|
49
|
+
def to_a
|
50
|
+
return @segments if @segments
|
51
|
+
set_prefix_and_names
|
52
|
+
@segments = @names.dup
|
53
|
+
@segments.delete('.')
|
54
|
+
@segments.unshift(@prefix) unless @prefix.empty?
|
55
|
+
@segments
|
56
|
+
end
|
57
|
+
alias segments to_a
|
58
|
+
|
59
|
+
def each_filename(&block)
|
60
|
+
to_a.each(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def ascend
|
64
|
+
parts = to_a
|
65
|
+
parts.length.downto(1) do |i|
|
66
|
+
yield self.class.join(parts[0, i])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def descend
|
71
|
+
parts = to_a
|
72
|
+
1.upto(parts.length) do |i|
|
73
|
+
yield self.class.join(parts[0, i])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def root?
|
78
|
+
set_prefix_and_names
|
79
|
+
@names.empty? && !@prefix.empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
def parent
|
83
|
+
self + '..'
|
84
|
+
end
|
85
|
+
|
86
|
+
def relative?
|
87
|
+
set_prefix_and_names
|
88
|
+
@prefix.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
def absolute?
|
92
|
+
!relative?
|
93
|
+
end
|
94
|
+
|
95
|
+
def +(path)
|
96
|
+
dup << path
|
97
|
+
end
|
98
|
+
|
99
|
+
def <<(path)
|
100
|
+
replace(join(path).cleanpath!)
|
101
|
+
end
|
102
|
+
|
103
|
+
def cleanpath!
|
104
|
+
parts = to_a
|
105
|
+
final = []
|
106
|
+
|
107
|
+
parts.each do |part|
|
108
|
+
case part
|
109
|
+
when '.' then next
|
110
|
+
when '..' then
|
111
|
+
case final.last
|
112
|
+
when '..' then final.push('..')
|
113
|
+
when nil then final.push('..')
|
114
|
+
else final.pop
|
115
|
+
end
|
116
|
+
else final.push(part)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
replace(final.empty? ? Dir.pwd : File.join(*final))
|
121
|
+
end
|
122
|
+
|
123
|
+
def cleanpath
|
124
|
+
dup.cleanpath!
|
125
|
+
end
|
126
|
+
|
127
|
+
def realpath
|
128
|
+
raise unless self.exist?
|
129
|
+
|
130
|
+
if File.symlink?(self)
|
131
|
+
file = self.dup
|
132
|
+
|
133
|
+
while true
|
134
|
+
file = File.join(File.dirname(file), File.readlink(file))
|
135
|
+
break unless File.symlink?(file)
|
136
|
+
end
|
137
|
+
|
138
|
+
self.class.new(file).clean
|
139
|
+
else
|
140
|
+
self.class.new(Dir.pwd) + self
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def relative_path_from(base)
|
145
|
+
base = self.class.for(base)
|
146
|
+
|
147
|
+
if self.absolute? != base.absolute?
|
148
|
+
raise ArgumentError, 'no relative path between a relative and absolute'
|
149
|
+
end
|
150
|
+
|
151
|
+
if self.prefix != base.prefix
|
152
|
+
raise ArgumentError, "different prefix: #{@prefix.inspect} and #{base.prefix.inspect}"
|
153
|
+
end
|
154
|
+
|
155
|
+
base = base.cleanpath!.segments
|
156
|
+
dest = dup.cleanpath!.segments
|
157
|
+
|
158
|
+
while !dest.empty? && !base.empty? && dest[0] == base[0]
|
159
|
+
base.shift
|
160
|
+
dest.shift
|
161
|
+
end
|
162
|
+
|
163
|
+
base.shift if base[0] == '.'
|
164
|
+
dest.shift if dest[0] == '.'
|
165
|
+
|
166
|
+
if base.include?('..')
|
167
|
+
raise ArgumentError, "base directory may not contain '..'"
|
168
|
+
end
|
169
|
+
|
170
|
+
path = base.fill('..') + dest
|
171
|
+
path = self.class.join(*path)
|
172
|
+
path = self.class.new('.') if path.empty?
|
173
|
+
|
174
|
+
path
|
175
|
+
end
|
176
|
+
|
177
|
+
def replace(path)
|
178
|
+
if path =~ %r{\0}
|
179
|
+
raise ArgumentError, "path cannot contain ASCII NULLs"
|
180
|
+
end
|
181
|
+
|
182
|
+
dememo
|
183
|
+
|
184
|
+
super(path)
|
185
|
+
end
|
186
|
+
|
187
|
+
def unlink
|
188
|
+
Dir.unlink(self)
|
189
|
+
true
|
190
|
+
rescue Errno::ENOTDIR
|
191
|
+
File.unlink(self)
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
def prefix
|
196
|
+
set_prefix_and_names
|
197
|
+
@prefix
|
198
|
+
end
|
199
|
+
|
200
|
+
def names
|
201
|
+
set_prefix_and_names
|
202
|
+
@names
|
203
|
+
end
|
204
|
+
|
205
|
+
def dememo
|
206
|
+
@set = nil
|
207
|
+
@segments = nil
|
208
|
+
@prefix = nil
|
209
|
+
@names = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def set_prefix_and_names
|
215
|
+
return if @set
|
216
|
+
|
217
|
+
@names = []
|
218
|
+
|
219
|
+
if (match = PREFIX_PAT.match(self))
|
220
|
+
@prefix = match[0].to_s
|
221
|
+
@names += match.post_match.split(SEPARATOR_PAT)
|
222
|
+
else
|
223
|
+
@prefix = ''
|
224
|
+
@names += self.split(SEPARATOR_PAT)
|
225
|
+
end
|
226
|
+
|
227
|
+
@names.compact!
|
228
|
+
@names.delete('')
|
229
|
+
|
230
|
+
@set = true
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
class Pathname
|
236
|
+
class << self
|
237
|
+
def glob(pattern, flags=0)
|
238
|
+
dirs = Dir.glob(pattern, flags)
|
239
|
+
dirs.map! {|path| new(path)}
|
240
|
+
|
241
|
+
if block_given?
|
242
|
+
dirs.each {|dir| yield dir}
|
243
|
+
nil
|
244
|
+
else
|
245
|
+
dirs
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def [](pattern)
|
250
|
+
Dir[pattern].map! {|path| new(path)}
|
251
|
+
end
|
252
|
+
|
253
|
+
def pwd
|
254
|
+
new(Dir.pwd)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def entries; Dir.entries(self).map! {|e| FSSM::Pathname.new(e) }; end
|
259
|
+
def mkdir(mode = 0777); Dir.mkdir(self, mode); end
|
260
|
+
def opendir(&blk); Dir.open(self, &blk); end
|
261
|
+
def rmdir; Dir.rmdir(self); end
|
262
|
+
|
263
|
+
def chdir
|
264
|
+
blk = lambda { yield self } if block_given?
|
265
|
+
Dir.chdir(self, &blk)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class Pathname
|
270
|
+
def blockdev?; FileTest.blockdev?(self); end
|
271
|
+
def chardev?; FileTest.chardev?(self); end
|
272
|
+
def directory?; FileTest.directory?(self); end
|
273
|
+
def executable?; FileTest.executable?(self); end
|
274
|
+
def executable_real?; FileTest.executable_real?(self); end
|
275
|
+
def exists?; FileTest.exists?(self); end
|
276
|
+
def file?; FileTest.file?(self); end
|
277
|
+
def grpowned?; FileTest.grpowned?(self); end
|
278
|
+
def owned?; FileTest.owned?(self); end
|
279
|
+
def pipe?; FileTest.pipe?(self); end
|
280
|
+
def readable?; FileTest.readable?(self); end
|
281
|
+
def readable_real?; FileTest.readable_real?(self); end
|
282
|
+
def setgid?; FileTest.setgit?(self); end
|
283
|
+
def setuid?; FileTest.setuid?(self); end
|
284
|
+
def socket?; FileTest.socket?(self); end
|
285
|
+
def sticky?; FileTest.sticky?(self); end
|
286
|
+
def symlink?; FileTest.symlink?(self); end
|
287
|
+
def world_readable?; FileTest.world_readable?(self); end
|
288
|
+
def world_writable?; FileTest.world_writable?(self); end
|
289
|
+
def writable?; FileTest.writable?(self); end
|
290
|
+
def writable_real?; FileTest.writable_real?(self); end
|
291
|
+
def zero?; FileTest.zero?(self); end
|
292
|
+
|
293
|
+
alias exist? exists?
|
294
|
+
end
|
295
|
+
|
296
|
+
class Pathname
|
297
|
+
def atime; File.atime(self); end
|
298
|
+
def ctime; File.ctime(self); end
|
299
|
+
def ftype; File.ftype(self); end
|
300
|
+
def lstat; File.lstat(self); end
|
301
|
+
def mtime; File.mtime(self); end
|
302
|
+
def stat; File.stat(self); end
|
303
|
+
def utime(atime, mtime); File.utime(self, atime, mtime); end
|
304
|
+
end
|
305
|
+
|
306
|
+
class Pathname
|
307
|
+
class << self
|
308
|
+
def join(*parts)
|
309
|
+
new(File.join(*parts.reject {|p| p.empty? }))
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def basename; self.class.new(File.basename(self)); end
|
314
|
+
def chmod(mode); File.chmod(mode, self); end
|
315
|
+
def chown(owner, group); File.chown(owner, group, self); end
|
316
|
+
def dirname; self.class.new(File.dirname(self)); end
|
317
|
+
def expand_path(from = nil); self.class.new(File.expand_path(self, from)); end
|
318
|
+
def extname; File.extname(self); end
|
319
|
+
def fnmatch?(pat, flags = 0); File.fnmatch(pat, self, flags); end
|
320
|
+
def join(*parts); self.class.join(self, *parts); end
|
321
|
+
def lchmod(mode); File.lchmod(mode, self); end
|
322
|
+
def lchown(owner, group); File.lchown(owner, group, self); end
|
323
|
+
def link(to); File.link(self, to); end
|
324
|
+
def open(mode = 'r', perm = nil, &blk); File.open(self, mode, perm, &blk); end
|
325
|
+
def readlink; self.class.new(File.readlink(self)); end
|
326
|
+
def rename(to); File.rename(self, to); replace(to); end
|
327
|
+
def size; File.size(self); end
|
328
|
+
def size?; File.size?(self); end
|
329
|
+
def symlink(to); File.symlink(self, to); end
|
330
|
+
def truncate; File.truncate(self); end
|
331
|
+
end
|
332
|
+
|
333
|
+
class Pathname
|
334
|
+
def mkpath; self.class.new(FileUtils.mkpath(self)); end
|
335
|
+
def rmtree; self.class.new(FileUtils.rmtree(self).first); end
|
336
|
+
def touch; self.class.new(FileUtils.touch(self).first); end
|
337
|
+
end
|
338
|
+
|
339
|
+
class Pathname
|
340
|
+
def each_line(sep = $/, &blk); IO.foreach(self, sep, &blk); end
|
341
|
+
def read(len = nil, off = 0); IO.read(self, len, off); end
|
342
|
+
def readlines(sep = $/); IO.readlines(self, sep); end
|
343
|
+
def sysopen(mode = 'r', perm = nil); IO.sysopen(self, mode, perm); end
|
344
|
+
end
|
345
|
+
|
346
|
+
class Pathname
|
347
|
+
def find; Find.find(self) {|path| yield FSSM::Pathname.new(path) }; end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|