phillipoertel-all_tweets_must_die 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ login.rb
2
+ coverage
3
+ pkg
data/README ADDED
@@ -0,0 +1,3 @@
1
+ Hi there!
2
+
3
+ This is a script and set of files that periodically kills my twitter status messages. I have a few reasons for that, but that's left for another day to be explained.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rake/testtask'
2
+
3
+ # for gem packaging etc. see http://github.com/technicalpickles/jeweler
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "all_tweets_must_die"
7
+ gemspec.summary = "Deletes all your tweets older than a certain age"
8
+ gemspec.email = "me AT phillipoertel DOHT com"
9
+ gemspec.homepage = "https://github.com/phillipoertel/all_tweets_must_die"
10
+ gemspec.description = "Deletes all your tweets older than a certain age"
11
+ gemspec.authors = ["Phillip Oertel"]
12
+ end
13
+
14
+ task :default => [:test]
15
+
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.test_files = FileList['test/**/*_test.rb']
18
+ t.verbose = true
19
+ end
20
+
21
+ task :rcov do
22
+ sh "rcov test/*test.rb --sort=coverage"
23
+ end
24
+
25
+ task :run do
26
+ require 'rubygems'
27
+ require 'login'
28
+ Dir.glob('lib/*.rb').each { |file| require file }
29
+ hitman = AllTweetsMustDie::Hitman.new(:username => @login[:user], :password => @login[:password])
30
+ hitman.run!
31
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 1
3
+ :patch: 0
4
+ :major: 0
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{all_tweets_must_die}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Phillip Oertel"]
9
+ s.date = %q{2009-06-19}
10
+ s.description = %q{Deletes all your tweets older than a certain age}
11
+ s.email = %q{me AT phillipoertel DOHT com}
12
+ s.extra_rdoc_files = [
13
+ "README"
14
+ ]
15
+ s.files = [
16
+ ".gitignore",
17
+ "README",
18
+ "Rakefile",
19
+ "VERSION.yml",
20
+ "all_tweets_must_die.gemspec",
21
+ "examples/crontab",
22
+ "lib/argument_validator.rb",
23
+ "lib/hitman.rb",
24
+ "lib/tweet.rb",
25
+ "test/argument_validator_test.rb",
26
+ "test/fixtures/user_timeline_noradio.json",
27
+ "test/hitman_test.rb",
28
+ "test/test_helper.rb",
29
+ "test/tweet_test.rb"
30
+ ]
31
+ s.has_rdoc = true
32
+ s.homepage = %q{https://github.com/phillipoertel/all_tweets_must_die}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.2}
36
+ s.summary = %q{Deletes all your tweets older than a certain age}
37
+ s.test_files = [
38
+ "test/argument_validator_test.rb",
39
+ "test/hitman_test.rb",
40
+ "test/test_helper.rb",
41
+ "test/tweet_test.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
data/examples/crontab ADDED
@@ -0,0 +1 @@
1
+ */5 * * * * cd /path/to/all_tweets_must_die && rake run &> /tmp/all_tweets_must_die.log
@@ -0,0 +1,20 @@
1
+ module AllTweetsMustDie
2
+ module ArgumentValidator
3
+
4
+ private
5
+
6
+ # fail early, don't swallow unknown arguments.
7
+ # especially useful for named parameter style argument passing
8
+ def validate_args!(valid_args, actual_args)
9
+ invalid_args = actual_args.keys - valid_args
10
+ unless invalid_args.empty?
11
+ msg = "Invalid argument(s) %s. Valid are: %s" % [format_args(invalid_args), format_args(valid_args)]
12
+ raise ArgumentError.new(msg)
13
+ end
14
+ end
15
+
16
+ def format_args(args)
17
+ args.sort { |a1, a2| a1.to_s <=> a2.to_s }.map { |a| %("#{a}") }.join(', ')
18
+ end
19
+ end
20
+ end
data/lib/hitman.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'rest_client'
3
+ require 'json'
4
+
5
+ require File.join(File.dirname(__FILE__), 'argument_validator')
6
+
7
+ #
8
+ # appropriate soundtracks: Miss Kittin -- Rippin Kittin; Kill Bill Vol. 1 & 2
9
+ #
10
+ module AllTweetsMustDie
11
+ class Hitman
12
+
13
+ include ArgumentValidator
14
+
15
+ attr_reader :default_tweet_lifetime, :username, :password
16
+
17
+ def initialize(options = {})
18
+ validate_args!([:lifetime, :username, :password], options)
19
+ @default_tweet_lifetime = options[:lifetime] || 12 # maximum allowed age in hours
20
+ @username = options[:username]
21
+ @password = options[:password]
22
+ end
23
+
24
+ def run!
25
+ fetch_victim_tweets.each do |tweet|
26
+ tweet.kill!(@password) unless tweet.should_live?(@default_tweet_lifetime)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def fetch_victim_tweets
33
+ auth = @password ? "#{@username}:#{@password}@" : ''
34
+ url = "http://%stwitter.com/statuses/user_timeline/%s.json" % [auth, @username]
35
+ response = RestClient.get(url)
36
+ JSON.parse(response).map { |tweet_data| Tweet.new(tweet_data) }
37
+ end
38
+
39
+ end
40
+ end
data/lib/tweet.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'rest_client'
2
+ require 'time'
3
+
4
+ module AllTweetsMustDie
5
+ class Tweet
6
+
7
+ def initialize(data)
8
+ @data = data
9
+ @data['created_at'] = Time.parse(@data['created_at'])
10
+ end
11
+
12
+ def kill!(password)
13
+ http_auth_string = "#{@data['user']['screen_name']}:#{password}@"
14
+ url = "http://%stwitter.com/statuses/destroy/%d.xml" % [http_auth_string, @data['id']]
15
+ RestClient.delete(url)
16
+ end
17
+
18
+ def should_live?(default_lifetime)
19
+ keep_hashtag = @data['text'].match(/#keep(\d+)h$/)
20
+ hours_to_live = keep_hashtag ? keep_hashtag[1].to_i : default_lifetime
21
+ maximum_age = (hours_to_live * 60 * 60)
22
+ age <= maximum_age
23
+ end
24
+
25
+ # in seconds
26
+ def age
27
+ Time.now.utc - @data['created_at']
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), '../test/test_helper')
2
+ require File.join(File.dirname(__FILE__), '../lib/argument_validator')
3
+
4
+ class MyClass
5
+
6
+ include AllTweetsMustDie::ArgumentValidator
7
+
8
+ def foo(args = {})
9
+ validate_args!([:foo, :bar], args)
10
+ "NOTHING RAISED"
11
+ end
12
+ end
13
+
14
+
15
+ class ArgumentValidatorTest < Test::Unit::TestCase
16
+
17
+ def test_with_zero_args
18
+ assert_equal "NOTHING RAISED", MyClass.new.foo
19
+ end
20
+
21
+ def test_with_one_valid_arg
22
+ assert_equal "NOTHING RAISED", MyClass.new.foo(:foo => 1)
23
+ end
24
+
25
+ def test_with_two_valid_args
26
+ assert_equal "NOTHING RAISED", MyClass.new.foo(:foo => 1, :bar => 'bar')
27
+ end
28
+
29
+ def test_with_invalid_arg
30
+ ex = assert_raises(ArgumentError) { MyClass.new.foo(:baz => 1) }
31
+ assert_equal 'Invalid argument(s) "baz". Valid are: "bar", "foo"', ex.message
32
+ end
33
+
34
+ def test_with_two_invalid_arg
35
+ ex = assert_raises(ArgumentError) { MyClass.new.foo(:baz => 1, :zed => 'dead') }
36
+ assert_equal 'Invalid argument(s) "baz", "zed". Valid are: "bar", "foo"', ex.message
37
+ end
38
+
39
+ def test_with_valid_and_invalid_arg
40
+ ex = assert_raises(ArgumentError) { MyClass.new.foo(:foo => 1, :bar => 'bar', :baz => 1) }
41
+ assert_equal 'Invalid argument(s) "baz". Valid are: "bar", "foo"', ex.message
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ [{"text":"Ubiquitous support for landscape mode in iPhone 3.0 means bigger keyboard keys means thumb typing means I can FINALLY type quickly.","in_reply_to_status_id":null,"user":{"profile_background_color":"9AE4E8","followers_count":1769,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"statuses_count":3220,"friends_count":272,"profile_sidebar_fill_color":"DDFFCC","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_sidebar_border_color":"BDDCAD","url":"http:\/\/project.ioni.st","screen_name":"noradio","name":"Marcel Molina","notifications":null,"favourites_count":73,"created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"verified":false,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","following":null,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","location":"San Francisco, CA","id":3191321,"profile_background_tile":true},"in_reply_to_user_id":null,"favorited":false,"created_at":"Fri Jun 19 06:31:54 +0000 2009","in_reply_to_screen_name":null,"id":2234681759,"truncated":false,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"@rael How else would he be able to see 10 patients at once though? ;-)","in_reply_to_user_id":257,"user":{"profile_sidebar_fill_color":"DDFFCC","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","screen_name":"noradio","utc_offset":-28800,"verified":false,"profile_sidebar_border_color":"BDDCAD","notifications":null,"url":"http:\/\/project.ioni.st","name":"Marcel Molina","created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_text_color":"333333","followers_count":1765,"protected":false,"statuses_count":3219,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","time_zone":"Pacific Time (US & Canada)","friends_count":272,"profile_link_color":"0084B4","profile_background_tile":true,"following":null,"favourites_count":73,"profile_background_color":"9AE4E8","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","location":"San Francisco, CA","id":3191321},"favorited":false,"in_reply_to_screen_name":"rael","created_at":"Thu Jun 18 23:18:04 +0000 2009","truncated":false,"id":2229855057,"in_reply_to_status_id":2229770002,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"in_reply_to_screen_name":null,"text":"Realizing that the Ying Yang Twins' Whisper Song is basically a rip of the end of Don Cherry's Brown Rice modulo raunch: http:\/\/bit.ly\/wPybS","user":{"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","screen_name":"noradio","utc_offset":-28800,"profile_sidebar_fill_color":"DDFFCC","notifications":null,"followers_count":1762,"profile_sidebar_border_color":"BDDCAD","friends_count":272,"url":"http:\/\/project.ioni.st","name":"Marcel Molina","created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"verified":false,"favourites_count":72,"profile_text_color":"333333","statuses_count":3218,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","following":null,"profile_background_tile":true,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","location":"San Francisco, CA","id":3191321,"profile_background_color":"9AE4E8"},"truncated":false,"in_reply_to_status_id":null,"created_at":"Thu Jun 18 17:41:46 +0000 2009","in_reply_to_user_id":null,"favorited":false,"id":2225418746,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"@bil_kleb I'm sad for your lose. It's a comfort that he passed quietly. Camping sounds, all things considered, like a calming diversion.","user":{"following":0,"statuses_count":3217,"profile_background_color":"9AE4E8","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"followers_count":1741,"profile_sidebar_fill_color":"DDFFCC","friends_count":271,"created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_sidebar_border_color":"BDDCAD","url":"http:\/\/project.ioni.st","name":"Marcel Molina","verified_profile":false,"protected":false,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","notifications":false,"time_zone":"Pacific Time (US & Canada)","favourites_count":72,"profile_link_color":"0084B4","location":"San Francisco, CA","id":3191321,"profile_background_tile":true,"screen_name":"noradio"},"truncated":false,"in_reply_to_status_id":2210543360,"created_at":"Wed Jun 17 18:46:39 +0000 2009","in_reply_to_user_id":9579412,"favorited":false,"id":2210593900,"in_reply_to_screen_name":"bil_kleb","source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"OH: \"If there is sufficient interest, I'd be pleased to lead a morning group practice whose ingredients include...tantra\" @gregpass","in_reply_to_screen_name":null,"user":{"following":null,"time_zone":"Pacific Time (US & Canada)","favourites_count":71,"profile_link_color":"0084B4","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","profile_background_tile":true,"utc_offset":-28800,"profile_background_color":"9AE4E8","verified_profile":false,"created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_sidebar_fill_color":"DDFFCC","url":"http:\/\/project.ioni.st","name":"Marcel Molina","notifications":null,"protected":false,"profile_sidebar_border_color":"BDDCAD","followers_count":1732,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","friends_count":267,"profile_text_color":"333333","location":"San Francisco, CA","id":3191321,"statuses_count":3216,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","screen_name":"noradio"},"truncated":false,"created_at":"Tue Jun 16 22:43:25 +0000 2009","in_reply_to_status_id":null,"in_reply_to_user_id":null,"id":2198187320,"favorited":false,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"truncated":false,"text":"@atduskgreg You might not even get one if you pre-order. It's still first come first serve. Though they said they might have separate lines.","user":{"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","created_at":"Mon Apr 02 07:47:28 +0000 2007","notifications":null,"time_zone":"Pacific Time (US & Canada)","favourites_count":71,"profile_link_color":"0084B4","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_tile":true,"url":"http:\/\/project.ioni.st","name":"Marcel Molina","statuses_count":3215,"profile_background_color":"9AE4E8","protected":false,"screen_name":"noradio","followers_count":1710,"profile_sidebar_fill_color":"DDFFCC","friends_count":267,"following":null,"profile_sidebar_border_color":"BDDCAD","location":"San Francisco, CA","id":3191321,"verified_profile":false},"in_reply_to_status_id":2183644637,"created_at":"Mon Jun 15 21:56:00 +0000 2009","in_reply_to_user_id":26853,"favorited":false,"in_reply_to_screen_name":"atduskgreg","id":2183748561,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"Lately, the most effective GTD apps for me are command-H and option-command-H.","user":{"following":0,"profile_sidebar_border_color":"BDDCAD","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"favourites_count":70,"followers_count":1711,"friends_count":267,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/18156348\/jessica_tiled.jpg.jpeg","created_at":"Mon Apr 02 07:47:28 +0000 2007","url":"http:\/\/project.ioni.st","name":"Marcel Molina","statuses_count":3214,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","protected":false,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_tile":true,"profile_background_color":"9AE4E8","screen_name":"noradio","verified_profile":false,"profile_sidebar_fill_color":"DDFFCC","location":"San Francisco, CA","id":3191321,"notifications":false},"truncated":false,"in_reply_to_status_id":null,"created_at":"Mon Jun 15 17:37:22 +0000 2009","in_reply_to_user_id":null,"favorited":false,"id":2180550348,"in_reply_to_screen_name":null,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"truncated":false,"in_reply_to_status_id":null,"in_reply_to_user_id":null,"user":{"utc_offset":-28800,"followers_count":1687,"screen_name":"noradio","profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","friends_count":266,"protected":false,"location":"San Francisco, CA","notifications":0,"time_zone":"Pacific Time (US & Canada)","favourites_count":69,"profile_link_color":"0084B4","name":"Marcel Molina","following":0,"profile_background_tile":false,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","statuses_count":3213,"profile_background_color":"9AE4E8","verified_profile":false,"profile_sidebar_fill_color":"DDFFCC","created_at":"Mon Apr 02 07:47:28 +0000 2007","url":"http:\/\/project.ioni.st","id":3191321,"profile_sidebar_border_color":"BDDCAD","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg"},"favorited":false,"text":"via @netik \"This is the coolest site, ever. Real people telling their life stories. http:\/\/www.peoplesarchive.com\/ I'm watching Donald K...\"","in_reply_to_screen_name":null,"id":2159490339,"created_at":"Sun Jun 14 00:14:10 +0000 2009","source":"web"},{"text":"That a throng of completely naked cyclists just rolled by would be remarkable were this not San Francisco.","user":{"following":null,"profile_sidebar_border_color":"BDDCAD","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"favourites_count":69,"followers_count":1687,"friends_count":266,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","created_at":"Mon Apr 02 07:47:28 +0000 2007","url":"http:\/\/project.ioni.st","name":"Marcel Molina","statuses_count":3212,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","protected":false,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_tile":false,"profile_background_color":"9AE4E8","screen_name":"noradio","verified_profile":false,"profile_sidebar_fill_color":"DDFFCC","location":"San Francisco, CA","id":3191321,"notifications":null},"truncated":false,"in_reply_to_status_id":null,"created_at":"Sat Jun 13 23:44:35 +0000 2009","in_reply_to_user_id":null,"favorited":false,"id":2159223054,"in_reply_to_screen_name":null,"source":"web"},{"in_reply_to_screen_name":"liquidgecka","text":"@liquidgecka Welcome to Twitter. You've made the right choice ;-)","user":{"notifications":false,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_sidebar_border_color":"BDDCAD","favourites_count":69,"created_at":"Mon Apr 02 07:47:28 +0000 2007","followers_count":1687,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","friends_count":266,"profile_text_color":"333333","url":"http:\/\/project.ioni.st","name":"Marcel Molina","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","protected":false,"screen_name":"noradio","statuses_count":3212,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","profile_background_tile":false,"profile_background_color":"9AE4E8","following":false,"location":"San Francisco, CA","id":3191321,"verified_profile":false,"profile_sidebar_fill_color":"DDFFCC"},"created_at":"Sat Jun 13 23:42:31 +0000 2009","truncated":false,"in_reply_to_status_id":null,"in_reply_to_user_id":34270047,"favorited":false,"id":2159204798,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"truncated":false,"text":"Be aprised: The frittata at Serpentine is the best brunch I've had since moving to SF. Better than e.g. Universal. What's your best brunch?","user":{"followers_count":1684,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","created_at":"Mon Apr 02 07:47:28 +0000 2007","friends_count":265,"notifications":false,"time_zone":"Pacific Time (US & Canada)","favourites_count":69,"profile_link_color":"0084B4","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_tile":false,"url":"http:\/\/project.ioni.st","name":"Marcel Molina","statuses_count":3210,"profile_background_color":"9AE4E8","protected":false,"screen_name":"noradio","verified_profile":false,"profile_sidebar_fill_color":"DDFFCC","following":0,"profile_sidebar_border_color":"BDDCAD","location":"San Francisco, CA","id":3191321},"in_reply_to_status_id":null,"created_at":"Sat Jun 13 21:03:38 +0000 2009","in_reply_to_user_id":null,"favorited":false,"in_reply_to_screen_name":null,"id":2157728641,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"in_reply_to_screen_name":"nodler","text":"@nodler when I was seven I literally day dreamed of living in a bathysphere though I didn't know it had a name.","user":{"notifications":null,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_sidebar_border_color":"BDDCAD","favourites_count":69,"followers_count":1676,"friends_count":265,"profile_text_color":"333333","url":"http:\/\/project.ioni.st","name":"Marcel Molina","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"statuses_count":3209,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_tile":false,"profile_background_color":"9AE4E8","screen_name":"noradio","following":null,"location":"San Francisco, CA","id":3191321,"verified_profile":false,"profile_sidebar_fill_color":"DDFFCC"},"truncated":false,"in_reply_to_status_id":2149720133,"created_at":"Sat Jun 13 03:34:24 +0000 2009","in_reply_to_user_id":10171902,"favorited":false,"id":2149748525,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"in_reply_to_user_id":null,"text":"OH: \"Follow these rules and you'll find success not only in the job market, but also the meat market.\"","user":{"profile_background_tile":false,"friends_count":265,"description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_background_color":"9AE4E8","favourites_count":69,"created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_sidebar_fill_color":"DDFFCC","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","url":"http:\/\/project.ioni.st","name":"Marcel Molina","statuses_count":3208,"profile_sidebar_border_color":"BDDCAD","protected":false,"screen_name":"noradio","profile_text_color":"333333","following":null,"verified_profile":false,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","followers_count":1659,"location":"San Francisco, CA","id":3191321,"notifications":null,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4"},"favorited":false,"created_at":"Fri Jun 12 20:27:09 +0000 2009","in_reply_to_screen_name":null,"truncated":false,"in_reply_to_status_id":null,"id":2136227015,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"How to butcher a pig: http:\/\/bit.ly\/QJVCd\n. The Boston Butt!!!","user":{"profile_sidebar_border_color":"BDDCAD","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","verified_profile":false,"utc_offset":-28800,"profile_text_color":"333333","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","url":"http:\/\/project.ioni.st","name":"Marcel Molina","notifications":false,"time_zone":"Pacific Time (US & Canada)","favourites_count":69,"created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_link_color":"0084B4","protected":false,"screen_name":"noradio","profile_background_tile":false,"statuses_count":3207,"profile_background_color":"9AE4E8","followers_count":1650,"following":0,"friends_count":265,"profile_sidebar_fill_color":"DDFFCC","location":"San Francisco, CA","id":3191321},"truncated":false,"in_reply_to_status_id":null,"created_at":"Fri Jun 12 07:20:25 +0000 2009","in_reply_to_user_id":null,"favorited":false,"id":2128646883,"in_reply_to_screen_name":null,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"@stop What a precious child. Her gaze has the intensity of someone fated for greatness.","user":{"profile_sidebar_border_color":"BDDCAD","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"favourites_count":69,"verified_profile":false,"created_at":"Mon Apr 02 07:47:28 +0000 2007","screen_name":"noradio","notifications":null,"profile_text_color":"333333","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","url":"http:\/\/project.ioni.st","name":"Marcel Molina","time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","protected":false,"profile_background_tile":false,"followers_count":1650,"profile_background_color":"9AE4E8","friends_count":265,"following":null,"statuses_count":3206,"profile_sidebar_fill_color":"DDFFCC","location":"San Francisco, CA","id":3191321},"truncated":false,"created_at":"Fri Jun 12 07:12:44 +0000 2009","in_reply_to_status_id":2127317239,"in_reply_to_user_id":949521,"favorited":false,"id":2128596354,"in_reply_to_screen_name":"stop","source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"text":"@drawohara At first I read 'homomorphic encryption' as 'homoerotic encryption'. Was confused.","in_reply_to_user_id":17058191,"user":{"favourites_count":69,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"profile_sidebar_fill_color":"DDFFCC","created_at":"Mon Apr 02 07:47:28 +0000 2007","screen_name":"noradio","profile_sidebar_border_color":"BDDCAD","url":"http:\/\/project.ioni.st","name":"Marcel Molina","verified_profile":false,"protected":false,"profile_text_color":"333333","statuses_count":3205,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","followers_count":1644,"notifications":null,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","following":null,"profile_background_tile":false,"friends_count":265,"location":"San Francisco, CA","id":3191321,"profile_background_color":"9AE4E8"},"created_at":"Fri Jun 12 00:12:53 +0000 2009","favorited":false,"in_reply_to_screen_name":"drawohara","truncated":false,"id":2124216812,"in_reply_to_status_id":2124151749,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"truncated":false,"text":"@vl You are an \"extraordinary alien\" ;-)","user":{"verified_profile":false,"notifications":null,"time_zone":"Pacific Time (US & Canada)","favourites_count":68,"profile_link_color":"0084B4","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","profile_background_tile":false,"utc_offset":-28800,"statuses_count":3204,"profile_background_color":"9AE4E8","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","profile_sidebar_fill_color":"DDFFCC","url":"http:\/\/project.ioni.st","name":"Marcel Molina","created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"screen_name":"noradio","profile_sidebar_border_color":"BDDCAD","followers_count":1643,"friends_count":265,"following":null,"profile_text_color":"333333","location":"San Francisco, CA","id":3191321,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg"},"in_reply_to_status_id":2122988543,"in_reply_to_user_id":760892,"created_at":"Thu Jun 11 22:51:54 +0000 2009","favorited":false,"in_reply_to_screen_name":"vl","id":2123331673,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"truncated":false,"text":"@objo Anything written by @sstephenson (http:\/\/github.com\/sstephenson).","user":{"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0084B4","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","profile_background_tile":false,"utc_offset":-28800,"profile_background_color":"9AE4E8","verified_profile":false,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","notifications":null,"profile_sidebar_fill_color":"DDFFCC","url":"http:\/\/project.ioni.st","name":"Marcel Molina","created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"screen_name":"noradio","profile_sidebar_border_color":"BDDCAD","followers_count":1631,"friends_count":264,"following":null,"profile_text_color":"333333","location":"San Francisco, CA","id":3191321,"statuses_count":3203,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/17589558\/512719047_83c871d09e_b.jpg.jpeg","favourites_count":68},"in_reply_to_status_id":2118859040,"in_reply_to_user_id":1315011,"created_at":"Thu Jun 11 17:06:09 +0000 2009","favorited":false,"in_reply_to_screen_name":"objo","id":2119253001,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"in_reply_to_status_id":null,"text":"I'm trilingual and often times borderline incoherent in conversation. It all makes sense now: http:\/\/bit.ly\/18yTtZ","user":{"profile_background_color":"9ae4e8","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"followers_count":1629,"created_at":"Mon Apr 02 07:47:28 +0000 2007","profile_sidebar_fill_color":"e0ff92","screen_name":"noradio","friends_count":264,"statuses_count":3202,"profile_sidebar_border_color":"87bc44","url":"http:\/\/project.ioni.st","name":"Marcel Molina","favourites_count":68,"protected":false,"profile_text_color":"000000","verified_profile":false,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/2467381\/elephants.jpg","following":null,"notifications":null,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0000ff","location":"San Francisco, CA","id":3191321,"profile_background_tile":false},"in_reply_to_user_id":null,"created_at":"Thu Jun 11 05:53:02 +0000 2009","favorited":false,"in_reply_to_screen_name":null,"truncated":false,"id":2113864022,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"},{"in_reply_to_status_id":2112786531,"text":"@rsarver Man that's terrible. I've gotten 2 windows smashed in the last few months over in Oakland. Have fully learned my lesson...","user":{"profile_background_color":"9ae4e8","description":"Engineer at Twitter, retired Rails Core member, co-author of iPhone SDK Development and burgeoning Scala enthusiast.","utc_offset":-28800,"followers_count":1627,"profile_sidebar_fill_color":"e0ff92","friends_count":263,"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53473799\/marcel-euro-rails-conf_normal.jpg","statuses_count":3201,"profile_sidebar_border_color":"87bc44","url":"http:\/\/project.ioni.st","name":"Marcel Molina","favourites_count":68,"created_at":"Mon Apr 02 07:47:28 +0000 2007","protected":false,"screen_name":"noradio","profile_text_color":"000000","verified_profile":false,"profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/2467381\/elephants.jpg","following":null,"notifications":null,"time_zone":"Pacific Time (US & Canada)","profile_link_color":"0000ff","location":"San Francisco, CA","id":3191321,"profile_background_tile":false},"in_reply_to_user_id":795649,"favorited":false,"created_at":"Thu Jun 11 04:02:40 +0000 2009","in_reply_to_screen_name":"rsarver","truncated":false,"id":2112873019,"source":"<a href=\"http:\/\/www.atebits.com\/\">Tweetie<\/a>"}]
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), '../test/test_helper')
2
+ require File.join(File.dirname(__FILE__), '../lib/hitman')
3
+
4
+ class InitializationTest < Test::Unit::TestCase
5
+
6
+ include AllTweetsMustDie
7
+
8
+ def test_should_initialize_tweet_lifetime_to_12h_if_none_given
9
+ killer = Hitman.new
10
+ assert_equal 12, killer.default_tweet_lifetime
11
+ end
12
+
13
+ def test_should_accept_different_tweet_lifetime
14
+ killer = Hitman.new(:lifetime => 42)
15
+ assert_equal 42, killer.default_tweet_lifetime
16
+ end
17
+
18
+ def test_should_accept_and_store_username_and_password
19
+ killer = Hitman.new(:username => 'phil76', :password => 'foo')
20
+ assert_equal 'phil76', killer.username
21
+ assert_equal 'foo', killer.password
22
+ end
23
+ end
24
+
25
+
26
+ class RunTest < Test::Unit::TestCase
27
+
28
+ include AllTweetsMustDie
29
+ include TweetFixtureProvider
30
+
31
+ def test_request_the_correct_users_timeline_when_updates_are_public
32
+ url = "http://noradio:foo@twitter.com/statuses/user_timeline/noradio.json"
33
+ RestClient.expects(:get).with(url).returns("{}")
34
+ Hitman.new(:username => 'noradio', :password => 'foo').run!
35
+ end
36
+
37
+ def test_request_the_correct_users_timeline_when_updates_are_protected
38
+ url = "http://twitter.com/statuses/user_timeline/noradio.json"
39
+ RestClient.expects(:get).with(url).returns("{}")
40
+ Hitman.new(:username => 'noradio').run!
41
+ end
42
+
43
+ # TODO decouple from implementation and make more specific
44
+ def test_should_kill_tweets_older_than_allowed
45
+ # fix the current time so we can operate on the canned tweets in the fixture
46
+ Time.stubs(:now).returns(Time.parse("Fri Jun 19 14:23:07 +0200 2009"))
47
+ RestClient.stubs(:get).returns(noradios_tweets_as_json)
48
+ Tweet.any_instance.expects(:kill!).times(19) # 20 tweets in fixture. all but the latest tweets are older than 12h.
49
+ Hitman.new(:username => 'noradio').run!
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'mocha'
4
+ require 'redgreen' unless ENV['TM_FILENAME']
5
+
6
+ require 'rest_client'
7
+ require 'json'
8
+ require 'time'
9
+
10
+ module TweetFixtureProvider
11
+
12
+ private
13
+
14
+ def valid_tweet_data(attributes = {})
15
+ tweets = JSON.parse(noradios_tweets_as_json)
16
+ tweets[0].merge(attributes)
17
+ end
18
+
19
+ def noradios_tweets_as_json
20
+ File.read(File.join(File.dirname(__FILE__), '../test/fixtures/user_timeline_noradio.json'))
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__), '../test/test_helper')
2
+ require File.join(File.dirname(__FILE__), '../lib/tweet')
3
+
4
+ class ShouldLiveTest < Test::Unit::TestCase
5
+
6
+ include AllTweetsMustDie
7
+ include TweetFixtureProvider
8
+
9
+ def test_should_live_if_tweet_is_6h_old_and_default_lifetime_is_12h
10
+ data = valid_tweet_data('created_at' => (Time.now - (6 * (60 * 60))).to_s, 'text' => 'Hello World')
11
+ default_lifetime = 12
12
+ assert_equal true, Tweet.new(data).should_live?(default_lifetime)
13
+ end
14
+
15
+ def test_should_die_if_tweet_is_24h_old_and_default_lifetime_is_12h
16
+ data = valid_tweet_data('created_at' => (Time.now - (24 * (60 * 60))).to_s, 'text' => 'Hello World')
17
+ default_lifetime = 12
18
+ assert_equal false, Tweet.new(data).should_live?(default_lifetime)
19
+ end
20
+
21
+ def test_should_live_if_tweet_is_20h_old_and_has_hashtag_with_keep24h
22
+ data = valid_tweet_data('created_at' => (Time.now - (20 * (60 * 60))).to_s, 'text' => 'Hello World #keep24h')
23
+ default_lifetime = 12
24
+ assert_equal true, Tweet.new(data).should_live?(default_lifetime)
25
+ end
26
+
27
+ def test_should_die_if_tweet_is_8h_old_and_has_hashtag_with_keep6h
28
+ data = valid_tweet_data('created_at' => (Time.now - (8 * (60 * 60))).to_s, 'text' => 'Hello World #keep6h')
29
+ default_lifetime = 12
30
+ assert_equal false, Tweet.new(data).should_live?(default_lifetime)
31
+ end
32
+
33
+ end
34
+
35
+
36
+ class KillTest < Test::Unit::TestCase
37
+
38
+ include AllTweetsMustDie
39
+ include TweetFixtureProvider
40
+
41
+ def test_should_call_the_correct_url
42
+ RestClient.expects(:delete).with('http://noradio:noradios_password@twitter.com/statuses/destroy/42.xml')
43
+ Tweet.new(valid_tweet_data('id' => 42)).kill!('noradios_password')
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: phillipoertel-all_tweets_must_die
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Phillip Oertel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Deletes all your tweets older than a certain age
17
+ email: me AT phillipoertel DOHT com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - .gitignore
26
+ - README
27
+ - Rakefile
28
+ - VERSION.yml
29
+ - all_tweets_must_die.gemspec
30
+ - examples/crontab
31
+ - lib/argument_validator.rb
32
+ - lib/hitman.rb
33
+ - lib/tweet.rb
34
+ - test/argument_validator_test.rb
35
+ - test/fixtures/user_timeline_noradio.json
36
+ - test/hitman_test.rb
37
+ - test/test_helper.rb
38
+ - test/tweet_test.rb
39
+ has_rdoc: true
40
+ homepage: https://github.com/phillipoertel/all_tweets_must_die
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Deletes all your tweets older than a certain age
65
+ test_files:
66
+ - test/argument_validator_test.rb
67
+ - test/hitman_test.rb
68
+ - test/test_helper.rb
69
+ - test/tweet_test.rb