Permalink
Browse files

add html and json views

  • Loading branch information...
1 parent 9c25ba2 commit 677c9aa94eff6e508d7fd65e495b433af8dfb005 Sven Fuchs committed Mar 25, 2010
View
@@ -1,4 +1,5 @@
sinatra
+rack-respond_to
httparty
mbbx6spp-twitter4r --source gems.github.com
twibot
View
@@ -11,10 +11,21 @@
Dir[File.expand_path('../app/**/*', __FILE__)].each { |file| require file }
configure :production do
+ # set :public, File.dirname('../static', __FILE__)
end
helpers do
include Sinatra::Authorization
+ include Identity::Helpers
+end
+
+get '/' do
+ identities = Identity.all
+
+ respond_to do |format|
+ format.html { erb :identities, :locals => { :identities => identities } }
+ format.json { identities.to_json }
+ end
end
get '/ping' do
@@ -23,5 +34,5 @@
:login => ENV['twitter_login'],
:password => ENV['twitter_password'],
:process => Identity::Message.max_message_id || 10420650959
- }).run! && "ok"
+ }).run!
end
View
@@ -0,0 +1,17 @@
+require 'rack/respond_to'
+
+class Sinatra::Application
+ include Rack::RespondTo
+
+ def respond_to
+ env['HTTP_ACCEPT'] ||= 'text/html'
+ Rack::RespondTo.env = env
+
+ super { |format|
+ yield(format).tap do |response|
+ type = Rack::RespondTo::Helpers.match(Rack::RespondTo.media_types, format).first
+ content_type(type) if type
+ end
+ }
+ end
+end
View
@@ -5,6 +5,7 @@
class Identity
autoload :Command, 'identity/command'
+ autoload :Helpers, 'identity/helpers'
autoload :Message, 'identity/message'
autoload :Listener, 'identity/listener'
autoload :Poller, 'identity/poller'
@@ -36,16 +37,39 @@ def initialize(profiles = {})
Sources.each_name do |name|
define_method(name) { profiles[name] || {} }
- define_method(:"#{name}=") { |profile| profiles[name] = profile }
+ define_method(:"#{name}=") { |profile| profile[name] = profile }
+ end
+
+ def handles
+ @handles ||= profiles.map { |name, profile| profile['handle'] }.compact.uniq
+ end
+
+ def avatar
+ @avatar ||= begin
+ profiles['twitter'] && "http://headhunter.heroku.com/#{profiles['twitter']['handle']}" ||
+ profiles.map { |name, profile| profile['avatar'] }.compact.first
+ end
+ end
+
+ def url
+ @url ||= profiles.map { |name, profile| profile['url'] }.compact.first
+ end
+
+ def name
+ @name ||= profiles.map { |name, profile| profile['name'] }.compact.first
end
def claim
- profiles.each { |name, profile| p profile.class; profile['claimed_at'] = Time.now unless profile['claimed_at'] }
+ profiles.each { |name, profile| profile['claimed_at'] = Time.now unless profile['claimed_at'] }
end
def set_profile(name, profile)
claimed_at = send(name)['claimed_at']
profile.merge!('claimed_at' => claimed_at) if claimed_at
profiles[name] = profile
end
+
+ def to_json(*args)
+ { :handles => handles, :profiles => profiles, :groups => groups, :created_at => created_at }.to_json
+ end
end
@@ -17,6 +17,7 @@ def join
def update
Identity::Sources.update_all(sender, args)
sender.claim
+ sender.save
end
def queue
@@ -0,0 +1,28 @@
+module Identity::Helpers
+ def identity_link_or_name(identity)
+ identity.url ?
+ %(<a class="name" href="#{identity.url}">#{identity.name}</a>) :
+ %(<span class="name">#{identity.name}</span>)
+ end
+
+ def identity_links(identity)
+ links = [identity_link(identity)] + profile_links(identity)
+ links.compact
+ end
+
+ def identity_link(identity)
+ %(<a class="name" href="#{identity.url}">#{identity.url}</a>) if identity.url
+ end
+
+ def profile_links(identity)
+ identity.profiles.map { |name, profile| profile_link(name, profile_url(name, profile)) }.compact
+ end
+
+ def profile_link(name, url)
+ %(<a href="#{url}" class="profile #{name}">#{url}</a>) if url
+ end
+
+ def profile_url(name, profile)
+ Identity::Sources[name].profile_url(profile)
+ end
+end
@@ -12,14 +12,22 @@ def all
@sources ||= { 'me' => Me.new, 'twitter' => Twitter.new, 'github' => Github.new }
end
+ def [](name)
+ all[name]
+ end
+
def update_all(identity, args)
each { |name, source| source.update(identity, args[name]) if args.key?(name) }
end
-
+
+ def map(&block) # use Numerable
+ all.map(&block)
+ end
+
def each(&block)
all.each(&block)
end
-
+
def each_name(&block)
all.keys.each(&block)
end
@@ -5,7 +5,10 @@ module Identity::Sources
class Base
class << self
def get(url)
- HTTParty.get(url)
+ data = HTTParty.get(url)
+ data = JSON.parse(data) rescue {} if data.is_a?(String) # TODO somehow communicate parsing errors
+ data['source_url'] = url
+ data
end
end
@@ -16,14 +19,11 @@ def name
end
def update(identity, handle)
- identity.set_profile(name, fetch(url(handle))) if handle
+ identity.set_profile(name, fetch(source_url(handle))) if handle
end
def fetch(url)
data = Base.get(url)
- p data
- p data.class
- data = JSON.parse(data) if data.is_a?(String)
data = remap(data) if map
data
end
@@ -12,12 +12,16 @@ def initialize
}
end
- def url(handle)
+ def profile_url(profile)
+ "http://github.com/#{profile['handle']}"
+ end
+
+ def source_url(handle)
"http://github.com/api/v2/json/user/show/#{handle}"
end
- def fetch(url)
- remap(Base.get(url)['user'])
+ def fetch(source_url)
+ remap(Base.get(source_url)['user'])
end
end
end
@@ -7,5 +7,9 @@ def update(identity, url)
source.update(identity, identity.me[name]) if identity.me[name]
end if identity.me
end
+
+ def profile_url(profile)
+ profile['source_url']
+ end
end
end
@@ -11,7 +11,11 @@ def initialize
}
end
- def url(handle)
+ def profile_url(profile)
+ "http://twitter.com/#{profile['handle']}"
+ end
+
+ def source_url(handle)
"http://api.twitter.com/1/users/show/#{handle}.json"
end
end
View
@@ -0,0 +1,68 @@
+require File.expand_path('../test_helper', __FILE__)
+
+require 'app'
+
+set :environment, :test
+
+class AppTest < Test::Unit::TestCase
+ include Rack::Test::Methods
+
+ def setup
+ setup_stubs
+ end
+
+ test '/ping is protected through http auth' do
+ Identity::Poller::Twitter.stubs(:new).returns(Object.new.tap { |o| o.stubs(:run!) })
+ authorized_get '/ping'
+ assert_equal 200, last_response.status
+
+ unauthorized_get '/ping'
+ assert_equal 401, last_response.status
+ end
+
+ test '/ping is runs a twitter poller' do
+ poller = Identity::Poller::Twitter.new(:reply, /#update/, :update, { :login => 'login', :process => 1 })
+ Identity::Poller::Twitter.stubs(:new).returns(poller)
+ poller.twitter.expects(:timeline_for).with(:replies, :since_id => 1).returns([status('rugb_test', '3update')])
+
+ log = capture_stdout { authorized_get '/ping' }
+
+ assert_match /imposing as @login/, log
+ assert_match /Received 1 reply/, log
+ end
+
+ test '/ responding to :html' do
+ setup_stubs
+ command('update', 'rugb', 'svenfuchs', 'twitter:svenfuchs github:svenphoox').dispatch
+ get '/'
+
+ assert_equal 'text/html', last_response['Content-Type']
+ assert_match /svenfuchs/, last_response.body # TODO use some tag matcher
+ end
+
+ test '/ responding to :json' do
+ Identity.new(:twitter => { :handle => 'svenfuchs' }).save
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/json' }
+
+ assert_equal 'application/json', last_response['Content-Type']
+ assert_equal({ 'twitter' => { 'handle' => 'svenfuchs' } }, JSON.parse(last_response.body).first['profiles'])
+ end
+
+ protected
+
+ def app
+ Sinatra::Application
+ end
+
+ def authorized_get(path)
+ get path, {}, { 'HTTP_AUTHORIZATION' => encode_credentials(ENV['twitter_login'], ENV['twitter_password']) }
+ end
+
+ def unauthorized_get(path)
+ get path, {}, { 'HTTP_AUTHORIZATION' => encode_credentials('go', 'away') }
+ end
+
+ def encode_credentials(username, password)
+ "Basic " + Base64.encode64("#{username}:#{password}")
+ end
+end
@@ -5,14 +5,6 @@ def setup
setup_stubs
end
- def teardown
- Identity.all.each { |identity| identity.delete }
- end
-
- def command(command, receiver, sender, message)
- Identity::Command.new(command, receiver, sender, message)
- end
-
test '#parse_args parses a message for foo:bar arguments' do
cmd = command('update', 'rugb', 'svenfuchs', 'github:foo me:http://tinyurl.com/yc7t8bv')
assert_equal cmd.args, {'github' => 'foo', 'me' => 'http://tinyurl.com/yc7t8bv'}
View
@@ -5,31 +5,18 @@ def setup
setup_stubs
end
- def teardown
- Identity.all.each { |identity| identity.delete }
- Identity::Message.all.each { |message| message.delete }
- end
-
def update!(from, message, id = '12345')
- message = message(from, message, id)
+ status = status(from, message, id)
listener = Identity::Listener::Twitter.new('rugb_test', /#update/, :update)
- listener.dispatch(message)
- end
-
- def message(from, message, id = '12345')
- Twitter::Status.new(:id => id, :user => sender(from), :text => message)
- end
-
- def sender(name)
- Twitter::User.new(:screen_name => name)
+ listener.dispatch(status)
end
test 'updating w/ a me url and a github handle' do
update!('svenfuchs', '#update me:http://tinyurl.com/yc7t8bv github:svenphoox')
identity = Identity.find_by_handle('svenphoox')
- assert_equal 'svenphoox', identity.github['handle']
- assert_equal 'Sven', identity.github['name']
+ assert_equal 'svenphoox', identity.github['handle']
+ assert_equal 'Sven', identity.github['name']
end
test 'updating an existing profile' do
@@ -1,10 +1,6 @@
require File.expand_path('../test_helper', __FILE__)
class MessageTwitterTest < Test::Unit::TestCase
- def teardown
- # Identity::Message.all.each { |message| message.delete }
- end
-
def message(id, text = 'da message', sender = 'svenfuchs', receiver = 'rugb_test')
Identity::Message.new :message_id => id,
:text => text,
View
@@ -11,13 +11,6 @@ def tweet(from, text)
)
end
- def capture_stdout
- @stdout, $stdout = $stdout, (io = StringIO.new)
- yield
- $stdout = @stdout
- io.string
- end
-
test "Poller::Twitter polls from twitter once and handles new replies by queueing commands" do
config = { :login => 'svenfuchs', :process => 10623176300 }
poller = Identity::Poller::Twitter.new(:reply, /#update/, :update, config)
Oops, something went wrong. Retry.

0 comments on commit 677c9aa

Please sign in to comment.