snifter 0.1.0.pre

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.
Files changed (69) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +46 -0
  3. data/LICENSE +20 -0
  4. data/README.md +38 -0
  5. data/Rakefile +16 -0
  6. data/bin/snifter +19 -0
  7. data/coverage/assets/0.5.3/app.js +88 -0
  8. data/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  9. data/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  10. data/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  11. data/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  12. data/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  13. data/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  14. data/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  15. data/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  16. data/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  17. data/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  18. data/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  19. data/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  20. data/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  21. data/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  22. data/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  23. data/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  24. data/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  25. data/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  26. data/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  27. data/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  28. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
  29. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  30. data/coverage/assets/0.5.3/favicon_green.png +0 -0
  31. data/coverage/assets/0.5.3/favicon_red.png +0 -0
  32. data/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  33. data/coverage/assets/0.5.3/highlight.css +129 -0
  34. data/coverage/assets/0.5.3/highlight.pack.js +1 -0
  35. data/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
  36. data/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
  37. data/coverage/assets/0.5.3/jquery.timeago.js +141 -0
  38. data/coverage/assets/0.5.3/jquery.url.js +174 -0
  39. data/coverage/assets/0.5.3/loading.gif +0 -0
  40. data/coverage/assets/0.5.3/magnify.png +0 -0
  41. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  42. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  43. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  44. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  45. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  46. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  47. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  48. data/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  49. data/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  50. data/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  51. data/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  52. data/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  53. data/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  54. data/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  55. data/coverage/assets/0.5.3/stylesheet.css +383 -0
  56. data/coverage/index.html +875 -0
  57. data/credentials-template.yml +10 -0
  58. data/lib/snifter.rb +7 -0
  59. data/lib/snifter/app.rb +13 -0
  60. data/lib/snifter/cli.rb +45 -0
  61. data/lib/snifter/readable_time.rb +19 -0
  62. data/lib/snifter/sniffer.rb +25 -0
  63. data/lib/snifter/version.rb +4 -0
  64. data/test/unit/app_test.rb +25 -0
  65. data/test/unit/cli_test.rb +33 -0
  66. data/test/unit/readable_time_test.rb +61 -0
  67. data/test/unit/sniffer_test.rb +40 -0
  68. data/test/unit/test_helper.rb +8 -0
  69. metadata +195 -0
@@ -0,0 +1,10 @@
1
+ # Get the following tokens by registering an app at https://dev.twitter.com
2
+ # twitter lets you generate access tokens for immediate development
3
+ # in the application settings section
4
+
5
+ consumer_key: ck
6
+ consumer_secret: cs
7
+ access_token: at
8
+ access_token_secret: ats
9
+
10
+ # Then duplicate/rename this file to ~/.snifter for the command line utility
@@ -0,0 +1,7 @@
1
+ require 'twitter'
2
+
3
+ require 'snifter/app'
4
+ require 'snifter/cli'
5
+ require 'snifter/readable_time'
6
+ require 'snifter/sniffer'
7
+ require 'snifter/version'
@@ -0,0 +1,13 @@
1
+ module Snifter
2
+ def self.store_consumer(credentials, service=Twitter)
3
+ service.configure do |config|
4
+ config.consumer_key = credentials['consumer_key']
5
+ config.consumer_secret = credentials['consumer_secret']
6
+ end
7
+ end
8
+
9
+ def self.make_client(credentials)
10
+ Twitter::Client.new(:oauth_token => credentials['access_token'],
11
+ :oauth_token_secret => credentials['access_token_secret'])
12
+ end
13
+ end
@@ -0,0 +1,45 @@
1
+ module Snifter
2
+ class CLI
3
+ def echo tweets
4
+ problem_rgx = /(unwatchable|slow|poor|(problem|error|issue)(s)?|fail(ed|ing|s)?|buffer(ing|ed)?|fuck(e(d|r)|ing|s)?)/i
5
+
6
+ tweets.reverse!
7
+ tweets.each { |t|
8
+ text = t.text.gsub(/\n/, ' ')
9
+ highlight text, problem_rgx
10
+
11
+ user = sprintf "%-17s", t.from_user
12
+ time = sprintf "%-12s", ReadableTime.relative(t.created_at)
13
+ rtweet = sprintf "%-3s", t.retweet_count
14
+
15
+ puts "#{yellow user} #{white time} #{green rtweet} #{cyan text}\n"
16
+ }
17
+ end
18
+
19
+ private
20
+
21
+ def highlight str, rgx
22
+ str.gsub!(rgx) {|s| "\033[33m#{s}\033[36m" }
23
+ end
24
+
25
+ #red 31
26
+ #blue 34
27
+ #magenta 35
28
+
29
+ def green txt
30
+ "\033[32m#{txt}\033[0m"
31
+ end
32
+
33
+ def yellow txt
34
+ "\033[33m#{txt}\033[0m"
35
+ end
36
+
37
+ def cyan txt
38
+ "\033[36m#{txt}\033[0m"
39
+ end
40
+
41
+ def white txt
42
+ "\033[37m#{txt}\033[0m"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ module ReadableTime
2
+ def self.relative to
3
+ a = (Time.now-to).to_i
4
+
5
+ case a
6
+ when 0 then 'Just now'
7
+ when 1 then 'A sec ago'
8
+ when 2..59 then a.to_s+' secs ago'
9
+ when 60..119 then 'A min ago' #120 = 2 minutes
10
+ when 120..3540 then (a/60).to_i.to_s+' mins ago'
11
+ when 3541..7100 then 'An hour ago' # 3600 = 1 hour
12
+ when 7101..82800 then ((a+99)/3600).to_i.to_s+' hours ago'
13
+ when 82801..172000 then 'A day ago' # 86400 = 1 day
14
+ when 172001..518400 then ((a+800)/(60*60*24)).to_i.to_s+' days ago'
15
+ when 518400..1036800 then 'A week ago'
16
+ else ((a+180000)/(60*60*24*7)).to_i.to_s+' weeks ago'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Snifter
2
+ class Sniffer
3
+ attr_reader :topic, :topic_fails
4
+
5
+ def initialize(client, topic)
6
+ @client = client
7
+
8
+ @topic = topic
9
+ @topic_fails = '### fail OR problem OR issue OR error OR slow OR buffer -"no problem"'.sub('###', topic)
10
+ end
11
+
12
+ def mentions
13
+ @client.search(@topic, :count => 100).results
14
+ end
15
+
16
+ def fails
17
+ @client.search(@topic_fails, :count => 100).results
18
+ end
19
+
20
+ def fails_in_last_hour
21
+ hour_ago = Time.new - 360
22
+ fails.reject { |tweet| tweet.created_at < hour_ago }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ module Snifter
2
+ NAME = 'snifter'
3
+ VERSION = '0.1.0.pre'
4
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class AppTest < Test::Unit::TestCase
4
+ context "A Snifter setup" do
5
+
6
+ should "configure the Application with a consumer key and secret" do
7
+ credentials = {'consumer_key' => 'skeleton', 'consumer_secret' => 'closet'}
8
+
9
+ config = mock()
10
+ config.expects(:consumer_key=).with('skeleton').once
11
+ config.expects(:consumer_secret=).with('closet').once
12
+
13
+ service = mock()
14
+ service.stubs(:configure).yields(config)
15
+
16
+ Snifter.store_consumer(credentials, service)
17
+ end
18
+
19
+ should "create clients with user account credentials" do
20
+ credentials = {'access_token' => '', 'access_token_secret' => '', }
21
+
22
+ assert_not_nil(Snifter.make_client(credentials))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ class SnifferTest < Test::Unit::TestCase
4
+
5
+ context "A sniffer" do
6
+
7
+ setup do
8
+ @cli = Snifter::CLI.new
9
+ @real_out = $stdout
10
+ $stdout = StringIO.new
11
+ end
12
+
13
+ teardown do
14
+ @cli = nil
15
+ $stdout = @real_out
16
+ end
17
+
18
+ should "output a list of tweets" do
19
+ tweet = mock()
20
+ tweet.expects(:from_user).returns('AdaLovelace').once
21
+ tweet.expects(:created_at).returns(Time.new).once
22
+ tweet.expects(:retweet_count).returns(0).once
23
+ tweet.expects(:text).returns('Whipped up Twitter on the old analytical engine').once
24
+
25
+ @cli.echo [tweet]
26
+
27
+ assert_match(/AdaLovelace/, $stdout.string)
28
+ assert_match(/Just now/, $stdout.string)
29
+ assert_match(/0/, $stdout.string)
30
+ assert_match(/Whipped up/, $stdout.string)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+
3
+ class ReadableTimeTest < Test::Unit::TestCase
4
+
5
+ context "Describing how long ago an event was" do
6
+
7
+ should "be just now" do
8
+ assert_equal('Just now', ReadableTime.relative(Time.new))
9
+ end
10
+
11
+ should "be a second ago" do
12
+ assert_equal('A sec ago', ReadableTime.relative(Time.new-1))
13
+ end
14
+
15
+ should "be seconds ago" do
16
+ assert_equal('2 secs ago', ReadableTime.relative(Time.new-2))
17
+ assert_equal('59 secs ago', ReadableTime.relative(Time.new-59))
18
+ end
19
+
20
+ should "be a minute ago" do
21
+ assert_equal("A min ago", ReadableTime.relative(Time.new-60))
22
+ assert_equal("A min ago", ReadableTime.relative(Time.new-119))
23
+ end
24
+
25
+ should "be minutes ago" do
26
+ assert_equal("2 mins ago", ReadableTime.relative(Time.new-120))
27
+ assert_equal("59 mins ago", ReadableTime.relative(Time.new-3540))
28
+ end
29
+
30
+ should "be an hour ago" do
31
+ assert_equal("An hour ago", ReadableTime.relative(Time.new-3541))
32
+ assert_equal("An hour ago", ReadableTime.relative(Time.new-7100))
33
+ end
34
+
35
+ should "be hours ago" do
36
+ assert_equal("2 hours ago", ReadableTime.relative(Time.new-7101))
37
+ assert_equal("23 hours ago", ReadableTime.relative(Time.new-82800))
38
+ end
39
+
40
+ should "be a day ago" do
41
+ assert_equal("A day ago", ReadableTime.relative(Time.new-82801))
42
+ assert_equal("A day ago", ReadableTime.relative(Time.new-172000))
43
+ end
44
+
45
+ should "be days ago" do
46
+ assert_equal("2 days ago", ReadableTime.relative(Time.new-172001))
47
+ assert_equal("6 days ago", ReadableTime.relative(Time.new-518400))
48
+ end
49
+
50
+ should "be a week ago" do
51
+ assert_equal("A week ago", ReadableTime.relative(Time.new-518401))
52
+ assert_equal("A week ago", ReadableTime.relative(Time.new-1036800))
53
+ end
54
+
55
+ should "be weeks ago" do
56
+ assert_equal("2 weeks ago", ReadableTime.relative(Time.new-1036801))
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class SnifferTest < Test::Unit::TestCase
4
+
5
+ context "A sniffer" do
6
+
7
+ setup do
8
+ @client = mock()
9
+ @sniffer = Snifter::Sniffer.new(@client, 'lovelace')
10
+ end
11
+
12
+ teardown do
13
+ @sniffer = nil
14
+ end
15
+
16
+ should "have a product name to search for" do
17
+ assert_equal('lovelace', @sniffer.topic)
18
+ end
19
+
20
+ should "have a failure topic to search for" do
21
+ assert_equal('lovelace fail OR problem OR issue OR error OR slow OR buffer -"no problem"',
22
+ @sniffer.topic_fails)
23
+ end
24
+
25
+ should "Discover tweets mentioning the product" do
26
+ @client.expects(:search).with('lovelace', {:count => 100}).returns(stub(:results => [])).once
27
+ @sniffer.mentions
28
+ end
29
+
30
+ should "Discover tweets mentioning the product and terms of failure" do
31
+ @client.expects(:search).returns(stub(:results => [])).once
32
+ @sniffer.fails
33
+ end
34
+
35
+ should "Discover how many tweets in the last hour have mentioned issues" do
36
+ @client.expects(:search).returns(stub(:results => [])).once
37
+ @sniffer.fails_in_last_hour
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,8 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter 'test'
4
+ end
5
+
6
+ require "rubygems"
7
+ require "bundler"
8
+ Bundler.require :development, :default
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snifter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Simon Gregory
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: twitter
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.2
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: shoulda
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mocha
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: simplecov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Keeping an eye on twitter to monitor user feedback for your product
95
+ email: simon.gregory@bbc.co.uk
96
+ executables:
97
+ - snifter
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - bin/snifter
102
+ - coverage/assets/0.5.3/app.js
103
+ - coverage/assets/0.5.3/fancybox/blank.gif
104
+ - coverage/assets/0.5.3/fancybox/fancy_close.png
105
+ - coverage/assets/0.5.3/fancybox/fancy_loading.png
106
+ - coverage/assets/0.5.3/fancybox/fancy_nav_left.png
107
+ - coverage/assets/0.5.3/fancybox/fancy_nav_right.png
108
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_e.png
109
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_n.png
110
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png
111
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png
112
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_s.png
113
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_se.png
114
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png
115
+ - coverage/assets/0.5.3/fancybox/fancy_shadow_w.png
116
+ - coverage/assets/0.5.3/fancybox/fancy_title_left.png
117
+ - coverage/assets/0.5.3/fancybox/fancy_title_main.png
118
+ - coverage/assets/0.5.3/fancybox/fancy_title_over.png
119
+ - coverage/assets/0.5.3/fancybox/fancy_title_right.png
120
+ - coverage/assets/0.5.3/fancybox/fancybox-x.png
121
+ - coverage/assets/0.5.3/fancybox/fancybox-y.png
122
+ - coverage/assets/0.5.3/fancybox/fancybox.png
123
+ - coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css
124
+ - coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js
125
+ - coverage/assets/0.5.3/favicon_green.png
126
+ - coverage/assets/0.5.3/favicon_red.png
127
+ - coverage/assets/0.5.3/favicon_yellow.png
128
+ - coverage/assets/0.5.3/highlight.css
129
+ - coverage/assets/0.5.3/highlight.pack.js
130
+ - coverage/assets/0.5.3/jquery-1.6.2.min.js
131
+ - coverage/assets/0.5.3/jquery.dataTables.min.js
132
+ - coverage/assets/0.5.3/jquery.timeago.js
133
+ - coverage/assets/0.5.3/jquery.url.js
134
+ - coverage/assets/0.5.3/loading.gif
135
+ - coverage/assets/0.5.3/magnify.png
136
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
137
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
138
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
139
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
140
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png
141
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
142
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
143
+ - coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
144
+ - coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png
145
+ - coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png
146
+ - coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png
147
+ - coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png
148
+ - coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png
149
+ - coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css
150
+ - coverage/assets/0.5.3/stylesheet.css
151
+ - coverage/index.html
152
+ - credentials-template.yml
153
+ - Gemfile
154
+ - Gemfile.lock
155
+ - lib/snifter/app.rb
156
+ - lib/snifter/cli.rb
157
+ - lib/snifter/readable_time.rb
158
+ - lib/snifter/sniffer.rb
159
+ - lib/snifter/version.rb
160
+ - lib/snifter.rb
161
+ - LICENSE
162
+ - Rakefile
163
+ - README.md
164
+ - test/unit/app_test.rb
165
+ - test/unit/cli_test.rb
166
+ - test/unit/readable_time_test.rb
167
+ - test/unit/sniffer_test.rb
168
+ - test/unit/test_helper.rb
169
+ homepage: http://github.com/simongregory/snifter
170
+ licenses: []
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - - lib
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: 1.3.6
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 1.8.24
191
+ signing_key:
192
+ specification_version: 3
193
+ summary: Robotic Twitter Monitor
194
+ test_files: []
195
+ has_rdoc: