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 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