tweettail 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 2009-05-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,32 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/tweet-tail
7
+ bin/tweet_tail
8
+ bin/tweettail
9
+ features/cli.feature
10
+ features/development.feature
11
+ features/fixtures/search-jaoo-since-1682666650.json
12
+ features/fixtures/search-jaoo.json
13
+ features/fixtures/search-jaoo.short.json
14
+ features/step_definitions/common_steps.rb
15
+ features/step_definitions/executable_steps.rb
16
+ features/step_definitions/logger_steps.rb
17
+ features/step_definitions/twitter_data_steps.rb
18
+ features/support/common.rb
19
+ features/support/env.rb
20
+ features/support/logger.rb
21
+ features/support/matchers.rb
22
+ features/support/mocha.rb
23
+ features/support/time_machine_helpers.rb
24
+ lib/tweet-tail.rb
25
+ lib/tweet-tail/cli.rb
26
+ lib/tweet-tail/tweet_poller.rb
27
+ script/console
28
+ script/destroy
29
+ script/generate
30
+ test/test_helper.rb
31
+ test/test_tweet-tail_cli.rb
32
+ test/test_tweet_poller.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on tweet-tail, see http://tweet-tail.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,55 @@
1
+ = tweet-tail
2
+
3
+ * http://github.com/drnic/tweet-tail
4
+
5
+ == DESCRIPTION:
6
+
7
+ Get the latest search results streaming to your console:
8
+
9
+ $ tweet-tail railsconf
10
+ rubysolo: protip: it helps to actually READ the error message. #railsconf
11
+ voxxit: So, everyone, how is #railsconf coming? When is the big 3.0 announcement?
12
+ JesseGoldberg: @GavinStark I don't have as much to chat about as you do while you are at RailsConf.
13
+ wndxlori: Anyone else not eaten yet #railsconf
14
+ zenmatt: Great dinner and coversation with @heroku at n9ne in the palms. #railsconf
15
+ Adkron: Damn you #railsconf why can I not visit you this year. I'm missing all the gitjour goodness.
16
+ pengwynn: Meeting a lot of great folks at the open gov hackathon at #railsconf #gov20
17
+ davidjrice: Enjoying ordering taxis to our hotel... "for the wynn!" #railsconf quick noms at stripburger then whiskeys at the stage door with ey ftw!
18
+ cricketgeek: as pointed out by @jnewland at sushi this evening... http://pic.im/2LY #railsconf
19
+ paulog: had fun at gilt groupe coctail party. props. #railsconf
20
+ Amuse_Bouche: I hope my two favorite people in the world form an alliance! (Swoon) RT: @dhh Loved talking to @tferris at #railsconf. So much resonates.
21
+ abie: At open gov BOF #railsconf
22
+ matthewcarriere: running a saas bof was great... I hope it gets some more time this week. #railsconf
23
+ jdar: @tullytully RT @dgou:for the benefit of people at #railsconf keynote, here is penelope trunk on tim ferris: http://bit.ly/b81E
24
+ yorzi: Reading: "Rails 3 and the Real Secret to High Productivity: RailsConf 2009 - May 04 - 07, 2009, Las Vegas,NV" ( http://tinyurl.com/czmkxn )
25
+
26
+ Or let it sit there all day with the `-f` option (as like `tail -f`)
27
+
28
+ == INSTALL:
29
+
30
+ * sudo gem install tweet-tail
31
+
32
+ == LICENSE:
33
+
34
+ (The MIT License)
35
+
36
+ Copyright (c) 2009 Dr Nic Williams
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining
39
+ a copy of this software and associated documentation files (the
40
+ 'Software'), to deal in the Software without restriction, including
41
+ without limitation the rights to use, copy, modify, merge, publish,
42
+ distribute, sublicense, and/or sell copies of the Software, and to
43
+ permit persons to whom the Software is furnished to do so, subject to
44
+ the following conditions:
45
+
46
+ The above copyright notice and this permission notice shall be
47
+ included in all copies or substantial portions of the Software.
48
+
49
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
50
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
53
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
54
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
55
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ %w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
3
+ require File.dirname(__FILE__) + '/lib/tweet-tail'
4
+
5
+ # Generate all the Rake tasks
6
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
7
+ $hoe = Hoe.new('tweettail', TweetTail::VERSION) do |p|
8
+ p.developer('Dr Nic', 'drnicwilliams@gmail.com')
9
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
+ p.rubyforge_name = 'drnicutilities'
11
+ p.extra_dev_deps = [
12
+ ['newgem', ">= #{::Newgem::VERSION}"]
13
+ ]
14
+
15
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
16
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
17
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
18
+ p.rsync_args = '-av --delete --ignore-errors'
19
+ end
20
+
21
+ require 'newgem/tasks' # load /tasks/*.rake
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # task :default => [:spec, :features]
data/bin/tweet-tail ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-5-1 by Dr Nic Williams
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/tweet-tail")
8
+ require "tweet-tail/cli"
9
+
10
+ TweetTail::CLI.execute(STDOUT, ARGV)
data/bin/tweet_tail ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-5-1 by Dr Nic Williams
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/tweet-tail")
8
+ require "tweet-tail/cli"
9
+
10
+ TweetTail::CLI.execute(STDOUT, ARGV)
data/bin/tweettail ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-5-1 by Dr Nic Williams
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/tweet-tail")
8
+ require "tweet-tail/cli"
9
+
10
+ TweetTail::CLI.execute(STDOUT, ARGV)
@@ -0,0 +1,39 @@
1
+ Feature: Live twitter search results on command line
2
+ In order to reduce cost of getting live search results
3
+ As a twitter user
4
+ I want twitter search results appearing in the console
5
+
6
+ Scenario: Display some search results
7
+ Given twitter has some search results for "jaoo"
8
+ When I run local executable "tweet-tail" with arguments "jaoo"
9
+ Then I should see some twitter messages
10
+
11
+ Scenario: Display explicit search results
12
+ Given twitter has some search results for "jaoo"
13
+ When I run executable internally with arguments "jaoo"
14
+ Then I should see
15
+ """
16
+ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
17
+ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
18
+ theRMK: Come speak with Matt at JAOO next week
19
+ drnic: reading my own abstract for JAOO presentation
20
+ """
21
+
22
+ Scenario: Poll for results until app cancelled
23
+ Given twitter has some search results for "jaoo"
24
+ When I run executable internally with arguments "jaoo -f" and wait 1 sleep cycle and quit
25
+ Then I should see
26
+ """
27
+ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
28
+ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
29
+ theRMK: Come speak with Matt at JAOO next week
30
+ drnic: reading my own abstract for JAOO presentation
31
+ CaioProiete: Wish I could be at #JAOO Australia...
32
+ """
33
+
34
+ Scenario: Show help if no search query passed
35
+ When I run local executable "tweet-tail" with arguments ""
36
+ Then I should see help option "-f"
37
+
38
+
39
+
@@ -0,0 +1,13 @@
1
+ Feature: Development processes of newgem itself (rake tasks)
2
+
3
+ As a Newgem maintainer or contributor
4
+ I want rake tasks to maintain and release the gem
5
+ So that I can spend time on the tests and code, and not excessive time on maintenance processes
6
+
7
+ Scenario: Generate RubyGem
8
+ Given this project is active project folder
9
+ And "pkg" folder is deleted
10
+ When I invoke task "rake gem"
11
+ Then folder "pkg" is created
12
+ And file with name matching "pkg/*.gem" is created else you should run "rake manifest" to fix this
13
+ And gem spec key "rdoc_options" contains /--mainREADME.rdoc/
@@ -0,0 +1,22 @@
1
+ {
2
+ "results": [{
3
+ "text": "Wish I could be at #JAOO Australia...",
4
+ "to_user_id": null,
5
+ "from_user": "CaioProiete",
6
+ "id": 1711269079,
7
+ "from_user_id": 4936114,
8
+ "iso_language_code": "en",
9
+ "source": "<a href="http:\/\/www.twhirl.org\/">twhirl<\/a>",
10
+ "profile_image_url": "http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/48685982\/image_normal.jpg",
11
+ "created_at": "Tue, 05 May 2009 23:33:52 +0000"
12
+ }],
13
+ "since_id": 1682666650,
14
+ "max_id": 1711269079,
15
+ "refresh_url": "?since_id=1711269079&q=jaoo",
16
+ "results_per_page": 15,
17
+ "next_page": "?page=2&max_id=1711269079&q=jaoo",
18
+ "warning": "since_id removed for pagination.",
19
+ "completed_in": 0.017173,
20
+ "page": 1,
21
+ "query": "jaoo"
22
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "results": [
3
+ {
4
+ "text": "reading my own abstract for JAOO presentation",
5
+ "to_user_id": null,
6
+ "from_user": "drnic",
7
+ "id": 1666627310,
8
+ "from_user_id": 5075,
9
+ "iso_language_code": "en",
10
+ "source": "<a href="http:\/\/iconfactory.com\/software\/twitterrific">twitterrific<\/a>",
11
+ "profile_image_url": "http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/55631479\/Photo_24_normal.jpg",
12
+ "created_at": "Fri, 01 May 2009 04:45:42 +0000"
13
+ },
14
+ {
15
+ "text": "Come speak with Matt at JAOO next week",
16
+ "to_user_id": null,
17
+ "from_user": "theRMK",
18
+ "id": 1666334207,
19
+ "from_user_id": 8783115,
20
+ "iso_language_code": "en",
21
+ "source": "<a href="http:\/\/83degrees.com\/to\/powertwitter">Power Twitter<\/a>",
22
+ "profile_image_url": "http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/110356397\/rmk_manga2_normal.jpg",
23
+ "created_at": "Fri, 01 May 2009 04:01:38 +0000"
24
+ },
25
+ {
26
+ "text": "@VenessaP I think they went out for noodles. #jaoo",
27
+ "to_user_id": 102294,
28
+ "to_user": "VenessaP",
29
+ "from_user": "Steve_Hayes",
30
+ "id": 1666166639,
31
+ "from_user_id": 3169244,
32
+ "iso_language_code": "en",
33
+ "source": "<a href="http:\/\/www.atebits.com\/">Tweetie<\/a>",
34
+ "profile_image_url": "http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/68791310\/Steve_Hayes_Thumbnail_normal.jpg",
35
+ "created_at": "Fri, 01 May 2009 03:38:48 +0000"
36
+ },
37
+ {
38
+ "text": "Come speak with me at JAOO next week - http:\/\/jaoo.dk\/",
39
+ "to_user_id": null,
40
+ "from_user": "mattnhodges",
41
+ "id": 1664823944,
42
+ "from_user_id": 503163,
43
+ "iso_language_code": "en",
44
+ "source": "<a href="http:\/\/desktop.seesmic.com\/">Seesmic Desktop<\/a>",
45
+ "profile_image_url": "http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/86466627\/Picture_2_normal.png",
46
+ "created_at": "Fri, 01 May 2009 01:06:48 +0000"
47
+ }],
48
+ "since_id": 0,
49
+ "max_id": 1682666650,
50
+ "refresh_url": "?since_id=1682666650&q=jaoo",
51
+ "results_per_page": 15,
52
+ "next_page": "?page=2&max_id=1682666650&q=jaoo",
53
+ "completed_in": 0.03198,
54
+ "page": 1,
55
+ "query": "jaoo"
56
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "results": [
3
+ {
4
+ "text": "reading my own abstract for JAOO presentation",
5
+ "from_user": "drnic",
6
+ "id": 1666627310
7
+ },
8
+ {
9
+ "text": "Come speak with Matt at JAOO next week",
10
+ "from_user": "theRMK",
11
+ "id": 1666334207
12
+ },
13
+ {
14
+ "text": "@VenessaP I think they went out for noodles. #jaoo",
15
+ "from_user": "Steve_Hayes",
16
+ "id": 1666166639
17
+ },
18
+ {
19
+ "text": "Come speak with me at JAOO next week - http:\/\/jaoo.dk\/",
20
+ "from_user": "mattnhodges",
21
+ "id": 1664823944,
22
+ }],
23
+ "refresh_url": "?since_id=1682666650&q=jaoo",
24
+ "results_per_page": 15,
25
+ "next_page": "?page=2&max_id=1682666650&q=jaoo"
26
+ }
@@ -0,0 +1,162 @@
1
+ Given /^this project is active project folder/ do
2
+ @active_project_folder = File.expand_path(File.dirname(__FILE__) + "/../..")
3
+ end
4
+
5
+ Given /^env variable \$([\w_]+) set to "(.*)"/ do |env_var, value|
6
+ ENV[env_var] = value
7
+ end
8
+
9
+ Given /"(.*)" folder is deleted/ do |folder|
10
+ in_project_folder { FileUtils.rm_rf folder }
11
+ end
12
+
13
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
14
+ @stdout = StringIO.new
15
+ in_project_folder do
16
+ if Object.const_defined?("APP_ROOT")
17
+ APP_ROOT.replace(FileUtils.pwd)
18
+ else
19
+ APP_ROOT = FileUtils.pwd
20
+ end
21
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
22
+ end
23
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
24
+ @stdout.rewind
25
+ f << @stdout.read
26
+ end
27
+ end
28
+
29
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
30
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
31
+ in_project_folder do
32
+ system "#{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
33
+ end
34
+ end
35
+
36
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
37
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
38
+ in_project_folder do
39
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
40
+ end
41
+ end
42
+
43
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
44
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
45
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
46
+ in_project_folder do
47
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
48
+ end
49
+ end
50
+
51
+ When /^I invoke task "rake (.*)"/ do |task|
52
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
53
+ in_project_folder do
54
+ system "rake #{task} --trace > #{@stdout} 2> #{@stdout}"
55
+ end
56
+ end
57
+
58
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
59
+ in_project_folder do
60
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
61
+ end
62
+ end
63
+
64
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
65
+ in_project_folder do
66
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
67
+ end
68
+ end
69
+
70
+ Then /^file with name matching "(.*)" is created/ do |pattern|
71
+ in_project_folder do
72
+ Dir[pattern].should_not be_empty
73
+ end
74
+ end
75
+
76
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
77
+ in_project_folder do
78
+ actual_output = File.read(file)
79
+ (does == 'does') ?
80
+ actual_output.should(match(/#{regex}/)) :
81
+ actual_output.should_not(match(/#{regex}/))
82
+ end
83
+ end
84
+
85
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
86
+ File.exists?(gem_file).should be_true
87
+ File.exists?(project_file).should be_true
88
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
89
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
90
+ project_file_contents.should == gem_file_contents
91
+ end
92
+
93
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
94
+ actual_output = File.read(@stdout)
95
+ does_invoke == "does" ?
96
+ actual_output.should(match(/dependency\s+#{generator}/)) :
97
+ actual_output.should_not(match(/dependency\s+#{generator}/))
98
+ end
99
+
100
+ Then /I should see help option "(.*)"/ do |opt|
101
+ actual_output = File.read(@stdout)
102
+ actual_output.should match(/#{opt}/)
103
+ end
104
+
105
+ Then /^I should see$/ do |text|
106
+ actual_output = File.read(@stdout)
107
+ actual_output.should contain(text)
108
+ end
109
+
110
+ Then /^I should not see$/ do |text|
111
+ actual_output = File.read(@stdout)
112
+ actual_output.should_not contain(text)
113
+ end
114
+
115
+ Then /^I should see exactly$/ do |text|
116
+ actual_output = File.read(@stdout)
117
+ actual_output.should == text
118
+ end
119
+
120
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
121
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
122
+ actual_output = File.read(@stdout)
123
+ actual_output.should match(expected)
124
+ end
125
+
126
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
127
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
128
+ actual_output = File.read(@stdout)
129
+ actual_output.should match(expected)
130
+ end
131
+
132
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
133
+ in_project_folder do
134
+ yaml = eval yaml
135
+ YAML.load(File.read(file)).should == yaml
136
+ end
137
+ end
138
+
139
+ Then /^Rakefile can display tasks successfully/ do
140
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
141
+ in_project_folder do
142
+ system "rake -T > #{@stdout} 2> #{@stdout}"
143
+ end
144
+ actual_output = File.read(@stdout)
145
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
146
+ end
147
+
148
+ Then /^task "rake (.*)" is executed successfully/ do |task|
149
+ @stdout.should_not be_nil
150
+ actual_output = File.read(@stdout)
151
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
152
+ actual_output.should_not match(/Error/i)
153
+ end
154
+
155
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
156
+ in_project_folder do
157
+ gem_file = Dir["pkg/*.gem"].first
158
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
159
+ spec_value = gem_spec.send(key.to_sym)
160
+ spec_value.to_s.should match(/#{regex}/)
161
+ end
162
+ end
@@ -0,0 +1,27 @@
1
+ # a version of the bin/tweet-tail file
2
+ When /^I run executable internally with arguments "([^\"]*)"$/ do |args|
3
+ require 'rubygems'
4
+ require File.dirname(__FILE__) + "/../../lib/tweet-tail"
5
+ require "tweet-tail/cli"
6
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
7
+ in_project_folder do
8
+ TweetTail::CLI.execute(@stdout_io = StringIO.new, args.split(" "))
9
+ @stdout_io.rewind
10
+ File.open(@stdout, "w") { |f| f << @stdout_io.read }
11
+ end
12
+ end
13
+
14
+ When /^I run executable internally with arguments "([^\"]*)" and wait (\d+) sleep cycles? and quit$/ do |args, cycles|
15
+ hijack_sleep(cycles.to_i)
16
+ When %Q{I run executable internally with arguments "#{args}"}
17
+ end
18
+
19
+ Then /^I should see some twitter messages$/ do
20
+ actual_output = File.read(@stdout)
21
+ # puts actual_output # UNCOMMENT this to see what the current live data looks like
22
+ lines = actual_output.split("\n")
23
+ lines.length.should_not == 0
24
+ lines.each do |line|
25
+ line.should =~ /^[\w_]+:\s.+$/
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ When /^I dump stdout$/ do
2
+ dump(File.exists?(@stdout) ? File.read(@stdout) : @stdout)
3
+ end
@@ -0,0 +1,12 @@
1
+ Given /^twitter has some search results for "([^\"]*)"$/ do |query|
2
+ FakeWeb.register_uri(
3
+ :get,
4
+ "http://search.twitter.com/search.json?q=#{query}",
5
+ :file => File.expand_path(File.dirname(__FILE__) + "/../fixtures/search-#{query}.json"))
6
+
7
+ since = "1682666650"
8
+ FakeWeb.register_uri(
9
+ :get,
10
+ "http://search.twitter.com/search.json?since_id=#{since}&q=#{query}",
11
+ :file => File.expand_path(File.dirname(__FILE__) + "/../fixtures/search-#{query}-since-#{since}.json"))
12
+ end
@@ -0,0 +1,29 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_home_folder(&block)
12
+ FileUtils.chdir(@home_path, &block)
13
+ end
14
+
15
+ def force_local_lib_override(project_name = @project_name)
16
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
17
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
18
+ f << "$:.unshift('#{@lib_path}')\n"
19
+ f << rakefile
20
+ end
21
+ end
22
+
23
+ def setup_active_project_folder project_name
24
+ @active_project_folder = File.join(@tmp_root, project_name)
25
+ @project_name = project_name
26
+ end
27
+ end
28
+
29
+ World(CommonHelpers)
@@ -0,0 +1,21 @@
1
+ # require File.dirname(__FILE__) + "/../../lib/tweet-tail"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
7
+
8
+ Before do
9
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
10
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
11
+ FileUtils.rm_rf @tmp_root
12
+ FileUtils.mkdir_p @home_path
13
+ ENV['HOME'] = @home_path
14
+ end
15
+
16
+ gem "fakeweb"
17
+ require "fakeweb"
18
+
19
+ Before do
20
+ FakeWeb.allow_net_connect = false
21
+ end
@@ -0,0 +1,17 @@
1
+ module LoggerHelper
2
+ def dump(contents)
3
+ in_tmp_folder do
4
+ File.open("dump_file.txt", "w") do |file|
5
+ file << contents
6
+ end
7
+ unless ENV['CUCUMBER_EDITOR']
8
+ puts contents
9
+ else
10
+ `#{ENV['CUCUMBER_EDITOR']} '#{File.expand_path filename}'`
11
+ end
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ World(LoggerHelper)
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
@@ -0,0 +1,15 @@
1
+ require "mocha"
2
+
3
+ World(Mocha::Standalone)
4
+
5
+ Before do
6
+ mocha_setup
7
+ end
8
+
9
+ After do
10
+ begin
11
+ mocha_verify
12
+ ensure
13
+ mocha_teardown
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module TimeMachineHelper
2
+ # expects sleep() to be called +cycles+ times, and then raises an Interrupt
3
+ def hijack_sleep(cycles)
4
+ results = [*1..cycles] # irrelevant truthy values for each sleep call expected
5
+ Kernel::stubs(:sleep).returns(*results).then.raises(Interrupt)
6
+ end
7
+ end
8
+
9
+ World(TimeMachineHelper)
data/lib/tweet-tail.rb ADDED
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module TweetTail
5
+ VERSION = '1.0.0'
6
+ end
7
+
8
+ gem 'json'
9
+ require "json"
10
+
11
+ require "open-uri"
12
+
13
+ require "tweet-tail/tweet_poller"
@@ -0,0 +1,42 @@
1
+ require 'optparse'
2
+
3
+ module TweetTail::CLI
4
+ def self.execute(stdout, arguments=[])
5
+ options = { :polling => false }
6
+
7
+ parser = OptionParser.new do |opts|
8
+ opts.banner = <<-BANNER.gsub(/^ /,'')
9
+ Display latest twitter search results. Even poll for them for hours of fun.
10
+
11
+ Usage: #{File.basename($0)} [options]
12
+
13
+ Options are:
14
+ BANNER
15
+ opts.separator ""
16
+ opts.on("-f", "Poll for new search results each 15 seconds."
17
+ ) { |arg| options[:polling] = true }
18
+ opts.on("-h", "--help",
19
+ "Show this help message.") { stdout.puts opts; exit }
20
+ opts.parse!(arguments)
21
+ end
22
+
23
+ unless query = arguments.shift
24
+ stdout.puts parser
25
+ exit
26
+ end
27
+
28
+ begin
29
+ app = TweetTail::TweetPoller.new(query)
30
+ app.refresh
31
+ stdout.puts app.render_latest_results
32
+ while(options[:polling])
33
+ Kernel::sleep(15)
34
+ app.refresh
35
+ if app.render_latest_results.size > 0
36
+ stdout.puts app.render_latest_results
37
+ end
38
+ end
39
+ rescue Interrupt
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ class TweetTail::TweetPoller
2
+ attr_accessor :query, :latest_results, :refresh_url
3
+
4
+ def initialize(query)
5
+ @query = query
6
+ end
7
+
8
+ def refresh
9
+ unless @refresh_url
10
+ @latest_feed = JSON.parse(initial_json_data)
11
+ else
12
+ @latest_feed = JSON.parse(refresh_json_data)
13
+ end
14
+ @latest_results = @latest_feed["results"].reverse
15
+ @refresh_url = @latest_feed["refresh_url"]
16
+ end
17
+
18
+ def render_latest_results
19
+ @latest_results.inject("") do |output, tweet|
20
+ screen_name = tweet['from_user']
21
+ message = tweet['text']
22
+ output += "#{screen_name}: #{message}\n"
23
+ end
24
+ end
25
+
26
+ protected
27
+ def initial_json_data
28
+ Net::HTTP.get(URI.parse("http://search.twitter.com/search.json?q=#{query}"))
29
+ end
30
+
31
+ def refresh_json_data
32
+ Net::HTTP.get(URI.parse("http://search.twitter.com/search.json#{refresh_url}"))
33
+ end
34
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/tweet-tail.rb'}"
9
+ puts "Loading tweet-tail gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,7 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require "rubygems"
4
+ require File.dirname(__FILE__) + '/../lib/tweet-tail'
5
+
6
+ gem "mocha"
7
+ require "mocha"
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
2
+ require 'tweet-tail/cli'
3
+
4
+ class TestTweetTailCli < Test::Unit::TestCase
5
+ def setup
6
+ TweetTail::CLI.execute(@stdout_io = StringIO.new, ['jaoo'])
7
+ @stdout_io.rewind
8
+ @stdout = @stdout_io.read
9
+ end
10
+
11
+ def test_print_default_output
12
+ puts @stdout
13
+ assert_match(/To update this executable/, @stdout)
14
+ end
15
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestTweetPoller < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @app = TweetTail::TweetPoller.new('jaoo')
7
+ @app.expects(:initial_json_data).returns(<<-JSON)
8
+ {
9
+ "results": [{
10
+ "text": "reading my own abstract for JAOO presentation",
11
+ "from_user": "drnic",
12
+ "id": 1666627310
13
+ },{
14
+ "text": "Come speak with Matt at JAOO next week",
15
+ "from_user": "theRMK",
16
+ "id": 1666334207
17
+ },{
18
+ "text": "@VenessaP I think they went out for noodles. #jaoo",
19
+ "from_user": "Steve_Hayes",
20
+ "id": 1666166639
21
+ },{
22
+ "text": "Come speak with me at JAOO next week - http:\/\/jaoo.dk\/",
23
+ "from_user": "mattnhodges",
24
+ "id": 1664823944
25
+ }],
26
+ "refresh_url": "?since_id=1682666650&q=jaoo"
27
+ }
28
+ JSON
29
+ @app.refresh
30
+ end
31
+
32
+ def test_found_results
33
+ assert_equal(4, @app.latest_results.length)
34
+ end
35
+
36
+ def test_message_render
37
+ expected = <<-RESULTS.gsub(/^ /, '')
38
+ mattnhodges: Come speak with me at JAOO next week - http://jaoo.dk/
39
+ Steve_Hayes: @VenessaP I think they went out for noodles. #jaoo
40
+ theRMK: Come speak with Matt at JAOO next week
41
+ drnic: reading my own abstract for JAOO presentation
42
+ RESULTS
43
+ assert_equal(expected, @app.render_latest_results)
44
+ end
45
+
46
+ def test_ready_for_refresh
47
+ assert_equal('?since_id=1682666650&q=jaoo', @app.refresh_url)
48
+ end
49
+
50
+ def test_refresh_data
51
+ @app.expects(:refresh_json_data).returns(<<-JSON)
52
+ {
53
+ "results": [{
54
+ "text": "Wish I could be at #JAOO Australia...",
55
+ "from_user": "CaioProiete",
56
+ "id": 1711269079
57
+ }],
58
+ "refresh_url": "?since_id=1711269079&q=jaoo"
59
+ }
60
+ JSON
61
+ @app.refresh
62
+ expected = <<-RESULTS.gsub(/^ /, '')
63
+ CaioProiete: Wish I could be at #JAOO Australia...
64
+ RESULTS
65
+ assert_equal(expected, @app.render_latest_results)
66
+ assert_equal('?since_id=1711269079&q=jaoo', @app.refresh_url)
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tweettail
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Dr Nic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-06 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.4.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ version:
35
+ description: |-
36
+ Get the latest search results streaming to your console:
37
+
38
+ $ tweet-tail railsconf
39
+ rubysolo: protip: it helps to actually READ the error message. #railsconf
40
+ voxxit: So, everyone, how is #railsconf coming? When is the big 3.0 announcement?
41
+ JesseGoldberg: @GavinStark I don't have as much to chat about as you do while you are at RailsConf.
42
+ wndxlori: Anyone else not eaten yet #railsconf
43
+ zenmatt: Great dinner and coversation with @heroku at n9ne in the palms. #railsconf
44
+ Adkron: Damn you #railsconf why can I not visit you this year. I'm missing all the gitjour goodness.
45
+ pengwynn: Meeting a lot of great folks at the open gov hackathon at #railsconf #gov20
46
+ davidjrice: Enjoying ordering taxis to our hotel... &quot;for the wynn!&quot; #railsconf quick noms at stripburger then whiskeys at the stage door with ey ftw!
47
+ cricketgeek: as pointed out by @jnewland at sushi this evening... http://pic.im/2LY #railsconf
48
+ paulog: had fun at gilt groupe coctail party. props. #railsconf
49
+ Amuse_Bouche: I hope my two favorite people in the world form an alliance! (Swoon) RT: @dhh Loved talking to @tferris at #railsconf. So much resonates.
50
+ abie: At open gov BOF #railsconf
51
+ matthewcarriere: running a saas bof was great... I hope it gets some more time this week. #railsconf
52
+ jdar: @tullytully RT @dgou:for the benefit of people at #railsconf keynote, here is penelope trunk on tim ferris: http://bit.ly/b81E
53
+ yorzi: Reading: &quot;Rails 3 and the Real Secret to High Productivity: RailsConf 2009 - May 04 - 07, 2009, Las Vegas,NV&quot; ( http://tinyurl.com/czmkxn )
54
+
55
+ Or let it sit there all day with the `-f` option (as like `tail -f`)
56
+ email:
57
+ - drnicwilliams@gmail.com
58
+ executables:
59
+ - tweet-tail
60
+ - tweet_tail
61
+ - tweettail
62
+ extensions: []
63
+
64
+ extra_rdoc_files:
65
+ - History.txt
66
+ - Manifest.txt
67
+ - PostInstall.txt
68
+ - README.rdoc
69
+ files:
70
+ - History.txt
71
+ - Manifest.txt
72
+ - PostInstall.txt
73
+ - README.rdoc
74
+ - Rakefile
75
+ - bin/tweet-tail
76
+ - bin/tweet_tail
77
+ - bin/tweettail
78
+ - features/cli.feature
79
+ - features/development.feature
80
+ - features/fixtures/search-jaoo-since-1682666650.json
81
+ - features/fixtures/search-jaoo.json
82
+ - features/fixtures/search-jaoo.short.json
83
+ - features/step_definitions/common_steps.rb
84
+ - features/step_definitions/executable_steps.rb
85
+ - features/step_definitions/logger_steps.rb
86
+ - features/step_definitions/twitter_data_steps.rb
87
+ - features/support/common.rb
88
+ - features/support/env.rb
89
+ - features/support/logger.rb
90
+ - features/support/matchers.rb
91
+ - features/support/mocha.rb
92
+ - features/support/time_machine_helpers.rb
93
+ - lib/tweet-tail.rb
94
+ - lib/tweet-tail/cli.rb
95
+ - lib/tweet-tail/tweet_poller.rb
96
+ - script/console
97
+ - script/destroy
98
+ - script/generate
99
+ - test/test_helper.rb
100
+ - test/test_tweet-tail_cli.rb
101
+ - test/test_tweet_poller.rb
102
+ has_rdoc: true
103
+ homepage: http://github.com/drnic/tweet-tail
104
+ licenses: []
105
+
106
+ post_install_message:
107
+ rdoc_options:
108
+ - --main
109
+ - README.rdoc
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: "0"
117
+ version:
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: "0"
123
+ version:
124
+ requirements: []
125
+
126
+ rubyforge_project: drnicutilities
127
+ rubygems_version: 1.3.3
128
+ signing_key:
129
+ specification_version: 3
130
+ summary: "Get the latest search results streaming to your console: $ tweet-tail railsconf rubysolo: protip: it helps to actually READ the error message"
131
+ test_files:
132
+ - test/test_helper.rb
133
+ - test/test_tweet-tail_cli.rb
134
+ - test/test_tweet_poller.rb