herald 0.1 → 0.2
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.
- data/LICENSE +22 -0
- data/README.md +72 -59
- data/Rakefile +1 -1
- data/TODO.txt +5 -3
- data/herald.gemspec +15 -5
- data/lib/herald.rb +32 -12
- data/lib/herald/notifiers/callback.rb +5 -1
- data/lib/herald/notifiers/growl.rb +19 -15
- data/lib/herald/notifiers/ping.rb +14 -33
- data/lib/herald/notifiers/post.rb +31 -19
- data/lib/herald/untitled.txt +5 -0
- data/lib/herald/version.rb +1 -1
- data/lib/herald/watcher.rb +17 -15
- data/lib/herald/watchers/rss.rb +12 -5
- data/lib/herald/watchers/twitter.rb +6 -5
- data/lib/herald/watchers/website.rb +95 -0
- data/test/herald_test.rb +3 -9
- data/test/mocks/rss.xml +19 -0
- data/test/mocks/web.html +17 -0
- data/test/notifier_callback_test.rb +29 -0
- data/test/notifier_growl_test.rb +22 -0
- data/test/notifier_ping_test.rb +30 -0
- data/test/notifier_post_test.rb +30 -0
- data/test/notifier_stdout_test.rb +26 -0
- data/test/notifier_test.rb +38 -0
- data/test/watcher_rss_test.rb +59 -0
- data/test/watcher_test.rb +74 -0
- data/test/watcher_twitter_test.rb +17 -0
- data/test/watcher_website_test.rb +33 -0
- metadata +56 -25
- data/lib/herald/watchers/imap.rb +0 -55
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Luke Duncalfe
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Herald
|
2
2
|
====
|
3
3
|
|
4
|
-
Herald is a simple notifier for Twitter
|
4
|
+
Herald is a simple notifier for Twitter and RSS.
|
5
5
|
|
6
|
-
Pass Herald some keywords and sources to watch, and Herald will notify you using Growl,
|
6
|
+
Pass Herald some keywords and sources to watch, and Herald will notify you using Growl, pinging or posting to a site, or running Ruby code as soon as those keywords appear.
|
7
7
|
|
8
8
|
Installation
|
9
9
|
------------
|
@@ -16,34 +16,34 @@ Installation
|
|
16
16
|
|
17
17
|
git clone git://github.com/lukes/herald.git
|
18
18
|
gem build herald.gemspec
|
19
|
-
gem install herald
|
19
|
+
gem install herald-0.2.gem
|
20
20
|
|
21
21
|
Usage
|
22
22
|
-----
|
23
23
|
|
24
|
-
First step is `require` Herald into your Ruby project
|
24
|
+
First step is to `require` Herald into your Ruby project
|
25
25
|
|
26
26
|
require 'rubygems'
|
27
27
|
require 'herald'
|
28
28
|
|
29
|
-
Then, to watch for tweets containing "soundofmusic"
|
29
|
+
Then, to watch for tweets containing "soundofmusic"
|
30
30
|
|
31
31
|
Herald.watch do
|
32
32
|
check :twitter
|
33
33
|
_for "soundofmusic"
|
34
34
|
end
|
35
35
|
|
36
|
-
Or an RSS feed
|
36
|
+
Or an RSS feed
|
37
37
|
|
38
38
|
Herald.watch do
|
39
39
|
check :rss, :from => "http://example.com/.rss"
|
40
40
|
_for "soundofmusic"
|
41
41
|
end
|
42
|
-
|
43
|
-
Or
|
42
|
+
|
43
|
+
Or text in a website
|
44
44
|
|
45
45
|
Herald.watch do
|
46
|
-
check :
|
46
|
+
check :website, :from => "http://example.com"
|
47
47
|
_for "soundofmusic"
|
48
48
|
end
|
49
49
|
|
@@ -78,7 +78,7 @@ Swap in another action by passing Herald one of the following `action` parameter
|
|
78
78
|
|
79
79
|
[Growl](http://growl.info/) is a notification system for Mac OS X.
|
80
80
|
|
81
|
-
To use Growl, enable "Listen for incoming notifications" and "Allow remote application registration" on the [Network tab](http://growl.info/documentation/exploring-preferences.php) of the Growl Preference Panel, and pass Herald `action :growl
|
81
|
+
To use Growl, enable "Listen for incoming notifications" and "Allow remote application registration" on the [Network tab](http://growl.info/documentation/exploring-preferences.php) of the Growl Preference Panel, and pass Herald `action :growl`
|
82
82
|
|
83
83
|
Herald.watch_twitter do
|
84
84
|
_for "nz", "#election"
|
@@ -87,25 +87,25 @@ To use Growl, enable "Listen for incoming notifications" and "Allow remote appli
|
|
87
87
|
|
88
88
|
#### Ping
|
89
89
|
|
90
|
-
To ping a URI, pass Herald `action :ping, :uri => "http://address.to.ping"
|
90
|
+
To ping a URI, pass Herald `action :ping, :uri => "http://address.to.ping"`
|
91
91
|
|
92
92
|
Herald.watch_twitter do
|
93
|
-
_for "#
|
94
|
-
action :ping, :uri => "http://
|
93
|
+
_for "#fundamentalists", "#in", "#space"
|
94
|
+
action :ping, :uri => "http://armageddon-clock.com/+1"
|
95
95
|
end
|
96
96
|
|
97
97
|
#### Post
|
98
98
|
|
99
|
-
To post information about what Herald finds to a URI, pass Herald `action :post, :uri => "http://address.to.post.to"
|
99
|
+
To post information about what Herald finds to a URI, pass Herald `action :post, :uri => "http://address.to.post.to"`
|
100
100
|
|
101
101
|
Herald.watch_twitter do
|
102
|
-
_for "
|
103
|
-
action :post, :uri => "http://
|
102
|
+
_for "vanity", "tweeting"
|
103
|
+
action :post, :uri => "http://twitter-loves-me.com/post"
|
104
104
|
end
|
105
105
|
|
106
106
|
#### Callbacks
|
107
107
|
|
108
|
-
If you'd like to do your own thing entirely each time a keyword appears, pass a callback in the form of a Ruby block
|
108
|
+
If you'd like to do your own thing entirely each time a keyword appears, pass a callback in the form of a Ruby block
|
109
109
|
|
110
110
|
Herald.watch_twitter do
|
111
111
|
_for "revolution"
|
@@ -113,55 +113,31 @@ If you'd like to do your own thing entirely each time a keyword appears, pass a
|
|
113
113
|
`say "Viva!"`
|
114
114
|
end
|
115
115
|
end
|
116
|
+
|
116
117
|
|
117
|
-
|
118
|
-
|
119
|
-
By default Herald will sleep for 1 minute after checking each of the sources independently.
|
120
|
-
To set a different sleep time:
|
118
|
+
Pass the callback a parameter to be given a `Herald::Item` object within your block. The object makes available everything Herald could find in the source.
|
121
119
|
|
122
120
|
Herald.watch_twitter do
|
123
|
-
_for "
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
### Look Once
|
128
|
-
|
129
|
-
Rather than watching, if you just want to get a single poll of keywords, use `once()`. All the same parameters as with `watch()` can be used (except `every`).
|
130
|
-
|
131
|
-
Herald.once do
|
132
|
-
check :twitter
|
133
|
-
_for "#herald"
|
121
|
+
_for "revolution"
|
122
|
+
action do |item|
|
123
|
+
puts item.data # full Hash of all data
|
124
|
+
end
|
134
125
|
end
|
135
|
-
|
136
|
-
### Callback Scope and Herald Metaprogramming
|
137
126
|
|
138
|
-
|
127
|
+
### Timer
|
139
128
|
|
140
|
-
|
129
|
+
By default Herald will sleep for 1 minute after checking each of the sources independently.
|
130
|
+
To set a different sleep time
|
141
131
|
|
142
132
|
Herald.watch_twitter do
|
143
|
-
_for "
|
144
|
-
|
145
|
-
puts instance_variables
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
If passed in within the scope of `Herald::Watcher`, it will have access to the particular `Watcher`'s methods and instance variables:
|
150
|
-
|
151
|
-
Herald.watch do
|
152
|
-
check :twitter do
|
153
|
-
_for "#breaking", "news"
|
154
|
-
action do
|
155
|
-
puts instance_variables
|
156
|
-
end
|
157
|
-
end
|
133
|
+
_for "soundofmusic"
|
134
|
+
every 30 => "seconds" # or "minutes", "hours", or "days"
|
158
135
|
end
|
159
136
|
|
160
137
|
### For inquisitive minds
|
161
138
|
|
162
139
|
Assign the herald watcher to a variable
|
163
140
|
|
164
|
-
require 'herald'
|
165
141
|
herald = Herald.watch_rss :from => "http://www.reddit.com/r/pics/new/.rss" do
|
166
142
|
_for "imgur"
|
167
143
|
end
|
@@ -173,7 +149,7 @@ Edit your herald instance while it's working
|
|
173
149
|
herald.watchers.first.keywords << "cats"
|
174
150
|
herald.watchers.first.action { puts "callback" }
|
175
151
|
|
176
|
-
Stop and start herald
|
152
|
+
Stop and start the herald
|
177
153
|
|
178
154
|
herald.stop
|
179
155
|
herald.alive? # => false
|
@@ -183,15 +159,52 @@ Start a second herald
|
|
183
159
|
|
184
160
|
herald_the_second = Herald.watch_twitter { _for "#herald" }
|
185
161
|
|
186
|
-
Use `Herald` class methods to inspect and edit heralds as a batch
|
162
|
+
Use the `Herald` class methods to inspect and edit your heralds as a batch
|
187
163
|
|
188
|
-
Herald.heralds # prints
|
189
|
-
Herald.
|
190
|
-
Herald.stop # both heralds stopped
|
164
|
+
Herald.heralds # prints all running heralds
|
165
|
+
Herald.stop # all heralds stopped
|
191
166
|
Herald.alive? # => false
|
192
|
-
Herald.start #
|
167
|
+
Herald.start # all heralds restarted
|
168
|
+
|
169
|
+
### Look once
|
170
|
+
|
171
|
+
Rather than watching, if you just want to get a single poll of keywords, use `once()`. All the same parameters as with `watch()` can be used (`every` will be ignored)
|
172
|
+
|
173
|
+
herald = Herald.once do
|
174
|
+
check :twitter
|
175
|
+
_for "#herald"
|
176
|
+
end
|
177
|
+
|
178
|
+
As with watching, Herald will fork a new process (or one for every source you're `check`ing), but unlike with watching, Herald will block and wait for the process to finish
|
179
|
+
|
180
|
+
herald.start # waiting ... process ends at the same time you receive your notifications
|
181
|
+
herald.alive? # => false
|
182
|
+
|
183
|
+
### A bit about callback scope and Herald metaprogramming
|
184
|
+
|
185
|
+
Callbacks allow a great deal of reflection into the internals of Herald.
|
186
|
+
|
187
|
+
If the callback is passed with the scope of `Herald`, it will have access to the `Herald` methods and instance variables
|
188
|
+
|
189
|
+
Herald.watch_twitter do
|
190
|
+
_for "#breaking", "news"
|
191
|
+
action do
|
192
|
+
puts instance_variables
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
If passed in within the scope of `Herald::Watcher` (`action` agents), it will have access to the particular `Watcher`'s methods and instance variables
|
197
|
+
|
198
|
+
Herald.watch do
|
199
|
+
check :twitter do
|
200
|
+
_for "#breaking", "news"
|
201
|
+
action do
|
202
|
+
puts instance_variables
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
193
206
|
|
194
|
-
### Herald
|
207
|
+
### Herald binary [Not Implemented]
|
195
208
|
|
196
209
|
herald -watch twitter -for #eqnz
|
197
210
|
herald -once twitter -for #herald
|
data/Rakefile
CHANGED
data/TODO.txt
CHANGED
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
- Best way to require gems? Can I vendorise crack gem?
|
4
4
|
|
5
|
-
- Suppress Growl text on test()
|
6
|
-
|
7
5
|
- Comment in RDOC
|
8
6
|
|
9
7
|
- Handle exceptions in processes, by reporting the error, then set the @subprocess to nil
|
10
8
|
|
11
9
|
- Look at Gemspec
|
12
|
-
- Show dependency on ruby-growl if installed for mac
|
10
|
+
- Show dependency on ruby-growl if installed for mac
|
11
|
+
|
12
|
+
- Growl icon
|
13
|
+
|
14
|
+
- Custom Error classes
|
data/herald.gemspec
CHANGED
@@ -10,15 +10,25 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = ["lduncalfe@eml.cc"]
|
11
11
|
s.homepage = "http://github.com/lukes/herald"
|
12
12
|
s.summary = %q{A simple notifier for Twitter, RSS, or email}
|
13
|
-
s.description = %q{
|
13
|
+
s.description = %q{A simple and flexible notifier for Twitter, RSS, or email}
|
14
14
|
|
15
|
-
s.required_rubygems_version = ">= 1.3.6" # TODO test earliest dependency
|
16
|
-
s.add_development_dependency
|
17
|
-
s.add_dependency
|
18
|
-
s.
|
15
|
+
# s.required_rubygems_version = ">= 1.3.6" # TODO test earliest dependency
|
16
|
+
s.add_development_dependency("minitest")
|
17
|
+
s.add_dependency("crack")
|
18
|
+
s.add_dependency("hpricot", "~> 0.8")
|
19
|
+
# if gem is being installed on a mac, add a dependency on ruby-growl.
|
20
|
+
# note, this is not a fool-proof method of detecting the OS,
|
21
|
+
# apparently if return "java" if platform is JRuby
|
22
|
+
if RUBY_PLATFORM.downcase.include?("darwin")
|
23
|
+
s.add_dependency("ruby-growl", "~> 3.0")
|
24
|
+
end
|
25
|
+
|
26
|
+
s.rubyforge_project = "herald"
|
27
|
+
s.extra_rdoc_files = ["README.md", "LICENSE"]
|
19
28
|
|
20
29
|
s.files = `git ls-files`.split("\n")
|
21
30
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
22
31
|
# s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
32
|
+
# s.default_executable = "herald"
|
23
33
|
s.require_paths = ["lib"]
|
24
34
|
end
|
data/lib/herald.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
#require 'json'
|
3
|
-
gem 'crack'
|
4
|
-
require 'crack'
|
5
2
|
|
6
3
|
require 'herald/watcher'
|
7
4
|
require 'herald/notifier'
|
@@ -11,6 +8,7 @@ require 'herald/item'
|
|
11
8
|
class Herald
|
12
9
|
|
13
10
|
@@heralds = []
|
11
|
+
|
14
12
|
attr_accessor :watchers, :keep_alive, :subprocess
|
15
13
|
|
16
14
|
def self.watch(&block)
|
@@ -31,6 +29,9 @@ class Herald
|
|
31
29
|
def self.watch_rss(options = {}, &block)
|
32
30
|
watch() { check(:rss, options, &block) }
|
33
31
|
end
|
32
|
+
def self.watch_website(options = {}, &block)
|
33
|
+
watch() { check(:website, options, &block) }
|
34
|
+
end
|
34
35
|
|
35
36
|
# batch methods
|
36
37
|
def self.start
|
@@ -88,7 +89,9 @@ class Herald
|
|
88
89
|
|
89
90
|
# create a new Watcher
|
90
91
|
def check(type, options = {}, &block)
|
91
|
-
|
92
|
+
options[:keep_alive] ||= @keep_alive
|
93
|
+
@watchers << Herald::Watcher.new(type, options, &block)
|
94
|
+
self
|
92
95
|
end
|
93
96
|
|
94
97
|
# send keywords to Watchers
|
@@ -96,6 +99,7 @@ class Herald
|
|
96
99
|
@watchers.each do |watcher|
|
97
100
|
watcher._for(*keywords)
|
98
101
|
end
|
102
|
+
self
|
99
103
|
end
|
100
104
|
|
101
105
|
# send instructions to Watchers
|
@@ -103,6 +107,7 @@ class Herald
|
|
103
107
|
@watchers.each do |watcher|
|
104
108
|
watcher.action(type, options, &block)
|
105
109
|
end
|
110
|
+
self
|
106
111
|
end
|
107
112
|
|
108
113
|
# send sleep time to Watchers
|
@@ -110,6 +115,7 @@ class Herald
|
|
110
115
|
@watchers.each do |watcher|
|
111
116
|
watcher.every(time)
|
112
117
|
end
|
118
|
+
self
|
113
119
|
end
|
114
120
|
|
115
121
|
# start Watchers
|
@@ -134,9 +140,18 @@ class Herald
|
|
134
140
|
if @keep_alive
|
135
141
|
Process.detach(@subprocess)
|
136
142
|
else
|
137
|
-
|
138
|
-
|
139
|
-
|
143
|
+
begin
|
144
|
+
# wait before the end of this script
|
145
|
+
# for all watchers to finish their jobs
|
146
|
+
Process.waitpid(@subprocess)
|
147
|
+
# if @subprocess PID does not exist, it will
|
148
|
+
# be due to an error in the subprocess
|
149
|
+
# which has terminated it and waitpid()
|
150
|
+
# will throw an exception
|
151
|
+
rescue Errno::ECHILD => e
|
152
|
+
# do nothing
|
153
|
+
end
|
154
|
+
@subprocess = nil # signal unalive state
|
140
155
|
end
|
141
156
|
self # return instance object
|
142
157
|
end
|
@@ -145,7 +160,16 @@ class Herald
|
|
145
160
|
# or have watchers do cleanup tasks on exit?
|
146
161
|
# look at GOD
|
147
162
|
def stop
|
148
|
-
|
163
|
+
if @subprocess
|
164
|
+
begin
|
165
|
+
Process.kill("TERM", @subprocess)
|
166
|
+
# if @subprocess PID does not exist,
|
167
|
+
# this will be due to an error in the subprocess
|
168
|
+
# which has terminated it
|
169
|
+
rescue Errno::ESRCH => e
|
170
|
+
# do nothing
|
171
|
+
end
|
172
|
+
end
|
149
173
|
@subprocess = nil
|
150
174
|
self
|
151
175
|
end
|
@@ -155,10 +179,6 @@ class Herald
|
|
155
179
|
def alive?
|
156
180
|
!!@subprocess
|
157
181
|
end
|
158
|
-
|
159
|
-
def to_s
|
160
|
-
"#{alive? ? 'Alive' : 'Stopped'}Alive"
|
161
|
-
end
|
162
182
|
|
163
183
|
private
|
164
184
|
|
@@ -4,27 +4,31 @@ class Herald
|
|
4
4
|
|
5
5
|
module Growl
|
6
6
|
|
7
|
-
|
8
|
-
def parse_options(options); end
|
7
|
+
@@notification_type = "Herald Notification"
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# TODO throw custom exception
|
15
|
-
raise "ruby-growl gem is not installed. Run:\n\tsudo gem install ruby-growl"
|
9
|
+
def self.extended(base)
|
10
|
+
Herald.lazy_load('ruby-growl')
|
11
|
+
class << base
|
12
|
+
attr_accessor :growl, :sticky
|
16
13
|
end
|
17
14
|
end
|
18
15
|
|
16
|
+
def parse_options(options)
|
17
|
+
@sticky = options.delete(:sticky) || false
|
18
|
+
@priority = options.delete(:priority) || 1
|
19
|
+
host = options.delete(:host) || "localhost"
|
20
|
+
pass = options.delete(:pass) || nil
|
21
|
+
@growl = ::Growl.new(host, "Herald", [@@notification_type], [@@notification_type], pass)
|
22
|
+
end
|
23
|
+
|
24
|
+
# no tests for Growl
|
25
|
+
def test; end
|
26
|
+
|
19
27
|
# send a Growl notification
|
20
28
|
def notify(item)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# TODO escape characters in title and message
|
25
|
-
item.title.gsub!("'", '')
|
26
|
-
item.message.gsub!("'", '')
|
27
|
-
if !system("growl -H localhost -t '#{item.title}' -m '#{item.message}'")
|
29
|
+
begin
|
30
|
+
@growl.notify(@@notification_type, item.title, item.message, @priority, @sticky)
|
31
|
+
rescue Errno::ECONNREFUSED => e
|
28
32
|
# TODO throw custom exception
|
29
33
|
raise "Growl settings not configured to allow remote application registration. See Growl website docs: http://growl.info/documentation/exploring-preferences.php"
|
30
34
|
end
|