file-monitor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +142 -0
- data/examples/f5.rb +106 -0
- data/examples/use-block.rb +24 -0
- data/examples/use-inherit.rb +27 -0
- data/lib/file-monitor.rb +146 -0
- metadata +99 -0
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
Ruby File Monitor
|
2
|
+
=================
|
3
|
+
|
4
|
+
Ruby File Monitor is a easy way to watch the directories and files, do anything when them changed. It's base on [rb-inotify](https://github.com/nex3/rb-inotify), So it only works in inotify supported system such as Linux.
|
5
|
+
|
6
|
+
Requirements
|
7
|
+
------------
|
8
|
+
|
9
|
+
Ruby >= 1.8.7
|
10
|
+
|
11
|
+
Linux Kernel >= 2.6.13
|
12
|
+
|
13
|
+
Features
|
14
|
+
--------
|
15
|
+
|
16
|
+
1. Auto watching recursively:
|
17
|
+
|
18
|
+
If make a new directory in the watched directory, the new directory will be watched automatically.
|
19
|
+
|
20
|
+
2. Support Ignore Dirs:
|
21
|
+
|
22
|
+
Any directory matched Regexp pattern ignored_dirs will not be watched.
|
23
|
+
|
24
|
+
3. Support Ignore Files:
|
25
|
+
|
26
|
+
Any files matched Regexp pattern ignored_files will not be recorded.
|
27
|
+
|
28
|
+
4. Events Buffer Mechanism:
|
29
|
+
|
30
|
+
To avoid run the check methods too quickly, for example, when delete 20 files at the same time, if without Events Buffer will run the check methods 20 times. the frequency of file-monitor is 0.2 second
|
31
|
+
|
32
|
+
|
33
|
+
Installation
|
34
|
+
------------
|
35
|
+
Install from source
|
36
|
+
|
37
|
+
git clone https://github.com/jiangmiao/ruby-file-monitor
|
38
|
+
cd ruby-file-monitor
|
39
|
+
gem build file-monitor.gemspec
|
40
|
+
gem install file-monitor-0.1.0.gem --user-install
|
41
|
+
|
42
|
+
Install from gem server
|
43
|
+
|
44
|
+
gem install file-monitor
|
45
|
+
|
46
|
+
Usage
|
47
|
+
-----
|
48
|
+
### Using block
|
49
|
+
|
50
|
+
#!/usr/bin/env ruby
|
51
|
+
# coding: utf-8
|
52
|
+
# File: examples/use-block.rb
|
53
|
+
|
54
|
+
require 'file-monitor'
|
55
|
+
|
56
|
+
dir = ARGV[0] || '.'
|
57
|
+
m = FileMonitor.new(dir)
|
58
|
+
|
59
|
+
# ignore any dirs contains .git on .svn
|
60
|
+
m.ignored_dirs = /\.git|\.svn/
|
61
|
+
|
62
|
+
# ignore any files contains .swp or ~
|
63
|
+
m.ignored_files = /\.swp|~/
|
64
|
+
|
65
|
+
# the block's events contains all file modified infomation in last 0.2 second
|
66
|
+
m.run do|events|
|
67
|
+
puts "#{events.size} events"
|
68
|
+
puts "do something"
|
69
|
+
end
|
70
|
+
|
71
|
+
### Using inherit
|
72
|
+
|
73
|
+
#!/usr/bin/env ruby
|
74
|
+
# coding: utf-8
|
75
|
+
# File: examples/use-inherit.rb
|
76
|
+
|
77
|
+
require 'file-monitor'
|
78
|
+
|
79
|
+
class MyFileMonitor < FileMonitor
|
80
|
+
def check(events)
|
81
|
+
puts "#{events.size} events"
|
82
|
+
puts "do something"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
dir = ARGV[0] || '.'
|
87
|
+
m = MyFileMonitor.new(dir)
|
88
|
+
m.ignored_dirs = /\.git|\.svn/
|
89
|
+
m.ignored_files = /\.swp|~/
|
90
|
+
m.run
|
91
|
+
|
92
|
+
If block exists, the check method will be ignored.
|
93
|
+
|
94
|
+
Examples
|
95
|
+
--------
|
96
|
+
### Auto F5
|
97
|
+
|
98
|
+
Auto F5 will auto refresh the webpage in browser when any watched files changed. It's simple but very useful.
|
99
|
+
|
100
|
+
f5.rb requires sinatra.
|
101
|
+
|
102
|
+
#### Limitation:
|
103
|
+
|
104
|
+
1. The watched page MUST have </body> tag.
|
105
|
+
f5.rb will insert script before </body> to refresh the page
|
106
|
+
when physical file changed.
|
107
|
+
|
108
|
+
2. Only support GET requests.
|
109
|
+
|
110
|
+
#### Usage
|
111
|
+
|
112
|
+
Environment:
|
113
|
+
the website physical path is /var/www/foo
|
114
|
+
the host is www.foo.com
|
115
|
+
homepage is www.foo.com/index.html
|
116
|
+
|
117
|
+
start watching the directory
|
118
|
+
ruby examples/f5.rb /var/www/foo
|
119
|
+
|
120
|
+
open www.foo.com:4567/index.html in browser
|
121
|
+
you will see the page same as www.foo.com/index.html
|
122
|
+
do some changes on /var/www/foo/index.html then save the file.
|
123
|
+
or create or modify any file in /var/www/foo
|
124
|
+
www.foo.com/index.html will refresh automatically.
|
125
|
+
|
126
|
+
### examples/use-block.rb
|
127
|
+
|
128
|
+
$ ruby examples/use-block.rb
|
129
|
+
watching .
|
130
|
+
watching ./lib/
|
131
|
+
ignore ./.git/
|
132
|
+
watching ./examples/
|
133
|
+
|
134
|
+
Edit and save README.md by gvim, examples/use-block outputs
|
135
|
+
|
136
|
+
+ ./4913
|
137
|
+
- ./4913
|
138
|
+
- ./README.md
|
139
|
+
+ ./README.md
|
140
|
+
# ./README.md
|
141
|
+
5 events
|
142
|
+
do something
|
data/examples/f5.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
lib_dir = File.join File.dirname(__FILE__), '../lib'
|
7
|
+
$:.unshift lib_dir unless $:.include? lib_dir
|
8
|
+
|
9
|
+
require 'file-monitor'
|
10
|
+
require 'sinatra'
|
11
|
+
require 'getoptlong'
|
12
|
+
|
13
|
+
class AjaxF5 < FileMonitor
|
14
|
+
attr_reader :updated_at, :js
|
15
|
+
def initialize(dir)
|
16
|
+
super dir
|
17
|
+
@updated_at = Time.now.to_f
|
18
|
+
# ajax library from http://code.google.com/p/miniajax/
|
19
|
+
@js = <<EOT
|
20
|
+
(function() {
|
21
|
+
var ajax;
|
22
|
+
function $(e){if(typeof e=='string')e=document.getElementById(e);return e};
|
23
|
+
function collect(a,f){var n=[];for(var i=0;i<a.length;i++){var v=f(a[i]);if(v!=null)n.push(v)}return n};
|
24
|
+
|
25
|
+
ajax={};
|
26
|
+
ajax.x=function(){try{return new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){return new XMLHttpRequest()}}};
|
27
|
+
ajax.serialize=function(f){var g=function(n){return f.getElementsByTagName(n)};var nv=function(e){if(e.name)return encodeURIComponent(e.name)+'='+encodeURIComponent(e.value);else return ''};var i=collect(g('input'),function(i){if((i.type!='radio'&&i.type!='checkbox')||i.checked)return nv(i)});var s=collect(g('select'),nv);var t=collect(g('textarea'),nv);return i.concat(s).concat(t).join('&');};
|
28
|
+
ajax.send=function(u,f,m,a){var x=ajax.x();x.open(m,u,true);x.onreadystatechange=function(){if(x.readyState==4)f(x.responseText)};if(m=='POST')x.setRequestHeader('Content-type','application/x-www-form-urlencoded');x.send(a)};
|
29
|
+
ajax.get=function(url,func){ajax.send(url,func,'GET')};
|
30
|
+
var now = Date.now();
|
31
|
+
function checkStatus() {
|
32
|
+
ajax.get("/f5_status", function(rt) {
|
33
|
+
if (now < parseFloat(rt)) {
|
34
|
+
location.reload(true);
|
35
|
+
}
|
36
|
+
setTimeout(checkStatus, 500);
|
37
|
+
});
|
38
|
+
}
|
39
|
+
checkStatus();
|
40
|
+
})();
|
41
|
+
EOT
|
42
|
+
end
|
43
|
+
|
44
|
+
# simple mark the last updated time
|
45
|
+
def check(events)
|
46
|
+
@updated_at = Time.now.to_f
|
47
|
+
puts "updated at #{@updated_at}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
# use new thread to monitor the file
|
52
|
+
Thread.new do
|
53
|
+
begin
|
54
|
+
super
|
55
|
+
rescue
|
56
|
+
puts $!.message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
f5 = self
|
61
|
+
set :logging, false
|
62
|
+
get '/f5_status' do
|
63
|
+
(f5.updated_at*1000).to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
get '/f5.js' do
|
67
|
+
f5.js
|
68
|
+
end
|
69
|
+
|
70
|
+
get '*' do
|
71
|
+
host = env['SERVER_NAME']
|
72
|
+
path = env['REQUEST_PATH']
|
73
|
+
begin
|
74
|
+
data = open('http://' + host + path) {|f|
|
75
|
+
content_type f.meta['content-type']
|
76
|
+
f.read
|
77
|
+
}
|
78
|
+
data.gsub Regexp.new('</body>', Regexp::IGNORECASE), '<script type="text/javascript" src="/f5.js"></script></body>'
|
79
|
+
rescue
|
80
|
+
$!.message.match /\d+/
|
81
|
+
[$&.to_i, {}, '']
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
dir = ARGV[0] || '.'
|
88
|
+
|
89
|
+
opts = GetoptLong.new(
|
90
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT]
|
91
|
+
)
|
92
|
+
|
93
|
+
opts.each do|opt, arg|
|
94
|
+
case opt
|
95
|
+
when '--help'
|
96
|
+
puts 'ruby f5.rb [project-directory]'
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
require 'sinatra'
|
102
|
+
require 'open-uri'
|
103
|
+
f5 = AjaxF5.new dir
|
104
|
+
f5.ignored_dirs = /\.git|\.svn/
|
105
|
+
f5.ignored_files = /\.sw.*|\~/
|
106
|
+
f5.run
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
# File: examples/use-block.rb
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
lib_dir = File.join File.dirname(__FILE__), '../lib'
|
8
|
+
$:.unshift lib_dir unless $:.include? lib_dir
|
9
|
+
|
10
|
+
require 'file-monitor'
|
11
|
+
|
12
|
+
dir = ARGV[0] || '.'
|
13
|
+
m = FileMonitor.new(dir)
|
14
|
+
# ignore any dirs end with .git on .svn
|
15
|
+
m.ignored_dirs = /\.git|\.svn/
|
16
|
+
|
17
|
+
# ignore any files end with .swp or ~
|
18
|
+
m.ignored_files = /\.swp|~/
|
19
|
+
|
20
|
+
# the block's events contains all file modified infomation in last 0.2 second
|
21
|
+
m.run do|events|
|
22
|
+
puts "#{events.size} events"
|
23
|
+
puts "do something"
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
# File: examples/use-inherit.rb
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
lib_dir = File.join File.dirname(__FILE__), '../lib'
|
8
|
+
$:.unshift lib_dir unless $:.include? lib_dir
|
9
|
+
|
10
|
+
require 'file-monitor.rb'
|
11
|
+
|
12
|
+
class MyFileMonitor < FileMonitor
|
13
|
+
def check(events)
|
14
|
+
puts "#{events.size} events"
|
15
|
+
puts "do something"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
dir = ARGV[0] || '.'
|
20
|
+
m = MyFileMonitor.new(dir)
|
21
|
+
# ignore any dirs end with .git on .svn
|
22
|
+
m.ignored_dirs = /\.git|\.svn/
|
23
|
+
|
24
|
+
# ignore any files end with .swp or ~
|
25
|
+
m.ignored_files = /\.swp|~/
|
26
|
+
|
27
|
+
m.run
|
data/lib/file-monitor.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require 'rb-inotify'
|
5
|
+
|
6
|
+
class FileMonitor
|
7
|
+
# the ignored list
|
8
|
+
attr_accessor :ignored_dirs, :ignored_files
|
9
|
+
|
10
|
+
# do the action every @frequency second, to avoid check too frequently
|
11
|
+
attr_accessor :frequency
|
12
|
+
|
13
|
+
def initialize(project_dir)
|
14
|
+
@notifier = INotify::Notifier.new
|
15
|
+
@project_dir = project_dir
|
16
|
+
|
17
|
+
@events = []
|
18
|
+
@ignores = []
|
19
|
+
@frequency = 0.2
|
20
|
+
end
|
21
|
+
|
22
|
+
def ignored?(path, pattern)
|
23
|
+
for ignore in Array(pattern)
|
24
|
+
if path =~ ignore
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
def ignored_dir?(path)
|
32
|
+
ignored? path, @ignored_dirs
|
33
|
+
end
|
34
|
+
|
35
|
+
def ignored_file?(path)
|
36
|
+
ignored? path, @ignored_files
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO combine events maybe it's not nesscary
|
40
|
+
# moved_from + moved_to = rename
|
41
|
+
# create + delete = delete
|
42
|
+
# delete + create = modify
|
43
|
+
# rename + delete = delete
|
44
|
+
def push_event event
|
45
|
+
@events << event
|
46
|
+
end
|
47
|
+
|
48
|
+
# Watch a directory
|
49
|
+
def watch(dir)
|
50
|
+
if ignored_dir?(dir)
|
51
|
+
puts "ignore #{dir}"
|
52
|
+
return false
|
53
|
+
else
|
54
|
+
puts "watching #{dir}"
|
55
|
+
end
|
56
|
+
|
57
|
+
@notifier.watch dir, :modify, :create, :move, :delete, :onlydir do|event|
|
58
|
+
flags = event.flags
|
59
|
+
|
60
|
+
# display event info
|
61
|
+
# + created or moved_to
|
62
|
+
# - deleted or moved_from
|
63
|
+
# # modified
|
64
|
+
info = ''
|
65
|
+
if flags.include? :moved_from
|
66
|
+
info += '-'
|
67
|
+
elsif flags.include? :moved_to
|
68
|
+
info += '+'
|
69
|
+
else
|
70
|
+
case flags[0]
|
71
|
+
when :create
|
72
|
+
info += '+'
|
73
|
+
when :modify
|
74
|
+
info += '#'
|
75
|
+
when :delete
|
76
|
+
info += '-'
|
77
|
+
when :ignored
|
78
|
+
info += 'stop watching'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if ignored_file?(event.absolute_name)
|
83
|
+
# the ignored info will not put currently
|
84
|
+
info += "i #{event.absolute_name}"
|
85
|
+
next
|
86
|
+
else
|
87
|
+
info += " #{event.absolute_name}"
|
88
|
+
puts info
|
89
|
+
end
|
90
|
+
|
91
|
+
if flags.include? :isdir
|
92
|
+
if flags.include? :ignore?
|
93
|
+
@notifier.watchers.delete watcher.id
|
94
|
+
elsif flags.include? :create or flags.include? :move
|
95
|
+
watch_recursive event.absolute_name
|
96
|
+
else
|
97
|
+
push_event event
|
98
|
+
end
|
99
|
+
else
|
100
|
+
push_event event
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
|
107
|
+
# Watch directory recursive
|
108
|
+
# use our recursive instead rb-inotify's :recursive to filter the directory
|
109
|
+
def watch_recursive(dir)
|
110
|
+
if watch dir
|
111
|
+
# if watch current directory succeeded, then continue watching the sub-directories
|
112
|
+
Dir.glob(File.join(dir, "*/"), File::FNM_DOTMATCH).each do|subdir|
|
113
|
+
name = File.basename(subdir)
|
114
|
+
next if name == ".." or name == "."
|
115
|
+
watch_recursive subdir
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# check the events received
|
121
|
+
def check(events)
|
122
|
+
if !@check_warning_notified
|
123
|
+
puts "[NOTICE] Inherit from FileMonitor and override method check(events) or\n use file_monitor_instance.run do|events| end to do anything when any file changes"
|
124
|
+
@check_warning_notified = true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# start watching
|
129
|
+
# if block given, check method will be ignored
|
130
|
+
def run(&block)
|
131
|
+
watch_recursive @project_dir
|
132
|
+
|
133
|
+
while true
|
134
|
+
if IO.select([@notifier.to_io], [], [], @frequency)
|
135
|
+
@notifier.process
|
136
|
+
elsif @events.size > 0
|
137
|
+
if block_given?
|
138
|
+
yield @events
|
139
|
+
else
|
140
|
+
check @events
|
141
|
+
end
|
142
|
+
@events = []
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: file-monitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- JiangMiao
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-10-13 00:00:00 +08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rb-inotify
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 8
|
31
|
+
- 8
|
32
|
+
version: 0.8.8
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rb-inotify
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 8
|
46
|
+
- 8
|
47
|
+
version: 0.8.8
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
description: Ruby File Monitor is a easy way to watch the directories and files, do anything when them changed.
|
51
|
+
email: jiangfriend@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- lib/file-monitor.rb
|
60
|
+
- examples/use-block.rb
|
61
|
+
- examples/use-inherit.rb
|
62
|
+
- examples/f5.rb
|
63
|
+
- README.md
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: https://github.com/jiangmiao/ruby-file-monitor
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 8
|
81
|
+
- 7
|
82
|
+
version: 1.8.7
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project: file-monitor
|
94
|
+
rubygems_version: 1.3.7
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: File Monitor Library
|
98
|
+
test_files: []
|
99
|
+
|