herald 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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, RSS, or email.
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, email, pinging a site, or running Ruby code as soon as those keywords appear.
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-<version>.gem
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 an email inbox: [Not Implemented]
42
+
43
+ Or text in a website
44
44
 
45
45
  Herald.watch do
46
- check :inbox, :imap => "imap.server.com", :user => "username", :pass => "supersecret"
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 "#yaks", "#in", "#space"
94
- action :ping, :uri => "http://counter.com/+1"
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 "#yaks", "#in", "#space"
103
- action :post, :uri => "http://yakdb.com/post"
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
- ### Timer
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 "soundofmusic"
124
- every 30 => "seconds" # or "minutes", "hours", or "days"
125
- end
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
- Callbacks allow a great deal of reflection into the internals of Herald.
127
+ ### Timer
139
128
 
140
- If the callback is passed with the scope of `Herald`, it will have access to the `Herald` methods and instance variables:
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 "#breaking", "news"
144
- action do
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 both running heralds
189
- Herald.heralds.to_s # print in English
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 # both heralds restarted
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 Binary [Not Implemented]
207
+ ### Herald binary [Not Implemented]
195
208
 
196
209
  herald -watch twitter -for #eqnz
197
210
  herald -once twitter -for #herald
data/Rakefile CHANGED
@@ -5,5 +5,5 @@ task :test do
5
5
  $LOAD_PATH.unshift './lib'
6
6
  require 'herald'
7
7
  require 'minitest/autorun'
8
- Dir.glob("test/**/*_test.rb").each { |test| require "./#{test}" }
8
+ Dir.glob("test/*_test.rb").each { |test| require "./#{test}" }
9
9
  end
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{Pass Herald some keywords and sources to watch, and Herald will notify you using Growl, email, pinging a site, or running Ruby code as soon as those keywords appear}
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 "minitest"
17
- s.add_dependency "crack"
18
- s.rubyforge_project = "herald"
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
- @watchers << Herald::Watcher.new(type, @keep_alive, options, &block)
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
- # wait before the end of this script
138
- # for all watchers to finish their jobs
139
- Process.waitpid(@subprocess)
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
- Process.kill("TERM", @subprocess) if @subprocess
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
 
@@ -14,7 +14,11 @@ class Herald
14
14
  def test; true; end
15
15
 
16
16
  def notify(item)
17
- @proc.call
17
+ if @proc.arity == 1
18
+ @proc.call(item)
19
+ else
20
+ @proc.call
21
+ end
18
22
  end
19
23
 
20
24
  end
@@ -4,27 +4,31 @@ class Herald
4
4
 
5
5
  module Growl
6
6
 
7
- # no options to parse for Growl
8
- def parse_options(options); end
7
+ @@notification_type = "Herald Notification"
9
8
 
10
- # test growl on system and throw exception if fail.
11
- def test
12
- # TODO suppress output
13
- unless(system("growl --version"))
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
- # response will be false if system() call exits with an error.
22
- # presence of ruby-growl has already been tested in test(), so a false
23
- # return is likely to be the user's Growl settings
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