fssm 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 1
4
- :patch: 4
3
+ :minor: 2
4
+ :patch: 0
5
5
  :build:
@@ -1,64 +1,64 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{fssm}
8
- s.version = "0.1.4"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Travis Tilley"]
12
- s.date = %q{2010-03-10}
12
+ s.date = %q{2010-11-13}
13
13
  s.description = %q{file system state monitor}
14
14
  s.email = %q{ttilley@gmail.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.markdown"
17
+ "README.markdown"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.markdown",
24
- "Rakefile",
25
- "VERSION.yml",
26
- "example.rb",
27
- "fssm.gemspec",
28
- "lib/fssm.rb",
29
- "lib/fssm/backends/fsevents.rb",
30
- "lib/fssm/backends/inotify.rb",
31
- "lib/fssm/backends/polling.rb",
32
- "lib/fssm/backends/rubycocoa/fsevents.rb",
33
- "lib/fssm/monitor.rb",
34
- "lib/fssm/path.rb",
35
- "lib/fssm/pathname.rb",
36
- "lib/fssm/state/directory.rb",
37
- "lib/fssm/state/file.rb",
38
- "lib/fssm/support.rb",
39
- "lib/fssm/tree.rb",
40
- "profile/prof-cache.rb",
41
- "profile/prof-fssm-pathname.html",
42
- "profile/prof-pathname.rb",
43
- "profile/prof-plain-pathname.html",
44
- "profile/prof.html",
45
- "spec/path_spec.rb",
46
- "spec/root/duck/quack.txt",
47
- "spec/root/file.css",
48
- "spec/root/file.rb",
49
- "spec/root/file.yml",
50
- "spec/root/moo/cow.txt",
51
- "spec/spec_helper.rb"
21
+ "LICENSE",
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "VERSION.yml",
25
+ "example.rb",
26
+ "fssm.gemspec",
27
+ "lib/fssm.rb",
28
+ "lib/fssm/backends/fsevents.rb",
29
+ "lib/fssm/backends/inotify.rb",
30
+ "lib/fssm/backends/polling.rb",
31
+ "lib/fssm/backends/rubycocoa/fsevents.rb",
32
+ "lib/fssm/monitor.rb",
33
+ "lib/fssm/path.rb",
34
+ "lib/fssm/pathname.rb",
35
+ "lib/fssm/state/directory.rb",
36
+ "lib/fssm/state/file.rb",
37
+ "lib/fssm/support.rb",
38
+ "lib/fssm/tree.rb",
39
+ "profile/prof-cache.rb",
40
+ "profile/prof-fssm-pathname.html",
41
+ "profile/prof-pathname.rb",
42
+ "profile/prof-plain-pathname.html",
43
+ "profile/prof.html",
44
+ "spec/monitor_spec.rb",
45
+ "spec/path_spec.rb",
46
+ "spec/root/duck/quack.txt",
47
+ "spec/root/file.css",
48
+ "spec/root/file.rb",
49
+ "spec/root/file.yml",
50
+ "spec/root/moo/cow.txt",
51
+ "spec/spec_helper.rb"
52
52
  ]
53
53
  s.homepage = %q{http://github.com/ttilley/fssm}
54
- s.rdoc_options = ["--charset=UTF-8"]
55
54
  s.require_paths = ["lib"]
56
55
  s.rubygems_version = %q{1.3.6}
57
56
  s.summary = %q{file system state monitor}
58
57
  s.test_files = [
58
+ "spec/monitor_spec.rb",
59
59
  "spec/path_spec.rb",
60
- "spec/spec_helper.rb",
61
- "spec/root/file.rb"
60
+ "spec/root/file.rb",
61
+ "spec/spec_helper.rb"
62
62
  ]
63
63
 
64
64
  if s.respond_to? :specification_version then
@@ -11,7 +11,8 @@ module FSSM
11
11
  end
12
12
 
13
13
  def monitor(*args, &block)
14
- monitor = FSSM::Monitor.new
14
+ options = args[-1].is_a?(Hash) ? args.pop : {}
15
+ monitor = FSSM::Monitor.new(options)
15
16
  FSSM::Support.use_block(args.empty? ? monitor : monitor.path(*args), block)
16
17
 
17
18
  monitor.run
@@ -4,18 +4,14 @@ class FSSM::Monitor
4
4
  @backend = FSSM::Backends::Default.new
5
5
  end
6
6
 
7
- def path(*args, &block)
8
- path = FSSM::Path.new(*args)
9
- FSSM::Support.use_block(path, block)
10
-
11
- @backend.add_handler(FSSM::State::Directory.new(path))
7
+ def path(path=nil, glob=nil, &block)
8
+ path = create_path(path, glob, &block)
9
+ @backend.add_handler(FSSM::State::Directory.new(path, @options))
12
10
  path
13
11
  end
14
12
 
15
- def file(*args, &block)
16
- path = FSSM::Path.new(*args)
17
- FSSM::Support.use_block(path, block)
18
-
13
+ def file(path=nil, glob=nil, &block)
14
+ path = create_path(path, glob, &block)
19
15
  @backend.add_handler(FSSM::State::File.new(path))
20
16
  path
21
17
  end
@@ -23,4 +19,12 @@ class FSSM::Monitor
23
19
  def run
24
20
  @backend.run
25
21
  end
22
+
23
+ private
24
+
25
+ def create_path(path, glob, &block)
26
+ path = FSSM::Path.new(path, glob, @options)
27
+ FSSM::Support.use_block(path, block)
28
+ path
29
+ end
26
30
  end
@@ -1,5 +1,6 @@
1
1
  class FSSM::Path
2
- def initialize(path=nil, glob=nil, &block)
2
+ def initialize(path=nil, glob=nil, options={}, &block)
3
+ @options = options
3
4
  set_path(path || '.')
4
5
  set_glob(glob || '**/*')
5
6
  init_callbacks
@@ -26,16 +27,16 @@ class FSSM::Path
26
27
  set_glob(value)
27
28
  end
28
29
 
29
- def create(callback_or_path=nil, &block)
30
- callback_action(:create, (block_given? ? block : callback_or_path))
30
+ def create(*args, &block)
31
+ callback_action(:create, (block_given? ? block : args))
31
32
  end
32
33
 
33
- def update(callback_or_path=nil, &block)
34
- callback_action(:update, (block_given? ? block : callback_or_path))
34
+ def update(*args, &block)
35
+ callback_action(:update, (block_given? ? block : args))
35
36
  end
36
37
 
37
- def delete(callback_or_path=nil, &block)
38
- callback_action(:delete, (block_given? ? block : callback_or_path))
38
+ def delete(*args, &block)
39
+ callback_action(:delete, (block_given? ? block : args))
39
40
  end
40
41
 
41
42
  private
@@ -45,13 +46,13 @@ class FSSM::Path
45
46
  @callbacks = Hash.new(do_nothing)
46
47
  end
47
48
 
48
- def callback_action(type, arg=nil)
49
- if arg.is_a?(Proc)
50
- set_callback(type, arg)
51
- elsif arg.nil?
49
+ def callback_action(type, args=[])
50
+ if args.is_a?(Proc)
51
+ set_callback(type, args)
52
+ elsif args.empty?
52
53
  get_callback(type)
53
54
  else
54
- run_callback(type, arg)
55
+ run_callback(type, args)
55
56
  end
56
57
  end
57
58
 
@@ -64,13 +65,14 @@ class FSSM::Path
64
65
  @callbacks[type]
65
66
  end
66
67
 
67
- def run_callback(type, arg)
68
- base, relative = split_path(arg)
68
+ def run_callback(type, args)
69
+ callback_args = split_path(args[0])
70
+ callback_args << args[1] if @options[:directories]
69
71
 
70
72
  begin
71
- @callbacks[type].call(base, relative)
73
+ @callbacks[type].call(*callback_args)
72
74
  rescue Exception => e
73
- raise FSSM::CallbackError, "#{type} - #{base.join(relative)}: #{e.message}", e.backtrace
75
+ raise FSSM::CallbackError, "#{type} - #{args[0]}: #{e.message}", e.backtrace
74
76
  end
75
77
  end
76
78
 
@@ -2,8 +2,9 @@ module FSSM::State
2
2
  class Directory
3
3
  attr_reader :path
4
4
 
5
- def initialize(path)
5
+ def initialize(path, options={})
6
6
  @path = path
7
+ @options = options
7
8
  @cache = FSSM::Tree::Cache.new
8
9
  end
9
10
 
@@ -20,24 +21,29 @@ module FSSM::State
20
21
  private
21
22
 
22
23
  def created(previous, current)
23
- (current.keys - previous.keys).each {|created| @path.create(created)}
24
+ (current.keys - previous.keys).sort.each do |file|
25
+ @path.create(file, current[file][1])
26
+ end
24
27
  end
25
28
 
26
29
  def deleted(previous, current)
27
- (previous.keys - current.keys).each {|deleted| @path.delete(deleted)}
30
+ (previous.keys - current.keys).sort.reverse.each do |file|
31
+ @path.delete(file, previous[file][1])
32
+ end
28
33
  end
29
34
 
30
35
  def modified(previous, current)
31
36
  (current.keys & previous.keys).each do |file|
32
- @path.update(file) if (current[file] <=> previous[file]) != 0
37
+ current_data = current[file]
38
+ @path.update(file, current_data[1]) if (current_data[0] <=> previous[file][0]) != 0
33
39
  end
34
40
  end
35
41
 
36
42
  def recache(base)
37
43
  base = FSSM::Pathname.for(base)
38
- previous = @cache.files
44
+ previous = cache_entries
39
45
  snapshot(base)
40
- current = @cache.files
46
+ current = cache_entries
41
47
  [previous, current]
42
48
  end
43
49
 
@@ -53,5 +59,16 @@ module FSSM::State
53
59
  end
54
60
  end
55
61
 
62
+ def cache_entries
63
+ entries = tag_entries(@cache.files, :file)
64
+ entries.merge! tag_entries(@cache.directories, :directory) if @options[:directories]
65
+ entries
66
+ end
67
+
68
+ def tag_entries(entries, tag)
69
+ tagged_entries = {}
70
+ entries.each_pair { |fname, mtime| tagged_entries[fname] = [mtime, tag] }
71
+ tagged_entries
72
+ end
56
73
  end
57
74
  end
@@ -0,0 +1,189 @@
1
+ require "spec_helper"
2
+
3
+ require "fileutils"
4
+ require "tempfile"
5
+
6
+ module FSSM::MonitorSpecHelpers
7
+ def create_tmp_dir
8
+ @tmp_dir = Dir.mktmpdir
9
+ FileUtils.cp_r File.join(File.dirname(__FILE__), 'root'), @tmp_dir
10
+ # Because git does not track empty directories, create one ourselves.
11
+ FileUtils.mkdir_p @tmp_dir + '/root/yawn'
12
+ @tmp_dir
13
+ end
14
+
15
+ def remove_tmp_dir
16
+ FileUtils.remove_entry_secure @tmp_dir
17
+ end
18
+
19
+ def create_handler(type)
20
+ lambda {|*args| @handler_results[type] << args}
21
+ end
22
+
23
+ def create_monitor(options={})
24
+ @handler_results = Hash.new {|hash, key| hash[key] = []}
25
+ @monitor = FSSM::Monitor.new(options)
26
+ @monitor.path(@tmp_dir) do |p|
27
+ p.create(&create_handler(:create))
28
+ p.update(&create_handler(:update))
29
+ p.delete(&create_handler(:delete))
30
+ end
31
+ sleep 1 # give time for monitor to preload
32
+ end
33
+
34
+ def run_monitor
35
+ thread = Thread.new {@monitor.run}
36
+ sleep 2 # give time for monitor to see changes
37
+ thread.kill
38
+ end
39
+ end
40
+
41
+ describe "The File System State Monitor" do
42
+ describe "monitor" do
43
+ include FSSM::MonitorSpecHelpers
44
+
45
+ before do
46
+ create_tmp_dir
47
+ end
48
+
49
+ after do
50
+ remove_tmp_dir
51
+ end
52
+
53
+ describe "with default options" do
54
+ before do
55
+ create_monitor
56
+ end
57
+
58
+ it "should call create callback upon file creation" do
59
+ file = @tmp_dir + "/newfile.rb"
60
+ File.exists?(file).should be_false
61
+ FileUtils.touch file
62
+ run_monitor
63
+ @handler_results[:create].should == [[@tmp_dir, 'newfile.rb']]
64
+ end
65
+
66
+ it "should call update callback upon file modification" do
67
+ FileUtils.touch @tmp_dir + '/root/file.rb'
68
+ run_monitor
69
+ @handler_results[:update].should == [[@tmp_dir, 'root/file.rb']]
70
+ end
71
+
72
+ it "should call delete callback upon file deletion" do
73
+ FileUtils.rm @tmp_dir + "/root/file.rb"
74
+ run_monitor
75
+ @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
76
+ end
77
+
78
+ it "should call create and delete callbacks upon file renaming in the same directory" do
79
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
80
+ run_monitor
81
+ @handler_results[:create].should == [[@tmp_dir, 'root/old_file.rb']]
82
+ @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
83
+ @handler_results[:update].should == []
84
+ end
85
+
86
+ it "should call create and delete callbacks upon file moving to another directory" do
87
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
88
+ run_monitor
89
+ @handler_results[:create].should == [[@tmp_dir, 'old_file.rb']]
90
+ @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
91
+ @handler_results[:update].should == []
92
+ end
93
+
94
+ it "should not call callbacks upon directory operations" do
95
+ FileUtils.mkdir @tmp_dir + "/another_yawn"
96
+ FileUtils.rmdir @tmp_dir + "/root/yawn"
97
+ run_monitor
98
+ @handler_results[:create].should == []
99
+ @handler_results[:delete].should == []
100
+ end
101
+ end
102
+
103
+ describe "when configured to consider files and directories" do
104
+ before do
105
+ create_monitor(:directories => true)
106
+ end
107
+
108
+ it "should call create callback upon directory creation" do
109
+ FileUtils.mkdir @tmp_dir + "/another_yawn"
110
+ run_monitor
111
+ @handler_results[:create].should == [[@tmp_dir, 'another_yawn', :directory]]
112
+ end
113
+
114
+ it "should call delete callback upon directory deletion" do
115
+ FileUtils.rmdir @tmp_dir + "/root/yawn"
116
+ run_monitor
117
+ @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
118
+ end
119
+
120
+ it "should call create, update, and delete callbacks upon directory renaming in the same directory" do
121
+ FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/root/old_yawn"
122
+ run_monitor
123
+ @handler_results[:create].should == [[@tmp_dir, 'root/old_yawn', :directory]]
124
+ @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
125
+ @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
126
+ end
127
+
128
+ it "should call create, update, and delete callbacks upon directory moving to another directory" do
129
+ FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/old_yawn"
130
+ run_monitor
131
+ @handler_results[:create].should == [[@tmp_dir, 'old_yawn', :directory]]
132
+ @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
133
+ @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
134
+ end
135
+
136
+ it "should call create, update, and delete callbacks upon file renaming in the same directory" do
137
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
138
+ run_monitor
139
+ @handler_results[:create].should == [[@tmp_dir, 'root/old_file.rb', :file]]
140
+ @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb', :file]]
141
+ @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
142
+ end
143
+
144
+ it "should call create, update, and delete callbacks upon file moving to another directory" do
145
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
146
+ run_monitor
147
+ @handler_results[:create].should == [[@tmp_dir, 'old_file.rb', :file]]
148
+ @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb', :file]]
149
+ @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
150
+ end
151
+
152
+ it "should call delete callbacks upon directory structure deletion, in reverse order" do
153
+ FileUtils.rm_rf @tmp_dir + '/.'
154
+ run_monitor
155
+ @handler_results[:create].should == []
156
+ @handler_results[:delete].should == [
157
+ ['root/yawn', :directory],
158
+ ['root/moo/cow.txt', :file],
159
+ ['root/moo', :directory],
160
+ ['root/file.yml', :file],
161
+ ['root/file.rb', :file],
162
+ ['root/file.css', :file],
163
+ ['root/duck/quack.txt', :file],
164
+ ['root/duck', :directory],
165
+ ['root', :directory]
166
+ ].map {|(file, type)| [@tmp_dir, file, type]}
167
+ @handler_results[:update].should == []
168
+ end
169
+
170
+ it "should call create callbacks upon directory structure creation, in order" do
171
+ FileUtils.cp_r @tmp_dir + '/root/.', @tmp_dir + '/new_root'
172
+ run_monitor
173
+ @handler_results[:create].should == [
174
+ ['new_root', :directory],
175
+ ['new_root/duck', :directory],
176
+ ['new_root/duck/quack.txt', :file],
177
+ ['new_root/file.css', :file],
178
+ ['new_root/file.rb', :file],
179
+ ['new_root/file.yml', :file],
180
+ ['new_root/moo', :directory],
181
+ ['new_root/moo/cow.txt', :file],
182
+ ['new_root/yawn', :directory]
183
+ ].map {|(file, type)| [@tmp_dir, file, type]}
184
+ @handler_results[:delete].should == []
185
+ @handler_results[:update].should == []
186
+ end
187
+ end
188
+ end
189
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require "spec_helper"
2
2
 
3
3
  describe "The File System State Monitor" do
4
4
  describe "paths" do
@@ -27,6 +27,10 @@ describe "The File System State Monitor" do
27
27
  path.glob.should == ['**/*.yml']
28
28
  end
29
29
 
30
+ it "should accept an optional option parameter" do
31
+ lambda {FSSM::Path.new('.', '**/*.yml', :foo => :bar)}.should_not raise_error
32
+ end
33
+
30
34
  it "should default the glob to ['**/*']" do
31
35
  path = FSSM::Path.new
32
36
  path.glob.should == ['**/*']
@@ -35,21 +39,21 @@ describe "The File System State Monitor" do
35
39
  it "should accept a callback for update events" do
36
40
  path = FSSM::Path.new
37
41
  callback = lambda {|base, relative| return true}
38
- path.update(callback)
42
+ path.update(&callback)
39
43
  (path.update).should == callback
40
44
  end
41
45
 
42
46
  it "should accept a callback for delete events" do
43
47
  path = FSSM::Path.new
44
48
  callback = lambda {|base, relative| return true}
45
- path.delete(callback)
49
+ path.delete(&callback)
46
50
  (path.delete).should == callback
47
51
  end
48
52
 
49
53
  it "should accept a callback for create events" do
50
54
  path = FSSM::Path.new
51
55
  callback = lambda {|base, relative| return true}
52
- path.create(callback)
56
+ path.create(&callback)
53
57
  (path.create).should == callback
54
58
  end
55
59
 
@@ -71,5 +75,22 @@ describe "The File System State Monitor" do
71
75
  path.create.call('', '').should == 'success'
72
76
  end
73
77
 
78
+ it "should pass file type to callbacks as the third argument if :directories option is used" do
79
+ path = FSSM::Path.new "#{@watch_root}", nil, :directories => true do
80
+ glob '**/*.yml'
81
+ update {|base, relative, type| [base, relative, type]}
82
+ delete {|base, relative, type| [base, relative, type]}
83
+ create {|base, relative, type| [base, relative, type]}
84
+ end
85
+
86
+ "#{path}".should == "#{@watch_root}"
87
+ path.glob.should == ['**/*.yml']
88
+ path.update.should be_a_kind_of(Proc)
89
+ path.delete.should be_a_kind_of(Proc)
90
+ path.create.should be_a_kind_of(Proc)
91
+ path.update.call('b', 'r', 't').should == ['b', 'r', 't']
92
+ path.delete.call('b', 'r', 't').should == ['b', 'r', 't']
93
+ path.create.call('b', 'r', 't').should == ['b', 'r', 't']
94
+ end
74
95
  end
75
96
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 4
9
- version: 0.1.4
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Travis Tilley
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-10 00:00:00 -05:00
17
+ date: 2010-11-13 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,6 @@ extra_rdoc_files:
40
40
  - README.markdown
41
41
  files:
42
42
  - .document
43
- - .gitignore
44
43
  - LICENSE
45
44
  - README.markdown
46
45
  - Rakefile
@@ -64,6 +63,7 @@ files:
64
63
  - profile/prof-pathname.rb
65
64
  - profile/prof-plain-pathname.html
66
65
  - profile/prof.html
66
+ - spec/monitor_spec.rb
67
67
  - spec/path_spec.rb
68
68
  - spec/root/duck/quack.txt
69
69
  - spec/root/file.css
@@ -76,8 +76,8 @@ homepage: http://github.com/ttilley/fssm
76
76
  licenses: []
77
77
 
78
78
  post_install_message:
79
- rdoc_options:
80
- - --charset=UTF-8
79
+ rdoc_options: []
80
+
81
81
  require_paths:
82
82
  - lib
83
83
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -102,6 +102,7 @@ signing_key:
102
102
  specification_version: 3
103
103
  summary: file system state monitor
104
104
  test_files:
105
+ - spec/monitor_spec.rb
105
106
  - spec/path_spec.rb
106
- - spec/spec_helper.rb
107
107
  - spec/root/file.rb
108
+ - spec/spec_helper.rb
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- .idea
4
- /*.gem
5
- coverage
6
- nbproject
7
- pkg
8
- rdoc
9
-