pat-maddox-twinkies 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
data/bin/twinkies ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'ostruct'
4
+ $:.unshift File.dirname(__FILE__) + '/../lib'
5
+ require 'twinkies'
6
+
7
+ def run_app(base_dir)
8
+ config = OpenStruct.new
9
+ Dir.chdir(base_dir) do
10
+ eval File.read('twinkies_config.rb')
11
+ config.tweet_db = File.expand_path(config.tweet_db)
12
+ end
13
+ require 'twinkies/server'
14
+ Sinatra::Application.default_options[:env] = :production
15
+ Twinkies::Server.new(config).run
16
+ end
17
+
18
+ if ARGV.size == 1
19
+ username = ARGV.first
20
+
21
+ if File.directory?(username)
22
+ run_app username
23
+ else
24
+ FileUtils.mkdir(username)
25
+ Dir.chdir(username) do
26
+ File.open('twinkies_config.rb', 'w+') do |file|
27
+ file << "config.username='#{username}'\n"
28
+ file << "config.password='CHANGE_ME'\n"
29
+ file << "config.tweet_db='#{username}_tweets.db'\n"
30
+ end
31
+ end
32
+ end
33
+ else
34
+ if File.exists?('twinkies_config.rb')
35
+ run_app '.'
36
+ else
37
+ puts "Usage: twinkies [username]"
38
+ puts "\ttwinkies padillac - creates a new dir named padillac with a config file if none exists, or runs a twinkie instance with the config in that dir"
39
+ puts "\ttwinkies - will run a twinkies instance with the config in the current dir"
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ module Twinkies
2
+ class FriendSearcher
3
+ def initialize(username, password)
4
+ @username = username
5
+ @password = password
6
+ end
7
+
8
+ def search(term)
9
+ Twitter::Base.new(@username, @password).timeline.select do |tweet|
10
+ tweet.text.include?('http')
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+ module Twinkies
2
+ class Item
3
+ include DataMapper::Resource
4
+ property :id, Integer, :serial => true
5
+ property :twitter_id, Integer
6
+ property :link, String
7
+ property :user, String
8
+ property :created_at, DateTime
9
+ property :text, Text, :lazy => false
10
+
11
+ def tweet=(tweet)
12
+ self.twitter_id = tweet.id
13
+ self.text = tweet.text
14
+ self.user = tweet.user.screen_name
15
+ self.created_at = tweet.created_at
16
+ end
17
+
18
+ def save
19
+ @existing = Item.first(:twitter_id => twitter_id)
20
+ if @existing && @existing.link == link
21
+ self.id = @existing.id
22
+ true
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def self.latest
29
+ all :order => [:twitter_id.desc], :limit => 100
30
+ end
31
+
32
+ def self.refresh(username, password)
33
+ raise "Must provide credentials" if username.nil? || password.nil?
34
+
35
+ tweets = FriendSearcher.new(username, password).search('http')
36
+ UrlList.new(tweets) {|t| t.text}.each do |item|
37
+ create :link => item[:url], :tweet => item[:item]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ require 'sinatra'
2
+
3
+ module Twinkies
4
+ class Server
5
+ def initialize(config)
6
+ @tweet_db = config.tweet_db
7
+ @username = config.username
8
+ @password = config.password
9
+ end
10
+
11
+ def run
12
+ setup_db
13
+ start_tweet_refresher
14
+ handle_request
15
+ end
16
+
17
+ def setup_db
18
+ DataMapper.setup(:default, "sqlite3://#{@tweet_db}")
19
+ DataMapper.auto_upgrade!
20
+ end
21
+
22
+ def start_tweet_refresher
23
+ Thread.abort_on_exception = true
24
+ Thread.new do
25
+ loop do
26
+ puts "refreshing tweets..."
27
+ Item.refresh @username, @password
28
+ sleep 60
29
+ end
30
+ end
31
+ end
32
+
33
+ def handle_request
34
+ username = @username # builder must instance_eval?
35
+ get '/feed.xml' do
36
+ builder do |xml|
37
+ xml.instruct!
38
+ xml.rss :version => '2.0' do
39
+ xml.channel do
40
+ xml.title "#{username}'s twitter URL feed"
41
+ xml.description "#{username}'s twitter URL feed"
42
+ xml.link "http://twitter.com/#{username}"
43
+
44
+ Item.latest.each do |tweet|
45
+ xml.item do
46
+ xml.title "#{tweet.user} - #{tweet.text}"
47
+ xml.link tweet.link
48
+ xml.pubDate tweet.created_at
49
+ xml.guid tweet.id, :isPermaLink => false
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ module Twinkies
2
+ class UrlList
3
+ include Enumerable
4
+
5
+ def initialize(*strings)
6
+ strings = strings.first if strings.size == 1 && strings.first.is_a?(Array)
7
+ @urls = strings.inject([]) do |urls, string|
8
+ if block_given?
9
+ yield(string).scan(/http:\/\/\S+/) {|s| urls << {:item => string, :url => s}}
10
+ urls
11
+ else
12
+ urls += string.scan(/http:\/\/\S+/)
13
+ end
14
+ end
15
+ end
16
+
17
+ def each(&block)
18
+ @urls.each &block
19
+ end
20
+
21
+ def inspect
22
+ @urls.inspect
23
+ end
24
+
25
+ def to_s
26
+ @urls.to_s
27
+ end
28
+
29
+ def ==(other)
30
+ @urls == other
31
+ end
32
+ end
33
+ end
data/lib/twinkies.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'dm-core'
3
+ require 'twitter'
4
+ require 'twinkies/friend_searcher'
5
+ require 'twinkies/url_list'
6
+ require 'twinkies/item'
data/spec/item_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module Twinkies
4
+ describe Item do
5
+ describe "created" do
6
+ before(:each) do
7
+ @tweet = Twitter::Status.new
8
+ @tweet.id = 123
9
+ @tweet.text = "hello mang!"
10
+ @tweet.created_at = DateTime.parse('7/24/1985')
11
+ @user = Twitter::User.new
12
+ @user.screen_name = 'padillac'
13
+ @tweet.user = @user
14
+ @item = Item.create :tweet => @tweet, :link => "http://foo"
15
+ end
16
+
17
+ it "should get the twitter ID from the tweet id" do
18
+ @item.twitter_id.should == 123
19
+ end
20
+
21
+ it "should get the text from the tweet" do
22
+ @item.text.should == "hello mang!"
23
+ end
24
+
25
+ it "should get the screen name from the tweet" do
26
+ @item.user.should == 'padillac'
27
+ end
28
+
29
+ it "should get the date from the tweet" do
30
+ @item.created_at.should == DateTime.parse('7/24/1985')
31
+ end
32
+
33
+ describe "created with the same twitter ID" do
34
+ it "should return existing record if URLs are the same" do
35
+ i = Item.create :tweet => @tweet, :link => "http://foo"
36
+ i.should == @item
37
+ end
38
+
39
+ it "should create a new item if URLs are different" do
40
+ i = Item.create :tweet => @tweet, :link => "http://bar"
41
+ i.should_not == @item
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ require 'twinkies'
5
+
6
+ DataMapper.setup(:default, 'sqlite3::memory:')
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.before(:each) { DataMapper.auto_migrate! }
10
+ end
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'ostruct'
3
+
4
+ module Twinkies
5
+ describe UrlList do
6
+ it "should find a url from a string" do
7
+ UrlList.new('http://foo').should include('http://foo')
8
+ end
9
+
10
+ it "should find a url from a list" do
11
+ UrlList.new('http://foo', 'http://bar').
12
+ should include('http://foo', 'http://bar')
13
+ end
14
+
15
+ it "should find a url from an array" do
16
+ UrlList.new(['http://foo', 'http://bar']).
17
+ should include('http://foo', 'http://bar')
18
+ end
19
+
20
+ it "should find a url from a string with extra chars" do
21
+ UrlList.new('I like http://foo !!').should include('http://foo')
22
+ end
23
+
24
+ it "should not find extra characters" do
25
+ UrlList.new('I like http://foo !!').
26
+ should_not include('I', 'like')
27
+ end
28
+
29
+ it "should find multiple urls from a single string" do
30
+ UrlList.new('here: http://foo & http://bar').
31
+ should include('http://foo', 'http://bar')
32
+ end
33
+
34
+ it "should have the enumerable methods" do
35
+ UrlList.new('http://foo http://bar', 'http://baz').
36
+ map.should == ['http://foo', 'http://bar', 'http://baz']
37
+ end
38
+
39
+ describe "working with objects instead of strings" do
40
+ it "should get the strings based on a block" do
41
+ tweet = OpenStruct.new(:text => 'http://foo http://bar')
42
+ UrlList.new(tweet) {|t| t.text }.
43
+ should == [{:item => tweet, :url => 'http://foo'},
44
+ {:item => tweet, :url => 'http://bar'}]
45
+ end
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pat-maddox-twinkies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pat Maddox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-09 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: dm-core
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: "0"
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: do_sqlite3
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0"
41
+ version:
42
+ description: TODO
43
+ email: pat.maddox@gmail.com
44
+ executables:
45
+ - twinkies
46
+ extensions: []
47
+
48
+ extra_rdoc_files: []
49
+
50
+ files:
51
+ - VERSION.yml
52
+ - bin/twinkies
53
+ - lib/twinkies.rb
54
+ - lib/twinkies
55
+ - lib/twinkies/friend_searcher.rb
56
+ - lib/twinkies/server.rb
57
+ - lib/twinkies/url_list.rb
58
+ - lib/twinkies/item.rb
59
+ - spec/spec_helper.rb
60
+ - spec/url_list_spec.rb
61
+ - spec/item_spec.rb
62
+ has_rdoc: false
63
+ homepage: http://github.com/pat-maddox/twinkies
64
+ post_install_message:
65
+ rdoc_options: []
66
+
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.2.0
85
+ signing_key:
86
+ specification_version: 2
87
+ summary: TODO
88
+ test_files: []
89
+