fssm 0.1.4 → 0.2.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.
@@ -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
-