geeklets 0.0.4
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/History.txt +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +57 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/bin/geeklets +8 -0
- data/lib/Get_Weather_Icon/get_weather_icon.rb +17 -0
- data/lib/Next_Months_Calendar/next_months_calendar.rb +9 -0
- data/lib/OPM_Alerts/opm_alerts.rb +61 -0
- data/lib/Trash_Usage/trash_usage.rb +8 -0
- data/lib/VRE_Alerts/vre_alerts.rb +123 -0
- data/lib/WMATA_Alerts/wmata_alerts.rb +96 -0
- data/lib/Weather/weather.rb +218 -0
- data/lib/geeklets.rb +51 -0
- data/lib/utility.rb +29 -0
- data/spec/geeklets_spec.rb +15 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/utility_spec.rb +78 -0
- data/vendor/trollop.rb +760 -0
- metadata +97 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 John F. Schank III
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= geeklets
|
2
|
+
|
3
|
+
Geeklets is a collection of scripts for use with GeekTool.
|
4
|
+
Here is the breakdown of the scripts
|
5
|
+
|
6
|
+
== Note about these scripts.
|
7
|
+
|
8
|
+
At the moment - they are all focused on my particular needs, so
|
9
|
+
they are currently hardcoded for my area of Virginia, USA. As this
|
10
|
+
project develops I will be trying to find a way to refactor that depency
|
11
|
+
away, and allow individual users to customize for their needs.
|
12
|
+
|
13
|
+
Just be aware, that right now, unless you live in the Washington Metro area,
|
14
|
+
some of these scripts won't be useful for you.
|
15
|
+
|
16
|
+
Run them with a -h parameter for details.
|
17
|
+
|
18
|
+
== get_weather_icon
|
19
|
+
|
20
|
+
Gets the current weather icon from yahoo, and saves it to the tmp directory.
|
21
|
+
|
22
|
+
== next_months_calendar
|
23
|
+
|
24
|
+
Runs the unix "cal" command for whatever the next month from now is.
|
25
|
+
|
26
|
+
== OPM_alerts
|
27
|
+
|
28
|
+
Gets the Office of Personnel Management alert for the current day.
|
29
|
+
This if for things like Federal Government closings and delays due to weather.
|
30
|
+
|
31
|
+
== trash_usage
|
32
|
+
|
33
|
+
A wrapper for the unix "du" command to display the amount of space taken up by
|
34
|
+
the traash on an OS X mac.
|
35
|
+
|
36
|
+
== VRE_alerts
|
37
|
+
|
38
|
+
Shows the alerts for today for the Virginia Rail Express
|
39
|
+
|
40
|
+
== WMATA_alerts
|
41
|
+
|
42
|
+
Shows the alerts and delays for the Washington Metro Area Transit Authority,
|
43
|
+
the "Metro" subway and trains.
|
44
|
+
|
45
|
+
== Note on Patches/Pull Requests
|
46
|
+
|
47
|
+
* Fork the project.
|
48
|
+
* Make your feature addition or bug fix.
|
49
|
+
* Add tests for it. This is important so I don't break it in a
|
50
|
+
future version unintentionally.
|
51
|
+
* Commit, do not mess with rakefile, version, or history.
|
52
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
53
|
+
* Send me a pull request. Bonus points for topic branches.
|
54
|
+
|
55
|
+
== Copyright
|
56
|
+
|
57
|
+
Copyright (c) 2010 John F. Schank III. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "geeklets"
|
8
|
+
gem.summary = %Q{Scripts for GeekTool}
|
9
|
+
gem.description = %Q{A collection of useful scripts for use with GeekTool}
|
10
|
+
gem.email = "jschank@mac.com"
|
11
|
+
gem.homepage = "http://github.com/jschank/geeklets"
|
12
|
+
gem.authors = ["John F. Schank III"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.files = FileList['lib/**/*', 'bin/*', '[A-Z]*', 'spec/**/*', 'vendor/**/*'].to_a
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "geeklets #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
data/bin/geeklets
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mechanize'
|
3
|
+
require 'rio'
|
4
|
+
|
5
|
+
class Get_Weather_Icon
|
6
|
+
|
7
|
+
def run(params)
|
8
|
+
agent = Mechanize.new
|
9
|
+
agent.get("http://weather.yahoo.com/forecast/USVA0262.html")
|
10
|
+
icon = agent.page.at(".forecast-icon")
|
11
|
+
style = icon.attributes["style"].value
|
12
|
+
icon_url = style.split("'")[1]
|
13
|
+
|
14
|
+
rio(icon_url) > rio("/tmp/weather-icon.png")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
URL = "http://www.opm.gov/status/index.aspx"
|
7
|
+
|
8
|
+
class OPM_Alerts
|
9
|
+
|
10
|
+
def die
|
11
|
+
puts <<-EOS
|
12
|
+
opm-status
|
13
|
+
|
14
|
+
USAGE:
|
15
|
+
|
16
|
+
\topm-status [wrapping-width]
|
17
|
+
|
18
|
+
Returns U.S. Office of Personnel Management status Alerts.
|
19
|
+
|
20
|
+
[wrapping-width] is an integer to limit the width of the descriptions
|
21
|
+
Defaults to: 40
|
22
|
+
|
23
|
+
EOS
|
24
|
+
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(params)
|
29
|
+
die if params[0] == "-h"
|
30
|
+
|
31
|
+
width = (params[1] and params[1].to_i) || 40
|
32
|
+
|
33
|
+
doc = Nokogiri::HTML(open(URL))
|
34
|
+
|
35
|
+
date_str = doc.css('#_ctl0__ctl0_DisplayDateSpan').text.strip
|
36
|
+
begin
|
37
|
+
date = Date.parse(date_str)
|
38
|
+
date_str = date.strftime("%x")
|
39
|
+
rescue
|
40
|
+
date_str = Date.today.strftime("%x") if date_str.length == 0
|
41
|
+
end
|
42
|
+
|
43
|
+
title = doc.css('h3').text.strip
|
44
|
+
title = "VRE Status" if title.length == 0
|
45
|
+
|
46
|
+
status = doc.css('.statusbox').text.strip
|
47
|
+
|
48
|
+
# Deal with the flaky case where OPM decides to
|
49
|
+
# display an image instead of actual text content
|
50
|
+
images = doc.xpath("//img")
|
51
|
+
status = images[0].attributes["alt"].text.strip if ((status.length == 0) && (images.count == 1))
|
52
|
+
|
53
|
+
status = "Not found" if status.length == 0
|
54
|
+
|
55
|
+
# display results
|
56
|
+
puts Utility.wrap_text("#{date_str} - #{title}", width, date_str.length + 3, :outdent)
|
57
|
+
puts "-" * width
|
58
|
+
puts Utility.wrap_text(status, width)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'chronic'
|
4
|
+
require 'mechanize'
|
5
|
+
|
6
|
+
FEED_URL = "http://www.vre.org/service/daily-download.html"
|
7
|
+
TRAIN_LINES = {"fbg_delay" => :fredericksburg, "mss_delay" => :manassas}
|
8
|
+
|
9
|
+
class VRE_Alerts
|
10
|
+
|
11
|
+
def die
|
12
|
+
puts <<-EOS
|
13
|
+
vre-alert
|
14
|
+
|
15
|
+
USAGE:
|
16
|
+
|
17
|
+
\tvre-alert [rail_lines] [wrapping-width] [specific-date]
|
18
|
+
|
19
|
+
Returns Virgina Railway Express (VRE) Alerts.
|
20
|
+
|
21
|
+
[rail_lines] specifies which rail lines you want alerts for.
|
22
|
+
Can be:
|
23
|
+
\tfredericsburg - for the Fredericsburg line
|
24
|
+
\tmanassas - for the Manassas line
|
25
|
+
\tboth - for both lines.
|
26
|
+
Defaults to: both
|
27
|
+
|
28
|
+
[wrapping-width] is an integer to limit the width of the descriptions
|
29
|
+
Defaults to: 40
|
30
|
+
|
31
|
+
[specific-date] is a string parseable by Date::parse which is the date from
|
32
|
+
the VRE daily download to display. NOTE: this does not access history, it only
|
33
|
+
allows you to choose a specific entry from the Daily Download to render. Mostly
|
34
|
+
used for debugging.
|
35
|
+
Defaults to: The current system Date.
|
36
|
+
|
37
|
+
EOS
|
38
|
+
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
# >> notice.search("tr")[2].search("td")[0].attributes["class"].value
|
43
|
+
# => "fbg2"
|
44
|
+
# >> notice.search("tr")[2].search("td")[0].text
|
45
|
+
# => "Train 300"
|
46
|
+
def parse_notice(notice)
|
47
|
+
detail = {}
|
48
|
+
detail[:summary] = notice.attributes["summary"].text
|
49
|
+
detail[:morning] = []
|
50
|
+
detail[:evening] = []
|
51
|
+
rows = notice.search("tr")
|
52
|
+
service = nil
|
53
|
+
|
54
|
+
rows.each do |row|
|
55
|
+
cells = row.search("td")
|
56
|
+
case cells.count
|
57
|
+
when 0 :
|
58
|
+
(detail[:date] = Date.parse(row.children[0].text)) if (row && row.children && row.children[0] && row.children[0].name == "th")
|
59
|
+
when 1 :
|
60
|
+
service = :morning if row.children[0].text =~ /Morning/
|
61
|
+
service = :evening if row.children[0].text =~ /Evening/
|
62
|
+
when 2 :
|
63
|
+
# sometimes they split a description across multiple rows, when they do, they leave the train cell blank
|
64
|
+
# in that case, append the description text to the last trains description.
|
65
|
+
if cells[0].text.empty? && detail[service][-1]
|
66
|
+
detail[service][-1][:description] << cells[1].text
|
67
|
+
next
|
68
|
+
end
|
69
|
+
|
70
|
+
cell_class = cells[0].attributes["class"].value unless cells[0].text.empty?
|
71
|
+
detail[service] << {:line => TRAIN_LINES[cell_class], :train => cells[0].text, :description => cells[1].text} if @rail_lines == :both || @rail_lines == TRAIN_LINES[cell_class]
|
72
|
+
else
|
73
|
+
next
|
74
|
+
end
|
75
|
+
end
|
76
|
+
detail
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_trains(trains, width)
|
80
|
+
puts " No delays" and return if trains.empty?
|
81
|
+
trains.each do |train|
|
82
|
+
puts " #{train[:line].to_s.capitalize} line #{train[:train]}"
|
83
|
+
Utility.wrap_text(train[:description], width, 3, :all)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def render_notice(notice, width)
|
88
|
+
if (notice.nil? || (notice[:morning].empty? && notice[:evening].empty?))
|
89
|
+
puts "No VRE notices for today"
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
puts notice[:summary]
|
94
|
+
puts "#{'-' * notice[:summary].length}"
|
95
|
+
puts "Morning Service"
|
96
|
+
render_trains(notice[:morning], width)
|
97
|
+
puts
|
98
|
+
puts "Afternoon/Evening Service"
|
99
|
+
render_trains(notice[:evening], width)
|
100
|
+
end
|
101
|
+
|
102
|
+
def run(params)
|
103
|
+
die if params[0] == "-h"
|
104
|
+
|
105
|
+
@rail_lines = (params[0] || "both").to_sym
|
106
|
+
width = (params[1] and params[1].to_i) || 40
|
107
|
+
alert_date = (params[2] and Date.parse(params[2])) || Date.today
|
108
|
+
|
109
|
+
#debug - set a date of interest, and rail line of interest
|
110
|
+
# alert_date = Date.civil(2010, 1, 13)
|
111
|
+
# @rail_lines = :fredericksburg
|
112
|
+
|
113
|
+
agent = Mechanize.new
|
114
|
+
agent.get(FEED_URL)
|
115
|
+
notices = agent.page.search(".format").map{ |notice| parse_notice(notice) }
|
116
|
+
|
117
|
+
#debug
|
118
|
+
#puts notices.to_yaml
|
119
|
+
|
120
|
+
render_notice(notices.select{ |notice| notice[:date] == alert_date }.shift, width)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rss'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'chronic'
|
6
|
+
require 'htmlentities'
|
7
|
+
|
8
|
+
REPLACEMENTS =
|
9
|
+
[
|
10
|
+
["p.m.", "pm"],
|
11
|
+
["a.m.", "am"]
|
12
|
+
]
|
13
|
+
|
14
|
+
FEED_URL = "http://www.wmata.com/rider_tools/metro_service_status/feeds/rail.xml"
|
15
|
+
|
16
|
+
class WMATA_Alerts
|
17
|
+
|
18
|
+
def die
|
19
|
+
puts <<-EOS
|
20
|
+
metro-feed
|
21
|
+
|
22
|
+
USAGE:
|
23
|
+
|
24
|
+
\tmetro-feed [history] [wrapping-width]
|
25
|
+
|
26
|
+
Returns WMATA feed information for metro lines.
|
27
|
+
|
28
|
+
[history] is how many days of feed history to display.
|
29
|
+
\tdefaults to 7 days. Can be anything parseable by Chronic gem
|
30
|
+
|
31
|
+
[wrapping-width] is the limit to how wide the text can be
|
32
|
+
\tdefaults to 40 characters. should be a positive integer value.
|
33
|
+
|
34
|
+
Requires gems: chronic, htmlentities
|
35
|
+
|
36
|
+
|
37
|
+
EOS
|
38
|
+
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(params)
|
43
|
+
|
44
|
+
die if params[0] == "-h"
|
45
|
+
|
46
|
+
@cutoff_string = params[0] || "7 days ago"
|
47
|
+
@width = params[1] || "40"
|
48
|
+
@width = @width.to_s.to_i
|
49
|
+
|
50
|
+
@cutoff = Chronic.parse(@cutoff_string)
|
51
|
+
|
52
|
+
@rss_content = ""
|
53
|
+
|
54
|
+
open(FEED_URL) do |f|
|
55
|
+
@rss_content = f.read
|
56
|
+
end
|
57
|
+
|
58
|
+
@rss = RSS::Parser.parse(@rss_content, false)
|
59
|
+
|
60
|
+
@item_hash = {}
|
61
|
+
|
62
|
+
# filter down to the items we want.
|
63
|
+
@rss.items.each do |item|
|
64
|
+
@item_hash[item.pubDate] = item unless item.pubDate < @cutoff
|
65
|
+
end
|
66
|
+
|
67
|
+
if @item_hash.empty?
|
68
|
+
puts "No data from WMATA feed."
|
69
|
+
else
|
70
|
+
@coder = HTMLEntities.new
|
71
|
+
@item_hash.keys.sort.reverse.each do |key|
|
72
|
+
item = @item_hash[key]
|
73
|
+
|
74
|
+
title = "#{item.pubDate.strftime('%x')} - #{item.title}"
|
75
|
+
puts title
|
76
|
+
puts ("-" * title.length)
|
77
|
+
|
78
|
+
message = @coder.decode(item.description)
|
79
|
+
REPLACEMENTS.each do |r|
|
80
|
+
message = message.gsub(r[0], r[1])
|
81
|
+
end
|
82
|
+
subject, *body = message.split(".")
|
83
|
+
|
84
|
+
puts Utility.wrap_text(subject+"...", @width)
|
85
|
+
body.each do |sentence|
|
86
|
+
puts Utility.wrap_text(sentence+".", @width, 3, :all)
|
87
|
+
end
|
88
|
+
|
89
|
+
puts
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|