bhauman-twroute 0.1.0

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 (41) hide show
  1. data/.document +5 -0
  2. data/.gitignore +10 -0
  3. data/README.rdoc +13 -0
  4. data/Rakefile +91 -0
  5. data/VERSION +1 -0
  6. data/example_app/Rakefile +5 -0
  7. data/example_app/config/config.yml.sample +16 -0
  8. data/example_app/config/twroutes.rb +3 -0
  9. data/example_app/scripts/twrouter.rb +10 -0
  10. data/lib/twroute/application/config.rb +30 -0
  11. data/lib/twroute/application/twrouter.rb +27 -0
  12. data/lib/twroute/application.rb +22 -0
  13. data/lib/twroute/dispatcher.rb +48 -0
  14. data/lib/twroute/parser/regex.rb +42 -0
  15. data/lib/twroute/parser/tags.rb +26 -0
  16. data/lib/twroute/parser/users.rb +27 -0
  17. data/lib/twroute/requester/basic.rb +13 -0
  18. data/lib/twroute/requester/delayed/create_delayed_jobs.rb +21 -0
  19. data/lib/twroute/requester/delayed/db_connection.rb +8 -0
  20. data/lib/twroute/requester/delayed.rb +21 -0
  21. data/lib/twroute/routes.rb +22 -0
  22. data/lib/twroute/tasks.rb +60 -0
  23. data/lib/twroute/tweet_printer.rb +7 -0
  24. data/lib/twroute/tweet_route.rb +64 -0
  25. data/lib/twroute/tweeter.rb +35 -0
  26. data/lib/twroute.rb +12 -0
  27. data/test/config_test.rb +19 -0
  28. data/test/dispatcher_test.rb +62 -0
  29. data/test/fixtures/config.yml +16 -0
  30. data/test/reqex_parser_test.rb +27 -0
  31. data/test/routes_test.rb +25 -0
  32. data/test/tags_parser_test.rb +30 -0
  33. data/test/test_helper.rb +15 -0
  34. data/test/test_suite.rb +11 -0
  35. data/test/tweet_maker.rb +47 -0
  36. data/test/tweet_maker_test.rb +13 -0
  37. data/test/tweet_route_test.rb +30 -0
  38. data/test/tweeter_test.rb +33 -0
  39. data/test/users_parser_test.rb +30 -0
  40. data/twroute.gemspec +96 -0
  41. metadata +133 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ example_app/config/config.yml
7
+ *~
8
+ example_app/log/*.log
9
+ example_app/db/twroute.sqlite3
10
+ #*
data/README.rdoc ADDED
@@ -0,0 +1,13 @@
1
+ = Twroute - Route Twitter status updates over http to your web app
2
+
3
+ As a Rails developer the motivation to write this gem is because
4
+ I wanted a super simple way to write a twitter app where my app
5
+ can recieve messages from twitter over http and respond to the sender
6
+ over http.
7
+
8
+ In other words a controller can recieve a tweet and then reply to the
9
+ sender by simply rendering a view.
10
+
11
+ Cool huh? Well I like it.
12
+
13
+ == more docs soon?
data/Rakefile ADDED
@@ -0,0 +1,91 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
5
+
6
+
7
+ namespace :jobs do
8
+ desc "Clear the delayed_job queue."
9
+ task :clear do
10
+
11
+ Delayed::Job.delete_all
12
+ end
13
+
14
+ desc "Start a delayed_job worker."
15
+ task :work do
16
+ #require 'twroute'
17
+ #require 'twroute/requester/delayed'
18
+ #require 'twroute/requester/delayed/create_delayed_jobs'
19
+ Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY']).start
20
+ end
21
+
22
+ desc 'creates delayed job database: not implemented'
23
+ desc :create_database do
24
+
25
+ end
26
+
27
+ desc "Create delayed_job table"
28
+ task :create_table do
29
+ CreateDelayedJobs.migrate(:up)
30
+ end
31
+
32
+ desc "Create delayed_job table"
33
+ task :drop_table do
34
+ CreateDelayedJobs.migrate(:down)
35
+ end
36
+ end
37
+
38
+ begin
39
+ require 'jeweler'
40
+ Jeweler::Tasks.new do |gem|
41
+ gem.name = "twroute"
42
+ gem.summary = %Q{Twroute listens for Twitter updates and redirects them to HTTP post requests}
43
+ gem.description = %Q{Twroute listens for Twitter updates and redirects them to HTTP post requests}
44
+ gem.email = "bhauman@gmail.com"
45
+ gem.homepage = "http://github.com/bhauman/twroute"
46
+ gem.authors = ["bhauman"]
47
+ gem.add_dependency('activerecord', '>= 2.1.0')
48
+ gem.add_dependency('tobi-delayed_job', '>= 1.7.0')
49
+ gem.add_dependency('brianmario-yajl-ruby', '>= 0.5.12')
50
+ gem.add_dependency('deamons', '>= 1.0.10')
51
+ end
52
+
53
+ rescue LoadError
54
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
55
+ end
56
+
57
+ require 'rake/testtask'
58
+ Rake::TestTask.new(:test) do |test|
59
+ test.libs << 'lib' << 'test'
60
+ test.pattern = 'test/**/*_test.rb'
61
+ test.verbose = true
62
+ end
63
+
64
+ begin
65
+ require 'rcov/rcovtask'
66
+ Rcov::RcovTask.new do |test|
67
+ test.libs << 'test'
68
+ test.pattern = 'test/**/*_test.rb'
69
+ test.verbose = true
70
+ end
71
+ rescue LoadError
72
+ task :rcov do
73
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
74
+ end
75
+ end
76
+
77
+ task :default => :test
78
+
79
+ require 'rake/rdoctask'
80
+ Rake::RDocTask.new do |rdoc|
81
+ if File.exist?('VERSION')
82
+ version = File.read('VERSION')
83
+ else
84
+ version = ""
85
+ end
86
+
87
+ rdoc.rdoc_dir = 'rdoc'
88
+ rdoc.title = "twroute #{version}"
89
+ rdoc.rdoc_files.include('README*')
90
+ rdoc.rdoc_files.include('lib/**/*.rb')
91
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,5 @@
1
+ require 'twroute'
2
+ require 'twroute/tasks'
3
+
4
+ Twroute::Tasks.new
5
+
@@ -0,0 +1,16 @@
1
+ submit_to:
2
+ host: localhost
3
+ port: 3000
4
+ http_auth_user: ''
5
+ http_auth_password: ''
6
+ twitter:
7
+ user: ''
8
+ password: ''
9
+ stream_api: track
10
+ stream_api_args:
11
+ track: twitter
12
+ database:
13
+ adapter: sqlite3
14
+ database: db/twroute.sqlite3
15
+ pool: 5
16
+ timeout: 5000
@@ -0,0 +1,3 @@
1
+ Twroute::Routes.draw do |map|
2
+ map.regex( {:tweet_text => /.*/ }, '/tweets/create' )
3
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'daemons'
4
+ require 'twroute'
5
+ require 'twroute/application'
6
+
7
+ Daemons.run_proc('twrout_proc.rb') do
8
+ ActiveRecord::Base.logger = Logger.new(File.open(File.join(::Twroute::Application.root_dir, 'log/database.log'), 'a'))
9
+ Twroute::Application.run
10
+ end
@@ -0,0 +1,30 @@
1
+ require 'yaml'
2
+ require 'ostruct'
3
+
4
+ module Twroute
5
+ module Application
6
+ def self.config
7
+ @@config ||= { }
8
+ end
9
+ def self.config=(config)
10
+ @@config = config
11
+ end
12
+ def self.root_dir
13
+ @@root_dir ||= Dir.pwd
14
+ end
15
+ def self.root_dir=(root)
16
+ @@root_dir = root
17
+ end
18
+ class Config
19
+ def self.load_config(file)
20
+ Application.config = self.open_struct(YAML.load_file(file))
21
+ end
22
+ def self.open_struct(hash)
23
+ hash.inject(OpenStruct.new) do |result, (key, value)|
24
+ result.send(key + '=', value.is_a?(Hash) ? open_struct(value) : value)
25
+ result
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ require 'twroute/requester/delayed'
2
+
3
+ module Twroute
4
+ module Application
5
+ class Twrouter
6
+ attr_accessor :config
7
+ def initialize
8
+ self.config = ::Twroute::Application.config
9
+ setup
10
+ end
11
+ def setup
12
+ @tweeter = ::Twroute::Tweeter.new(config.twitter.user,
13
+ config.twitter.password,
14
+ config.twitter.stream_api,
15
+ config.twitter.stream_api_args.marshal_dump)
16
+ @dispatcher = ::Twroute::Dispatcher.new( config.submit_to.marshal_dump,
17
+ *::Twroute::Routes.routes )
18
+ @requester = ::Twroute::Requester::Delayed.new
19
+ @tweeter.add_observer @dispatcher
20
+ @dispatcher.add_observer @requester
21
+ end
22
+ def start
23
+ @tweeter.start
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # load the routes
2
+ require File.join(Dir.pwd, 'config', 'twroutes.rb')
3
+
4
+ # load the config
5
+ require 'twroute/application/config'
6
+ ::Twroute::Application::Config.load_config(File.join(Dir.pwd, 'config', 'config.yml'))
7
+ ::Twroute::Application.root_dir = Dir.pwd
8
+
9
+ # this pulls in delayed job and is dependant on the config having been read
10
+ require 'twroute/application/twrouter'
11
+ module Twroute
12
+ module Application
13
+ def self.run
14
+ @@twrouter = ::Twroute::Application::Twrouter.new
15
+ @@twrouter.start
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+
22
+
@@ -0,0 +1,48 @@
1
+ require 'uri'
2
+
3
+ module Twroute
4
+ class Dispatcher
5
+ include Observable
6
+
7
+ attr_accessor :tweet_routes
8
+
9
+ def initialize(options, *args)
10
+ @options = {
11
+ :host => "",
12
+ :port => false,
13
+ :http_auth_user => false,
14
+ :http_auth_password => false
15
+ }.merge(options)
16
+ self.tweet_routes = [*args]
17
+ end
18
+
19
+ def update(tweet_hash)
20
+ route = find_route(tweet_hash)
21
+ dispatch_route(route) if route
22
+ end
23
+
24
+ def find_route(tweet_hash)
25
+ tweet_routes.find do |route|
26
+ route.tweet(tweet_hash)
27
+ return route if route.is_match?
28
+ end
29
+ end
30
+
31
+ def dispatch_route(route)
32
+ uri = get_uri(route)
33
+ post_args = route.get_post_args
34
+ changed
35
+ notify_observers(uri, post_args)
36
+ end
37
+
38
+ def get_uri(route)
39
+ url = "http://"
40
+ url += "#{@options[:http_auth_user]}:#{@options[:http_auth_password]}@" if @options[:http_auth_user] && @options[:http_auth_password]
41
+ url += "#{@options[:host]}"
42
+ url += ":#{@options[:port]}" if @options[:port]
43
+ url += route.path
44
+ URI.parse(url)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ module Twroute
2
+ module Parser
3
+ class Regex
4
+ attr_accessor :regex_hash
5
+
6
+ def initialize(regex_hash)
7
+ self.regex_hash = regex_hash
8
+ end
9
+
10
+ def parse_tweet(tweet_text)
11
+ @parsed_hash = parse_to_hash(tweet_text)
12
+ is_match? ? @parsed_hash : false
13
+ end
14
+
15
+ def is_match?
16
+ @parsed_hash.values.inject(true) do |accum, val|
17
+ accum && !!val
18
+ end
19
+ end
20
+
21
+ def parse_to_hash(tweet_text)
22
+ regex_hash.inject({ }) do |accum, key_val|
23
+ match_data = tweet_text.match(key_val.last)
24
+ accum[key_val.first] = (process_data(match_data) ? match_data[0] : nil)
25
+ accum
26
+ end
27
+ end
28
+
29
+ # delayed job can't unmarshal things that have lines that end in :
30
+ def process_data(data)
31
+ if(data && data.is_a?(String))
32
+ data.gsub(/:$/m, '')
33
+ else
34
+ data
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+
42
+
@@ -0,0 +1,26 @@
1
+ module Twroute
2
+ module Parser
3
+ class Tags
4
+ TAG_REGEX = /\#([\w\d_]+)/
5
+
6
+ def parse_tweet(tweet_text)
7
+ @parsed_hash = parse_to_hash(tweet_text)
8
+ end
9
+
10
+ def is_match?
11
+ !@parsed_hash.empty?
12
+ end
13
+
14
+ def parse_to_hash(tweet_text)
15
+ tags = parse_tags_out(tweet_text)
16
+ return { } if tags.empty?
17
+ { :tags => tags.join('-') }
18
+ end
19
+
20
+ def parse_tags_out(tweet_text = "")
21
+ tweet_text.scan(TAG_REGEX).to_a.flatten
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,27 @@
1
+ module Twroute
2
+ module Parser
3
+ class Users
4
+ USERS_REGEX = /\@([\w\d_]+)/
5
+
6
+ def parse_tweet(tweet_text)
7
+ @parsed_hash = parse_to_hash(tweet_text)
8
+ end
9
+
10
+ def is_match?
11
+ !@parsed_hash.empty?
12
+ end
13
+
14
+ def parse_to_hash(tweet_text)
15
+ users = parse_users_out(tweet_text)
16
+ return { } if users.empty?
17
+ { :users => users.join('-') }
18
+ end
19
+
20
+ def parse_users_out(tweet_text = "")
21
+ tweet_text.scan(USERS_REGEX).to_a.flatten
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+
@@ -0,0 +1,13 @@
1
+ require 'net/http'
2
+
3
+ module Twroute
4
+ module Requester
5
+ class Basic
6
+ def update(uri, post_args)
7
+ Thread.new(uri, post_args) do |uri, post_args|
8
+ response = Net::HTTP.post_form(uri, post_args)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ class CreateDelayedJobs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :delayed_jobs, :force => true do |table|
4
+ table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
5
+ table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
6
+ table.text :handler # YAML-encoded string of the object that will do work
7
+ table.text :last_error # reason for last failure (See Note below)
8
+ table.datetime :run_at # When to run. Could be Time.now for immediately, or sometime in the future.
9
+ table.datetime :locked_at # Set when a client is working on this object
10
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
11
+ table.string :locked_by # Who is working on this object (if locked)
12
+ table.timestamps
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table :delayed_jobs
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,8 @@
1
+ require 'activerecord'
2
+
3
+ ::Twroute::Application.config.database.database = File.join(::Twroute::Application.root_dir,
4
+ ::Twroute::Application.config.database.database).to_s
5
+
6
+ ActiveRecord::Base.establish_connection(::Twroute::Application.config.database.marshal_dump)
7
+
8
+
@@ -0,0 +1,21 @@
1
+ require 'twroute/requester/delayed/db_connection'
2
+ require 'delayed_job'
3
+
4
+ require 'net/http'
5
+ module Twroute
6
+ module Requester
7
+
8
+ class Delayed
9
+ def update(uri, post_args)
10
+ ::Delayed::Job.enqueue DelayedRequest.new(uri, post_args)
11
+ end
12
+ end
13
+
14
+ class DelayedRequest < Struct.new(:uri, :post_args)
15
+ def perform
16
+ response = Net::HTTP.post_form(uri, post_args)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module Twroute
2
+ class Routes
3
+ def self.routes
4
+ @@routes ||= []
5
+ @@routes
6
+ end
7
+ def self.append_route(x)
8
+ @@routes ||= []
9
+ @@routes << x
10
+ end
11
+ def self.draw(&block)
12
+ yield Collector
13
+ end
14
+ class Collector
15
+ def self.regex(regex_hash, url_pattern)
16
+ Routes.append_route(
17
+ TweetRoute.new(Parser::Regex.new(regex_hash),
18
+ url_pattern))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,60 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module Twroute
5
+ class Tasks < ::Rake::TaskLib
6
+ def initialize
7
+ namespace :twroute do
8
+ task :load_env do
9
+ require 'twroute'
10
+ require 'twroute/application'
11
+ end
12
+
13
+ desc "create database"
14
+ task :create_database => :load_env do
15
+ require 'sqlite3'
16
+ db = SQLite3::Database.new(Twroute::Application.config.database.database)
17
+ db.close
18
+ end
19
+
20
+ desc "Create delayed_job table"
21
+ task :create_table => :load_env do
22
+ require 'twroute/requester/delayed/create_delayed_jobs'
23
+ CreateDelayedJobs.migrate(:up)
24
+ end
25
+
26
+ desc "create required directories"
27
+ task :create_dirs do
28
+ require 'fileutils'
29
+ ['log', 'db'].each do |dir|
30
+ FileUtils.mkdir_p(dir) if !File.exists?(dir)
31
+ end
32
+ end
33
+
34
+ desc "initialize twroute app : creates database : creates table : creates directories"
35
+ task :init => [:create_dirs, :create_database, :create_table] do
36
+
37
+ end
38
+
39
+ desc "Delete delayed_job table"
40
+ task :drop_table => :load_env do
41
+ require 'twroute/requester/delayed/create_delayed_jobs'
42
+ CreateDelayedJobs.migrate(:down)
43
+ end
44
+
45
+ desc "Clear the delayed_job queue."
46
+ task :clear => :load_env do
47
+ ActiveRecord::Base.logger = Logger.new(File.open('log/delayed.log', 'a'))
48
+ ::Delayed::Job.delete_all
49
+ end
50
+
51
+ desc "Start a delayed_job worker."
52
+ task :work => :load_env do
53
+ ActiveRecord::Base.logger = Logger.new(File.open('log/delayed.log', 'a'))
54
+ ::Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'],
55
+ :max_priority => ENV['MAX_PRIORITY']).start
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ module Twroute
2
+ class TweetPrinter
3
+ def update(tweet_hash)
4
+ puts tweet_hash.inspect
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ module Twroute
2
+ class TweetRoute
3
+ attr_accessor :tweet_parser, :url_pattern
4
+ def initialize(tweet_parser, url_pattern)
5
+ self.tweet_parser = tweet_parser
6
+ self.url_pattern = url_pattern
7
+ end
8
+
9
+ def tweet(tweet_hash)
10
+ reset
11
+ @tweet_hash = tweet_hash
12
+ @sender_hash = tweet_hash.delete(:user)
13
+ parse_tweet(tweet_hash[:text])
14
+ end
15
+
16
+ def reset
17
+ @tweet_hash = { }
18
+ @sender_hash = { }
19
+ @parsed_hash = { }
20
+ end
21
+
22
+ def parse_tweet(tweet_text)
23
+ @parsed_hash = tweet_parser.parse_tweet(tweet_text) || { }
24
+ end
25
+
26
+ def is_match?
27
+ tweet_parser.is_match?
28
+ end
29
+
30
+ def path
31
+ fill_in_url_pattern
32
+ end
33
+
34
+ def fill_in_url_pattern
35
+ pattern = self.url_pattern
36
+ @parsed_hash.keys.each do |key|
37
+ pattern.gsub!(':' + key.to_s, @parsed_hash[key])
38
+ end
39
+
40
+ tweet_h = @tweet_hash.merge({ })
41
+ tweet_h.delete(:user)
42
+ tweet_h.keys.each do |key|
43
+ pattern.gsub!(':tweet[' + key.to_s + ']', @tweet_hash[key].to_s)
44
+ end
45
+
46
+ pattern
47
+ end
48
+
49
+ def get_post_args
50
+ post_hash = { }
51
+ @parsed_hash.keys.each do |key|
52
+ post_hash[key.to_s] = @parsed_hash[key]
53
+ end
54
+ @sender_hash.keys.each do |key|
55
+ post_hash["sender[#{key.to_s}]"] = @sender_hash[key]
56
+ end
57
+ @tweet_hash.keys.each do |key|
58
+ post_hash["tweet[#{key.to_s}]"] = @tweet_hash[key]
59
+ end
60
+ post_hash
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ require 'yajl'
2
+ require 'yajl/http_stream'
3
+ require 'observer'
4
+ require 'uri'
5
+
6
+ module Twroute
7
+ class Tweeter
8
+ include Observable
9
+
10
+ def initialize(user, password, twitter_stream, post_arg_hash = {})
11
+ @user = user
12
+ @password = password
13
+ @twitter_stream = twitter_stream
14
+ @post_arg_hash = post_arg_hash
15
+ end
16
+
17
+ def twitter_stream_uri
18
+ URI.parse("http://#{@user}:#{@password}@stream.twitter.com/#{@twitter_stream}.json?#{self.query_string}")
19
+ end
20
+
21
+ def query_string
22
+ @post_arg_hash.keys.collect do |key|
23
+ "#{key}=#{URI.escape(@post_arg_hash[key])}"
24
+ end.sort_by { |x| x }.join('&')
25
+ end
26
+
27
+ def start
28
+ puts twitter_stream_uri.to_s
29
+ Yajl::HttpStream.get(twitter_stream_uri, :symbolize_keys => true) do |hash|
30
+ changed
31
+ notify_observers( hash )
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/twroute.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'twroute/tweeter'
3
+ require 'twroute/routes'
4
+ require 'twroute/dispatcher'
5
+ require 'twroute/tweet_route'
6
+ require 'twroute/parser/regex'
7
+ require 'twroute/parser/tags'
8
+ require 'twroute/parser/users'
9
+
10
+
11
+
12
+
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+ require 'twroute/application/config'
3
+
4
+ class ConfigTest < Test::Unit::TestCase
5
+ include Twroute
6
+
7
+ context "with loaded config" do
8
+ setup do
9
+ file = File.join(File.dirname(__FILE__), 'fixtures', 'config.yml')
10
+ Application::Config.load_config(file)
11
+ end
12
+ should "have config loaded" do
13
+ Application.config.database.nil?.should == false
14
+ Application.config.database.adapter.should == 'sqlite3'
15
+ Application.config.twitter.stream_api_args.track.should == 'twitter'
16
+ Application.config.submit_to.host.should == 'localhost'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+ require 'twroute/dispatcher'
3
+
4
+ class DispatcherTest < Test::Unit::TestCase
5
+ include Twroute
6
+
7
+ context "with dispatcher" do
8
+ setup do
9
+ @tweet_route_user = TweetRoute.new(Parser::Regex.new({:user => /\@\w+/}), '/my_test_path/:user')
10
+ @tweet_route_tag = TweetRoute.new(Parser::Regex.new({:tag => /\#\w+/}), '/my_test_path/:tag')
11
+ @tweet_route_user_and_tag = TweetRoute.new(Parser::Regex.new({:tag => /\#\w+/,
12
+ :user => /\@\w+/ }),
13
+ '/my_test_path/:tag/:user')
14
+ @dispatcher = Dispatcher.new( {:host => 'test-host.com'},
15
+ @tweet_route_user_and_tag,
16
+ @tweet_route_user,
17
+ @tweet_route_tag )
18
+
19
+ @match_user_tweet = TweetMaker.make_tweet :text => "@this is cool too here 3about_it"
20
+ @match_tag_tweet = TweetMaker.make_tweet :text => "#this is cool too here 3about_it"
21
+ @match_user_and_tag_tweet = TweetMaker.make_tweet :text => "@random #this is cool too here 3about_it"
22
+ end
23
+
24
+ should "match be able to get the first matched route" do
25
+ @dispatcher.tweet_routes.length.should == 3
26
+
27
+ @dispatcher.find_route(@match_user_and_tag_tweet).should == @tweet_route_user_and_tag
28
+ @dispatcher.find_route(@match_user_tweet).should == @tweet_route_user
29
+ @dispatcher.find_route(@match_tag_tweet).should == @tweet_route_tag
30
+
31
+ end
32
+
33
+ should "be able to handle one route" do
34
+ disp = Dispatcher.new( {:host => 'test-host.com'},
35
+ @tweet_route_user
36
+ )
37
+ disp.find_route(@match_user_tweet).should == @tweet_route_user
38
+ end
39
+
40
+ should "to create a uri" do
41
+ disp = Dispatcher.new({:host => 'test-host.com'},
42
+ @tweet_route_user)
43
+ route = stub(:path, :return => '/path')
44
+ disp.get_uri(route).to_s.should == 'http://test-host.com/path'
45
+ disp = Dispatcher.new({:host => 'test-host.com',
46
+ :http_auth_user => 'jay'},
47
+ @tweet_route_user)
48
+ disp.get_uri(route).to_s.should == 'http://test-host.com/path'
49
+ disp = Dispatcher.new({:host => 'test-host.com',
50
+ :http_auth_user => 'jay',
51
+ :http_auth_password => 'man'},
52
+ @tweet_route_user)
53
+ disp.get_uri(route).to_s.should == 'http://jay:man@test-host.com/path'
54
+ disp = Dispatcher.new({:host => 'test-host.com',
55
+ :port => '3000'},
56
+ @tweet_route_user)
57
+ disp.get_uri(route).to_s.should == 'http://test-host.com:3000/path'
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,16 @@
1
+ submit_to:
2
+ host: localhost
3
+ port: 3000
4
+ http_auth_user: auth_user
5
+ http_auth_password: auth_pass
6
+ twitter:
7
+ user: twitter_user
8
+ password: twitter_pass
9
+ stream_api: track
10
+ stream_api_args:
11
+ track: twitter
12
+ database:
13
+ adapter: sqlite3
14
+ database: db/twroute.sqlite3
15
+ pool: 5
16
+ timeout: 5000
@@ -0,0 +1,27 @@
1
+ require 'test_helper.rb'
2
+
3
+ class RegexParserTest < Test::Unit::TestCase
4
+ context "with regex parser" do
5
+ setup do
6
+ @regex_parser = Twroute::Parser::Regex.new(:whole => /\@\w+\s+(\#\w+\s)+for\s+(\d+)\s+(day|week|month|year)s{0,1}\s+.*/,
7
+ :user => /\@\w+/,
8
+ :tags => /(\#\w+\s)+/,
9
+ :time => /for\s+(\d+)\s+(day|week|month|year)s{0,1}/)
10
+ @tweet_str = "@johnny #this #is #cool for 17 days yeah buddy"
11
+ end
12
+
13
+ should "parse hash" do
14
+ hash = @regex_parser.parse_to_hash(@tweet_str)
15
+ assert hash
16
+ hash[:whole].should == "@johnny #this #is #cool for 17 days yeah buddy"
17
+ hash[:user].should == '@johnny'
18
+ hash[:tags].should == '#this #is #cool '
19
+ hash[:time].should == 'for 17 days'
20
+ end
21
+
22
+ should "return false if all regexes don't match" do
23
+ @regex_parser.parse_tweet("@johnny #this #is #cool 17 days yeah buddy").should == false
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class RoutesTest < Test::Unit::TestCase
4
+ include Twroute
5
+ context "with routes" do
6
+ setup do
7
+ Routes.draw do |map|
8
+ map.regex({ :tweet_text => /.*/ },
9
+ '/tweet/create')
10
+ map.regex({ :tweet_text => /happy/ },
11
+ '/happy/create')
12
+ map.regex({ :tweet_text => /happy2/ },
13
+ '/happy2/create')
14
+ end
15
+ end
16
+
17
+ should "have created the tweet routes and agregated them" do
18
+ Routes.routes.length.should == 3
19
+ Routes.routes[0].is_a?(TweetRoute).should == true
20
+ Routes.routes[0].tweet_parser.is_a?(Parser::Regex).should == true
21
+ Routes.routes[0].url_pattern.should == '/tweet/create'
22
+ Routes.routes[2].url_pattern.should == '/happy2/create'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+ class TagsParserTest < Test::Unit::TestCase
3
+ context "with tags parser" do
4
+ setup do
5
+ @tags_parser = Twroute::Parser::Tags.new
6
+ @tags_str = "#this #is cool #too here #3about_it"
7
+ end
8
+
9
+ should "be able to determine a match" do
10
+ @tags_parser.parse_tweet(@tags_str)
11
+ @tags_parser.is_match?.should == true
12
+ @tags_parser.parse_tweet("hello there")
13
+ @tags_parser.is_match?.should == false
14
+ end
15
+
16
+ should "parse twitter tags from a string" do
17
+ @tags_parser.parse_tags_out(@tags_str).should == ['this', 'is', 'too', '3about_it']
18
+ end
19
+
20
+ should "be able to parse nil string" do
21
+ @tags_parser.parse_tags_out().should == []
22
+ end
23
+
24
+ should "return hash of tags" do
25
+ @tags_parser.parse_to_hash(@tags_str).should == {:tags => 'this-is-too-3about_it'}
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'matchy'
5
+ require 'stump'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+
10
+ require 'twroute'
11
+
12
+ class Test::Unit::TestCase
13
+ end
14
+
15
+
@@ -0,0 +1,11 @@
1
+ require 'test/unit'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+
5
+ require 'users_parser_test'
6
+ require 'tags_parser_test'
7
+ require 'reqex_parser_test'
8
+ require 'tweet_route_test'
9
+ require 'tweeter_test'
10
+ require 'tweet_maker_test'
11
+ require 'dispatcher_test'
@@ -0,0 +1,47 @@
1
+ class TweetMaker
2
+
3
+ def self.make_tweet(tweet_hash = { })
4
+ tweet = { }.merge(BASE_TWEET)
5
+ user = tweet_hash.delete(:user)
6
+ tweet[:user].merge!(user || { })
7
+ tweet.merge!(tweet_hash)
8
+ tweet
9
+ end
10
+
11
+ BASE_TWEET = {
12
+ :source=>"web",
13
+ :user=>{:url=>"http://test_host.com/",
14
+ :friends_count=>275,
15
+ :time_zone=>"Central Time (US & Canada)",
16
+ :screen_name=>"test_screen_name",
17
+ :notifications=>nil,
18
+ :utc_offset=>-21600,
19
+ :profile_image_url=>"http://s3.amazonaws.com/twitter_production/profile_images/352932267/twitterProfilePhoto_normal.jpg",
20
+ :protected=>false,
21
+ :verified=>false,
22
+ :profile_text_color=>"000000",
23
+ :name=>"test_name",
24
+ :profile_sidebar_border_color=>"D5D39E",
25
+ :statuses_count=>625,
26
+ :profile_link_color=>"B4C67C",
27
+ :location=>"Asheville NC",
28
+ :following=>nil,
29
+ :profile_background_image_url=>"http://s3.amazonaws.com/twitter_production/profile_background_images/2475770/background.png",
30
+ :description=>"I beleive that life is pretty darn cool",
31
+ :followers_count=>211,
32
+ :created_at=>"Thu May 01 02:11:28 +0000 2008",
33
+ :profile_background_tile=>true,
34
+ :id=>14609143,
35
+ :profile_background_color=>"9ae4e8",
36
+ :profile_sidebar_fill_color=>"778F5F",
37
+ :favourites_count=>19},
38
+ :text=>"this is a #test #tweet",
39
+ :favorited=>false,
40
+ :in_reply_to_screen_name=>nil,
41
+ :in_reply_to_status_id=>nil,
42
+ :created_at=>"Sun Aug 09 00:25:46 +0000 2009",
43
+ :id=>3199928174,
44
+ :in_reply_to_user_id=>nil,
45
+ :truncated=>false}
46
+ end
47
+
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+ require 'tweet_maker'
3
+ class TweetMakerTest < Test::Unit::TestCase
4
+ context "tweet maker" do
5
+ should "be able to make a tweet hash" do
6
+ tweet = TweetMaker.make_tweet({ :text => 'test test test', :user => { :name => 'testy_name'}})
7
+ tweet[:source].should == 'web'
8
+ tweet[:text].should == 'test test test'
9
+ tweet[:user][:url].should == 'http://test_host.com/'
10
+ tweet[:user][:name].should == 'testy_name'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper.rb'
2
+ class TweetRouteTest < Test::Unit::TestCase
3
+ context "with users parser" do
4
+ setup do
5
+ @tweet_route = Twroute::TweetRoute.new(Twroute::Parser::Tags.new, '/user/:tweet[sender]/tags/:tags')
6
+ @tweet_str = "#this #is cool #too"
7
+ @tweet = {:source=>"<a href=\"http://twitterfox.net/\">TwitterFox</a>", :user=>{:url=>"http://rosed1st.multiply.com/", :friends_count=>275, :time_zone=>"Central Time (US & Canada)", :screen_name=>"RoseD1st", :notifications=>nil, :utc_offset=>-21600, :profile_image_url=>"http://s3.amazonaws.com/twitter_production/profile_images/352932267/twitterProfilePhoto_normal.jpg", :protected=>false, :verified=>false, :profile_text_color=>"000000", :name=>"RoseD1st", :profile_sidebar_border_color=>"D5D39E", :statuses_count=>625, :profile_link_color=>"B4C67C", :location=>"Mad as Hell!", :following=>nil, :profile_background_image_url=>"http://s3.amazonaws.com/twitter_production/profile_background_images/2475770/background.png", :description=>"A conservatve whos had it with Obamas lies!I'm a proud \342\200\234Birther\342\200\235 and only those who haven't looked at the facts think there's no problem", :followers_count=>211, :created_at=>"Thu May 01 02:11:28 +0000 2008", :profile_background_tile=>true, :id=>14609143, :profile_background_color=>"9ae4e8", :profile_sidebar_fill_color=>"778F5F", :favourites_count=>19}, :text=>"this is a #test #tweet", :favorited=>false, :in_reply_to_screen_name=>nil, :in_reply_to_status_id=>nil, :created_at=>"Sun Aug 09 00:25:46 +0000 2009", :id=>3199928174, :in_reply_to_user_id=>nil, :truncated=>false}
8
+ end
9
+
10
+ should "be able to determine if the required hash keys have been met" do
11
+ @tweet_route.parse_tweet(@tweet_str)
12
+ @tweet_route.is_match?.should == true
13
+ end
14
+
15
+ should "be able to fill in a url_pattern" do
16
+ @tweet_route.tweet({ :text => @tweet_str, :sender => 'charlie' })
17
+ @tweet_route.fill_in_url_pattern.should == '/user/charlie/tags/this-is-too'
18
+ end
19
+
20
+ should "be able to generate post args" do
21
+ @tweet_route.tweet(@tweet)
22
+ post_args = @tweet_route.get_post_args
23
+ post_args['sender[screen_name]'].should == 'RoseD1st'
24
+ post_args['tweet[text]'].should == 'this is a #test #tweet'
25
+ post_args['tags'].should == 'test-tweet'
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper.rb'
2
+ require 'tweeter'
3
+
4
+ class TweeterTest < Test::Unit::TestCase
5
+ def setup
6
+ end
7
+
8
+ def teardown
9
+ end
10
+
11
+ context "with tweeter" do
12
+ setup do
13
+ @tweeter = Twroute::Tweeter.new('bhauman',
14
+ 'watertank',
15
+ 'track',
16
+ :track => 'twitter' )
17
+ end
18
+
19
+ should "be able to create query string" do
20
+ @tweeter = Twroute::Tweeter.new('bhauman',
21
+ 'watertank',
22
+ 'track',
23
+ :track => 'twitter', :twank => 'twonkle' )
24
+ @tweeter.query_string.should == 'track=twitter&twank=twonkle'
25
+ end
26
+
27
+ should "be able to create a twitter search url" do
28
+ @tweeter.twitter_stream_uri.to_s.should == 'http://bhauman:watertank@stream.twitter.com/track.json?track=twitter'
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+ class UsersParserTest < Test::Unit::TestCase
3
+ context "with users parser" do
4
+ setup do
5
+ @users_parser = Twroute::Parser::Users.new
6
+ @users_str = "@this @is cool @too here @3about_it"
7
+ end
8
+
9
+ should "be able to determine a match" do
10
+ @users_parser.parse_tweet(@users_str)
11
+ @users_parser.is_match?.should == true
12
+ @users_parser.parse_tweet("hello there")
13
+ @users_parser.is_match?.should == false
14
+ end
15
+
16
+ should "parse twitter users from a string" do
17
+ @users_parser.parse_users_out(@users_str).should == ['this', 'is', 'too', '3about_it']
18
+ end
19
+
20
+ should "be able to parse nil string" do
21
+ @users_parser.parse_users_out().should == []
22
+ end
23
+
24
+ should "return hash of users" do
25
+ @users_parser.parse_to_hash(@users_str).should == {:users => 'this-is-too-3about_it'}
26
+ end
27
+
28
+ end
29
+
30
+ end
data/twroute.gemspec ADDED
@@ -0,0 +1,96 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{twroute}
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 = ["bhauman"]
9
+ s.date = %q{2009-08-12}
10
+ s.description = %q{Twroute listens for Twitter updates and redirects them to HTTP post requests}
11
+ s.email = %q{bhauman@gmail.com}
12
+ s.extra_rdoc_files = [
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".document",
17
+ ".gitignore",
18
+ "README.rdoc",
19
+ "Rakefile",
20
+ "VERSION",
21
+ "example_app/Rakefile",
22
+ "example_app/config/config.yml.sample",
23
+ "example_app/config/twroutes.rb",
24
+ "example_app/scripts/twrouter.rb",
25
+ "lib/twroute.rb",
26
+ "lib/twroute/application.rb",
27
+ "lib/twroute/application/config.rb",
28
+ "lib/twroute/application/twrouter.rb",
29
+ "lib/twroute/dispatcher.rb",
30
+ "lib/twroute/parser/regex.rb",
31
+ "lib/twroute/parser/tags.rb",
32
+ "lib/twroute/parser/users.rb",
33
+ "lib/twroute/requester/basic.rb",
34
+ "lib/twroute/requester/delayed.rb",
35
+ "lib/twroute/requester/delayed/create_delayed_jobs.rb",
36
+ "lib/twroute/requester/delayed/db_connection.rb",
37
+ "lib/twroute/routes.rb",
38
+ "lib/twroute/tasks.rb",
39
+ "lib/twroute/tweet_printer.rb",
40
+ "lib/twroute/tweet_route.rb",
41
+ "lib/twroute/tweeter.rb",
42
+ "test/config_test.rb",
43
+ "test/dispatcher_test.rb",
44
+ "test/fixtures/config.yml",
45
+ "test/reqex_parser_test.rb",
46
+ "test/routes_test.rb",
47
+ "test/tags_parser_test.rb",
48
+ "test/test_helper.rb",
49
+ "test/test_suite.rb",
50
+ "test/tweet_maker.rb",
51
+ "test/tweet_maker_test.rb",
52
+ "test/tweet_route_test.rb",
53
+ "test/tweeter_test.rb",
54
+ "test/users_parser_test.rb",
55
+ "twroute.gemspec"
56
+ ]
57
+ s.has_rdoc = true
58
+ s.homepage = %q{http://github.com/bhauman/twroute}
59
+ s.rdoc_options = ["--charset=UTF-8"]
60
+ s.require_paths = ["lib"]
61
+ s.rubygems_version = %q{1.3.1}
62
+ s.summary = %q{Twroute listens for Twitter updates and redirects them to HTTP post requests}
63
+ s.test_files = [
64
+ "test/config_test.rb",
65
+ "test/dispatcher_test.rb",
66
+ "test/reqex_parser_test.rb",
67
+ "test/routes_test.rb",
68
+ "test/tags_parser_test.rb",
69
+ "test/test_helper.rb",
70
+ "test/test_suite.rb",
71
+ "test/tweet_maker.rb",
72
+ "test/tweet_maker_test.rb",
73
+ "test/tweet_route_test.rb",
74
+ "test/tweeter_test.rb",
75
+ "test/users_parser_test.rb"
76
+ ]
77
+
78
+ if s.respond_to? :specification_version then
79
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
80
+ s.specification_version = 2
81
+
82
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
83
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.1.0"])
84
+ s.add_runtime_dependency(%q<tobi-delayed_job>, [">= 1.7.0"])
85
+ s.add_runtime_dependency(%q<brianmario-yajl-ruby>, [">= 0.5.12"])
86
+ else
87
+ s.add_dependency(%q<activerecord>, [">= 2.1.0"])
88
+ s.add_dependency(%q<tobi-delayed_job>, [">= 1.7.0"])
89
+ s.add_dependency(%q<brianmario-yajl-ruby>, [">= 0.5.12"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<activerecord>, [">= 2.1.0"])
93
+ s.add_dependency(%q<tobi-delayed_job>, [">= 1.7.0"])
94
+ s.add_dependency(%q<brianmario-yajl-ruby>, [">= 0.5.12"])
95
+ end
96
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bhauman-twroute
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - bhauman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: tobi-delayed_job
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: brianmario-yajl-ruby
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.5.12
44
+ version:
45
+ description: Twroute listens for Twitter updates and redirects them to HTTP post requests
46
+ email: bhauman@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README.rdoc
53
+ files:
54
+ - .document
55
+ - .gitignore
56
+ - README.rdoc
57
+ - Rakefile
58
+ - VERSION
59
+ - example_app/Rakefile
60
+ - example_app/config/config.yml.sample
61
+ - example_app/config/twroutes.rb
62
+ - example_app/scripts/twrouter.rb
63
+ - lib/twroute.rb
64
+ - lib/twroute/application.rb
65
+ - lib/twroute/application/config.rb
66
+ - lib/twroute/application/twrouter.rb
67
+ - lib/twroute/dispatcher.rb
68
+ - lib/twroute/parser/regex.rb
69
+ - lib/twroute/parser/tags.rb
70
+ - lib/twroute/parser/users.rb
71
+ - lib/twroute/requester/basic.rb
72
+ - lib/twroute/requester/delayed.rb
73
+ - lib/twroute/requester/delayed/create_delayed_jobs.rb
74
+ - lib/twroute/requester/delayed/db_connection.rb
75
+ - lib/twroute/routes.rb
76
+ - lib/twroute/tasks.rb
77
+ - lib/twroute/tweet_printer.rb
78
+ - lib/twroute/tweet_route.rb
79
+ - lib/twroute/tweeter.rb
80
+ - test/config_test.rb
81
+ - test/dispatcher_test.rb
82
+ - test/fixtures/config.yml
83
+ - test/reqex_parser_test.rb
84
+ - test/routes_test.rb
85
+ - test/tags_parser_test.rb
86
+ - test/test_helper.rb
87
+ - test/test_suite.rb
88
+ - test/tweet_maker.rb
89
+ - test/tweet_maker_test.rb
90
+ - test/tweet_route_test.rb
91
+ - test/tweeter_test.rb
92
+ - test/users_parser_test.rb
93
+ - twroute.gemspec
94
+ has_rdoc: true
95
+ homepage: http://github.com/bhauman/twroute
96
+ licenses:
97
+ post_install_message:
98
+ rdoc_options:
99
+ - --charset=UTF-8
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "0"
107
+ version:
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ version:
114
+ requirements: []
115
+
116
+ rubyforge_project:
117
+ rubygems_version: 1.3.5
118
+ signing_key:
119
+ specification_version: 2
120
+ summary: Twroute listens for Twitter updates and redirects them to HTTP post requests
121
+ test_files:
122
+ - test/config_test.rb
123
+ - test/dispatcher_test.rb
124
+ - test/reqex_parser_test.rb
125
+ - test/routes_test.rb
126
+ - test/tags_parser_test.rb
127
+ - test/test_helper.rb
128
+ - test/test_suite.rb
129
+ - test/tweet_maker.rb
130
+ - test/tweet_maker_test.rb
131
+ - test/tweet_route_test.rb
132
+ - test/tweeter_test.rb
133
+ - test/users_parser_test.rb