Skip to content

Instantly share code, notes, and snippets.

@jiangmiao
Created September 15, 2011 15:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jiangmiao/1219633 to your computer and use it in GitHub Desktop.
Save jiangmiao/1219633 to your computer and use it in GitHub Desktop.
File Monitor and auto F5
#!/usr/bin/env ruby
require 'rubygems'
require 'rb-inotify'
require 'getoptlong'
class Monitor
def initialize(project_dir)
@notifier = INotify::Notifier.new
@project_dir = project_dir
@events = []
end
def ignore?(path)
file = File.basename path
if ['.git', '.svn'].include? file
return true
end
return false
end
def watch(dir)
if ignore?(dir)
puts "ignore #{dir}"
return false
else
puts "watching #{dir}"
end
@notifier.watch dir, :modify, :create, :move, :delete, :onlydir do|event|
flags = event.flags
if flags.include? :isdir
if flags.include? :ignore?
@notifier.watchers.delete watcher.id
elsif not flags.include? :delete
watch path
else
@events << event
end
else
@events << event
end
end
return true
end
# use our recursive instead :recursive to filt
def watch_recursive(dir)
Dir.glob(File.join(dir, "*/"), File::FNM_DOTMATCH).each do|dir|
name = File.basename(dir)
next if name == ".." or name == "."
if watch dir
watch_recursive dir
end
end
end
def run
# watch all sub-directories
watch @project_dir
watch_recursive @project_dir
while true
if IO.select([@notifier.to_io], [], [], 0.2)
@notifier.process
elsif @events.size > 0
check @events
@events = []
end
end
end
end
class KeyF5 < Monitor
def initialize(dir)
super(dir)
end
def get_browser_id
puts "Select browser"
if `xwininfo -wm`.match /Window id: (\w*)/
@browser_id = $1
puts "browser id #{@browser_id}"
else
raise 'get browser id failed'
end
end
def refresh_browser
info = `xwininfo -id #{@browser_id} -wm`
if info.include? 'Firefox'
# firefox不聚焦时发送F5不工作
puts "refresh Firefox #{@browser_id}"
system "xdotool windowactivate --sync #{@browser_id} key F5 windowactivate --sync `xdotool getactivewindow`"
elsif info.include? 'Chromium'
puts "refresh Chromium #{@browser_id}"
system "xdotool key --window #{@browser_id} F5"
else
puts "only support firefox and chromium, please repick the window"
get_browser_id
end
end
def check(events)
for event in events
path = File.expand_path(event.absolute_name)
puts "#{path} [ #{event.flags.join(", ")} ]"
end
refresh_browser
end
def run
get_browser_id
super
end
end
class AjaxF5 < Monitor
attr_reader :updated_at, :js
def initialize(dir)
super dir
@updated_at = Time.now.to_f
@js = <<EOT
(function() {
var ajax;
function $(e){if(typeof e=='string')e=document.getElementById(e);return e};
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};
ajax={};
ajax.x=function(){try{return new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){return new XMLHttpRequest()}}};
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('&');};
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)};
ajax.get=function(url,func){ajax.send(url,func,'GET')};
var now = Date.now();
function check() {
ajax.get("/f5_status", function(rt) {
if (now < parseFloat(rt)) {
location.reload(true);
}
});
setTimeout(check, 500);
}
check();
})();
EOT
end
def check(events)
@updated_at = Time.now.to_f
puts "updated_at #{@updated_at}"
end
def run
Thread.new do
super
end
f5 = self
set :logging, false
get '/f5_status' do
(f5.updated_at*1000).to_s
end
get '/f5.js' do
f5.js
end
get '*' do
host = env['SERVER_NAME']
path = env['REQUEST_PATH']
begin
data = open('http://' + host + path) {|f|
content_type f.meta['content-type']
f.read
}
data.gsub Regexp.new('</body>', Regexp::IGNORECASE), '<script type="text/javascript" src="/f5.js"></script></body>'
rescue
$!.message.match /\d+/
[$&.to_i, {}, '']
end
end
end
end
if __FILE__ == $0
dir = ARGV[0] || '.'
mode = nil
opts = GetoptLong.new(
[ '--mode', '-m', GetoptLong::OPTIONAL_ARGUMENT],
[ '--help', '-h', GetoptLong::NO_ARGUMENT]
)
opts.each do|opt, arg|
case opt
when '--help'
puts <<EOT
ruby f5.rb [选项] [项目路径]
--help, -h
帮助
--mode, -m
ajax 嵌入ajax模式, 默认
key 发送F5键模式
关于ajax模式: 访问 原域名:4567
比如 127.0.0.1,则通过 127.0.0.1:4567 访问
example.com 则通过 example.com:4567 访问
仅支持 GET
EOT
exit
when '--mode'
mode = arg
end
end
if mode == 'key'
f5 = KeyF5.new dir
else
require 'sinatra'
require 'open-uri'
f5 = AjaxF5.new dir
end
f5.run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment