watcher_in_the_water 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +13 -0
- data/README.rdoc +34 -0
- data/Rakefile +16 -0
- data/bin/watcher_in_the_water +6 -0
- data/lib/watcher_in_the_water.rb +68 -0
- data/test/fixtures/dimrill_gate +1 -0
- data/test/fixtures/durins_bridge +1 -0
- data/test/fixtures/west_gate +1 -0
- data/test/live.yml +6 -0
- data/test/live_test.rb +54 -0
- data/test/test_watcher_in_the_water.rb +60 -0
- data/test/watcher/test.yml +8 -0
- metadata +87 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
bin/watcher_in_the_water
|
6
|
+
lib/watcher_in_the_water.rb
|
7
|
+
test/fixtures/dimrill_gate
|
8
|
+
test/fixtures/durins_bridge
|
9
|
+
test/fixtures/west_gate
|
10
|
+
test/live.yml
|
11
|
+
test/live_test.rb
|
12
|
+
test/test_watcher_in_the_water.rb
|
13
|
+
test/watcher/test.yml
|
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= Watcher In The Water - http://en.wikipedia.org/wiki/Watcher_in_the_Water
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
Want to know when the page at a given URL changes? The Watcher in the
|
6
|
+
Water will tell you over Jabber; just create a ~/.watcher/config.yml
|
7
|
+
file and toss +watcher_in_the_water+ into your crontab!
|
8
|
+
|
9
|
+
---
|
10
|
+
jid: watcher-in-the-water@jabber.org
|
11
|
+
password: mellon
|
12
|
+
recipient: phil@hagelb.org
|
13
|
+
urls:
|
14
|
+
- http://rubyconf.org
|
15
|
+
|
16
|
+
== Issues
|
17
|
+
|
18
|
+
XMPP4R takes a really long time to authenticate with my Jabber
|
19
|
+
server. Maybe it's just me though... No idea. This shouldn't run more
|
20
|
+
than once an hour or so, whatever; shouldn't matter.
|
21
|
+
|
22
|
+
== Install
|
23
|
+
|
24
|
+
$ sudo gem install technomancy-watcher_in_the_water --source=http://gems.github.com
|
25
|
+
|
26
|
+
== TODO
|
27
|
+
|
28
|
+
* Finish live tests?
|
29
|
+
* Just send a diff over IM?
|
30
|
+
* Only look at a given XPath?
|
31
|
+
|
32
|
+
== Author
|
33
|
+
|
34
|
+
By Phil Hagelberg (http://technomancy.us)
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/watcher_in_the_water.rb'
|
6
|
+
|
7
|
+
Hoe.new('watcher_in_the_water', WatcherInTheWater::VERSION) do |p|
|
8
|
+
p.rubyforge_name = "seattlerb"
|
9
|
+
p.summary = "Want to know when the page at a given URL changes?
|
10
|
+
The Watcher in the Water will tell you over Jabber."
|
11
|
+
p.developer('Phil Hagelberg', 'http://technomancy.us')
|
12
|
+
p.url = 'http://en.wikipedia.org/wiki/Watcher_in_the_Water'
|
13
|
+
p.extra_deps << 'xmpp4r'
|
14
|
+
end
|
15
|
+
|
16
|
+
# vim: syntax=Ruby
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'yaml'
|
6
|
+
require 'digest/sha1'
|
7
|
+
require 'xmpp4r'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
module WatcherInTheWater
|
11
|
+
include Jabber
|
12
|
+
VERSION = '0.1'
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def configure(config = nil)
|
17
|
+
config = File.expand_path(config || '~/.watcher/config.yml')
|
18
|
+
FileUtils.cd(File.dirname(config))
|
19
|
+
@config = YAML.load(File.read(config))
|
20
|
+
rescue
|
21
|
+
abort "You need #{config} to contain a sender jabber ID, password,
|
22
|
+
a recipient jabber ID, and a list of URLs in YAML format. Example:
|
23
|
+
|
24
|
+
---
|
25
|
+
jid: watcher-in-the-water@jabber.org
|
26
|
+
password: mellon
|
27
|
+
recipient: phil@hagelb.org
|
28
|
+
urls: ---
|
29
|
+
http://rubyconf.org
|
30
|
+
"
|
31
|
+
end
|
32
|
+
|
33
|
+
def watch
|
34
|
+
@config['urls'].each do |url|
|
35
|
+
content = open(url).read
|
36
|
+
hash = Digest::SHA1.hexdigest(content)
|
37
|
+
filename = 'hashes/' + url_transform(url)
|
38
|
+
|
39
|
+
if File.exist?(filename)
|
40
|
+
if File.read(filename) != hash
|
41
|
+
alert("#{url} changed to #{content[0 .. 200]}")
|
42
|
+
end
|
43
|
+
else
|
44
|
+
alert("Watching #{url} for you!")
|
45
|
+
end
|
46
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
47
|
+
File.open(filename, 'w') { |f| f.write hash }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def alert(message)
|
52
|
+
connect
|
53
|
+
@client.send Message.new(@config['recipient'],
|
54
|
+
message).set_type(:normal).set_id('1')
|
55
|
+
end
|
56
|
+
|
57
|
+
def connect
|
58
|
+
return if @client
|
59
|
+
jid = JID.new("#{@config['jid']}/#{`hostname`.chomp}-watcher")
|
60
|
+
@client = Client.new(jid)
|
61
|
+
@client.connect
|
62
|
+
@client.auth(@config['password'])
|
63
|
+
end
|
64
|
+
|
65
|
+
def url_transform(url)
|
66
|
+
url.gsub(/[^\w]/, '_')[0 .. 50] + '_' + Digest::SHA1.hexdigest(url)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dimrill Gate
|
@@ -0,0 +1 @@
|
|
1
|
+
Durin's Bridge
|
@@ -0,0 +1 @@
|
|
1
|
+
West Gate
|
data/test/live.yml
ADDED
data/test/live_test.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin; gem 'miniunit'; rescue LoadError; end
|
3
|
+
require 'test/unit'
|
4
|
+
require 'mongrel'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/watcher_in_the_water'
|
6
|
+
|
7
|
+
# launch Mongrel
|
8
|
+
class LiveServer < Mongrel::HttpHandler
|
9
|
+
attr_accessor :string
|
10
|
+
def process(request, response)
|
11
|
+
response.start(200) do |head,out|
|
12
|
+
out.write(@string || "hello!\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
MONGREL = Mongrel::HttpServer.new("0.0.0.0", "3000")
|
18
|
+
MONGREL.register("/test", SimpleHandler.new)
|
19
|
+
MONGREL.run
|
20
|
+
|
21
|
+
class LiveTest < Test::Unit::TestCase
|
22
|
+
def setup
|
23
|
+
WatcherInTheWater.configure(File.dirname(__FILE__) + '/live.yml')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_live_usage
|
27
|
+
# should alert about all pages being watched on first run
|
28
|
+
# then should alert for a single change
|
29
|
+
# then should send no alerts if nothing's changed
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_host_not_found
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_404
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_sender_jabber_down
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_bad_auth
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_recipient_jabber_down
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_recipient_jid_not_found
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def watch
|
52
|
+
system "#{File.dirname(__FILE__)}/../bin/watcher #{File.dirname(__FILE__)}/live.yml"
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin; gem 'miniunit'; rescue LoadError; end
|
3
|
+
require 'test/unit'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/watcher_in_the_water'
|
5
|
+
|
6
|
+
module WatcherInTheWater
|
7
|
+
# Let's mock out the Jabber connection
|
8
|
+
@client = Object.new
|
9
|
+
@messages = []
|
10
|
+
messages_for_closure = @messages
|
11
|
+
|
12
|
+
def @client.send(message)
|
13
|
+
WatcherInTheWater.messages << message
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.messages; @messages; end
|
17
|
+
def self.config; @config; end
|
18
|
+
end
|
19
|
+
|
20
|
+
WatcherInTheWater.configure(File.dirname(__FILE__) + '/watcher/test.yml')
|
21
|
+
|
22
|
+
class TestWatcherInTheWater < Test::Unit::TestCase
|
23
|
+
def setup
|
24
|
+
FileUtils.rm_rf('hashes')
|
25
|
+
FileUtils.rm_rf('fixtures')
|
26
|
+
FileUtils.cp_r('../fixtures', 'fixtures')
|
27
|
+
WatcherInTheWater.messages.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_watch_first_time
|
31
|
+
WatcherInTheWater.watch
|
32
|
+
|
33
|
+
['fixtures_dimrill_gate_d2785ed5a8c89b47e541e85b7216d0153a383651',
|
34
|
+
'fixtures_durins_bridge_f81c948ae30baa391371150bc42cc935ebf8287c',
|
35
|
+
'fixtures_west_gate_cdef5d9283f6bdeddfa89de21bbca8d5acb3c673'].each do |u|
|
36
|
+
assert_equal 40, File.read('hashes/' + u).length
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal 3, WatcherInTheWater.messages.size
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_watch_one_change
|
43
|
+
WatcherInTheWater.watch
|
44
|
+
WatcherInTheWater.messages.clear
|
45
|
+
|
46
|
+
File.open('fixtures/durins_bridge', 'w') { |f| f.puts "Durin's Super-Bridge!" }
|
47
|
+
WatcherInTheWater.watch
|
48
|
+
|
49
|
+
assert_equal 1, WatcherInTheWater.messages.size
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_watch_no_changes
|
53
|
+
WatcherInTheWater.watch
|
54
|
+
WatcherInTheWater.messages.clear
|
55
|
+
|
56
|
+
WatcherInTheWater.watch
|
57
|
+
|
58
|
+
assert_equal [], WatcherInTheWater.messages
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: watcher_in_the_water
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Phil Hagelberg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-07 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: xmpp4r
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.2
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email:
|
37
|
+
- http://technomancy.us
|
38
|
+
executables:
|
39
|
+
- watcher_in_the_water
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- History.txt
|
44
|
+
- Manifest.txt
|
45
|
+
files:
|
46
|
+
- History.txt
|
47
|
+
- Manifest.txt
|
48
|
+
- README.rdoc
|
49
|
+
- Rakefile
|
50
|
+
- bin/watcher_in_the_water
|
51
|
+
- lib/watcher_in_the_water.rb
|
52
|
+
- test/fixtures/dimrill_gate
|
53
|
+
- test/fixtures/durins_bridge
|
54
|
+
- test/fixtures/west_gate
|
55
|
+
- test/live.yml
|
56
|
+
- test/live_test.rb
|
57
|
+
- test/test_watcher_in_the_water.rb
|
58
|
+
- test/watcher/test.yml
|
59
|
+
has_rdoc: true
|
60
|
+
homepage: http://en.wikipedia.org/wiki/Watcher_in_the_Water
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --main
|
64
|
+
- README.txt
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project: seattlerb
|
82
|
+
rubygems_version: 1.3.1
|
83
|
+
signing_key:
|
84
|
+
specification_version: 2
|
85
|
+
summary: Want to know when the page at a given URL changes? The Watcher in the Water will tell you over Jabber.
|
86
|
+
test_files:
|
87
|
+
- test/test_watcher_in_the_water.rb
|