Double Shot #632 »

Created at: 22.01.2010 15:23, source: A Fresh Cup, tagged: Double Shot actionmailer db2 osx PostgreSQL rails smtp twitter

12 is really too many billable hours for one day.


more »

Twizzle Your Deplizzles »

Created at: 22.12.2009 04:30, source: RailsTips - Home, tagged: twitter capistrano

Steve and I have a Twitter account that we send all our commits to. It is all handled by Github and both of us find it really handy. Rain or shine, we get commit updates on our phone which is great for staying in the loop.

For a little while now, I’ve been wanting to add deploy notices to this twitter account and tonight I finally got around to it. It was pretty easy, but I’ll post the code here to save others time. I went the no dependencies route (even though I created the Twitter gem).

set :twitter_username, 'username'
set :twitter_password, 'password'

namespace :twitter do
  task :update do
    require 'open-uri'
    require 'net/http'
    
    url = URI.parse('http://twitter.com/statuses/update.xml')
    request = Net::HTTP::Post.new(url.path)
    request.basic_auth twitter_username, twitter_password
    request.set_form_data('status' => "Deployed #{current_revision[0..6]} to #{rails_env}")
    
    begin
      response = Net::HTTP.new(url.host, url.port).start do |http|
        http.open_timeout = 10
        http.request(request)
      end
      puts 'Deploy notice sent to twitter'
    rescue Timeout::Error => e
      puts "Timeout after 10s: Seems like Twitter is down."
      puts "Use \"cap twitter:update\" to update Twitter status later w/o deploying"
    end
  end
end

after "deploy", "twitter:update"

Just drop this in your deploy file and update the username and password. Notice that I even included the git revision and rails environment so you can easily see what was last deployed and where to. You end up with a nice little message like this:

Deployed 09ea23f to staging

Very handy!


more »

Twizzle Your Deplizzles »

Created at: 21.12.2009 04:30, source: RailsTips - Home, tagged: capistrano twitter

In which I show how to easily update Twitter with deploy notices that include the environment and revision.

Steve and I have a Twitter account that we send all our commits to. It is all handled by Github and both of us find it really handy. Rain or shine, we get commit updates on our phone which is great for staying in the loop.

For a little while now, I’ve been wanting to add deploy notices to this twitter account and tonight I finally got around to it. It was pretty easy, but I’ll post the code here to save others time. I went the no dependencies route (even though I created the Twitter gem).

set :twitter_username, 'username'
set :twitter_password, 'password'

namespace :twitter do
  task :update do
    require 'open-uri'
    require 'net/http'

    url = URI.parse('http://twitter.com/statuses/update.xml')
    request = Net::HTTP::Post.new(url.path)
    request.basic_auth twitter_username, twitter_password
    request.set_form_data('status' => "Deployed #{current_revision[0..6]} to #{rails_env}")

    begin
      response = Net::HTTP.new(url.host, url.port).start do |http|
        http.open_timeout = 10
        http.request(request)
      end
      puts 'Deploy notice sent to twitter'
    rescue Timeout::Error => e
      puts "Timeout after 10s: Seems like Twitter is down." 
      puts "Use \"cap twitter:update\" to update Twitter status later w/o deploying" 
    end
  end
end

after "deploy", "twitter:update"

Just drop this in your deploy file and update the username and password. Notice that I even included the git revision and rails environment so you can easily see what was last deployed and where to. You end up with a nice little message like this:

Deployed 09ea23f to staging

Very handy!


more »

Twizzle Your Deplizzles »

Created at: 21.12.2009 04:30, source: RailsTips - Home, tagged: twitter capistrano

Steve and I have a Twitter account that we send all our commits to. It is all handled by Github and both of us find it really handy. Rain or shine, we get commit updates on our phone which is great for staying in the loop.

For a little while now, I’ve been wanting to add deploy notices to this twitter account and tonight I finally got around to it. It was pretty easy, but I’ll post the code here to save others time. I went the no dependencies route (even though I created the Twitter gem).

set :twitter_username, 'username'
set :twitter_password, 'password'

namespace :twitter do
  task :update do
    require 'open-uri'
    require 'net/http'
    
    url = URI.parse('http://twitter.com/statuses/update.xml')
    request = Net::HTTP::Post.new(url.path)
    request.basic_auth twitter_username, twitter_password
    request.set_form_data('status' => "Deployed #{current_revision[0..6]} to #{rails_env}")
    
    begin
      response = Net::HTTP.new(url.host, url.port).start do |http|
        http.open_timeout = 10
        http.request(request)
      end
      puts 'Deploy notice sent to twitter'
    rescue Timeout::Error => e
      puts "Timeout after 10s: Seems like Twitter is down."
      puts "Use \"cap twitter:update\" to update Twitter status later w/o deploying"
    end
  end
end

after "deploy", "twitter:update"

Just drop this in your deploy file and update the username and password. Notice that I even included the git revision and rails environment so you can easily see what was last deployed and where to. You end up with a nice little message like this:

Deployed 09ea23f to staging

Very handy!


more »

Swine Flu and the Twitter Gem »

Created at: 19.05.2009 01:11, source: RailsTips - Home, tagged: gems httparty twitter

In which I wax poetic about the trendy new addition to the Twitter gem.

I had some extra time today and I’ve been spotty on open source work over the past few weeks, so I decided to add support for the Twitter trends API to my Twitter gem.

Using HTTParty, the code for this turned out to be insanely simple, so short, in fact, that I’ll just put it inline here so you don’t even have to go over to Github. Aww, I’m so nice.

module Twitter
  class Trends
    include HTTParty
    base_uri 'search.twitter.com/trends'
    format :json

    # :exclude => 'hashtags' to exclude hashtags
    def self.current(options={})
      mashup(get('/current.json', :query => options))
    end

    # :exclude => 'hashtags' to exclude hashtags
    # :date => yyyy-mm-dd for specific date
    def self.daily(options={})
      mashup(get('/daily.json', :query => options))
    end

    # :exclude => 'hashtags' to exclude hashtags
    # :date => yyyy-mm-dd for specific date
    def self.weekly(options={})
      mashup(get('/weekly.json', :query => options))
    end

    private
      def self.mashup(response)
        response['trends'].values.flatten.map { |t| Mash.new(t) }
      end
  end
end

Pure TDD

I am most definitely a tester, but I’ll admit I usually write code and then write the test. Of late, I’ve been reversing this trend and actually practicing TDD in full force by writing a small test, then only enough code to make it pass, followed by another test or more code for the existing test, finished with just enough code to make the new addition pass.

It is a different mindset to code in this way, compared to my code first and then make sure my butt is covered method and I’ve loving it. I thought I would find pure TDD tedious, but on the contrary, I think I’m coding faster and cleaner.

The Tests

So how did I test the code above? Again, inline for your viewing pleasure, are the tests I added to make sure I don’t break something in the future and get yelled at. Feel free to take a gander and I’ll meet back up with you at the bottom of it.

require File.dirname(__FILE__) + '/../test_helper'

class TrendsTest < Test::Unit::TestCase
  include Twitter

  context "Getting current trends" do
    should "work" do
      stub_get('http://search.twitter.com:80/trends/current.json', 'trends_current.json')
      trends = Trends.current
      trends.size.should == 10
      trends[0].name.should == '#musicmonday'
      trends[0].query.should == '#musicmonday'
      trends[1].name.should == '#newdivide'
      trends[1].query.should == '#newdivide'
    end

    should "be able to exclude hashtags" do
      stub_get('http://search.twitter.com:80/trends/current.json?exclude=hashtags', 'trends_current_exclude.json')
      trends = Trends.current(:exclude => 'hashtags')
      trends.size.should == 10
      trends[0].name.should == 'New Divide'
      trends[0].query.should == %Q(\"New Divide\")
      trends[1].name.should == 'Star Trek'
      trends[1].query.should == %Q(\"Star Trek\")
    end
  end

  context "Getting daily trends" do
    should "work" do
      stub_get('http://search.twitter.com:80/trends/daily.json?', 'trends_daily.json')
      trends = Trends.daily
      trends.size.should == 480
      trends[0].name.should == '#3turnoffwords'
      trends[0].query.should == '#3turnoffwords'
    end

    should "be able to exclude hastags" do
      stub_get('http://search.twitter.com:80/trends/daily.json?exclude=hashtags', 'trends_daily_exclude.json')
      trends = Trends.daily(:exclude => 'hashtags')
      trends.size.should == 480
      trends[0].name.should == 'Star Trek'
      trends[0].query.should == %Q(\"Star Trek\")
    end

    should "be able to get for specific date (with date string)" do
      stub_get 'http://search.twitter.com:80/trends/daily.json?date=2009-05-01', 'trends_daily_date.json'
      trends = Trends.daily(:date => '2009-05-01')
      trends.size.should == 440
      trends[0].name.should == 'Swine Flu'
      trends[0].query.should == %Q(\"Swine Flu\")
    end

    should "be able to get for specific date (with date object)" do
      stub_get 'http://search.twitter.com:80/trends/daily.json?date=2009-05-01', 'trends_daily_date.json'
      trends = Trends.daily(:date => Date.new(2009, 5, 1))
      trends.size.should == 440
      trends[0].name.should == 'Swine Flu'
      trends[0].query.should == %Q(\"Swine Flu\")
    end
  end

  context "Getting weekly trends" do
    should "work" do
      stub_get('http://search.twitter.com:80/trends/weekly.json?', 'trends_weekly.json')
      trends = Trends.weekly
      trends.size.should == 210
      trends[0].name.should == 'Happy Mothers Day'
      trends[0].query.should == %Q(\"Happy Mothers Day\" OR \"Mothers Day\")
    end

    should "be able to exclude hastags" do
      stub_get('http://search.twitter.com:80/trends/weekly.json?exclude=hashtags', 'trends_weekly_exclude.json')
      trends = Trends.weekly(:exclude => 'hashtags')
      trends.size.should == 210
      trends[0].name.should == 'Happy Mothers Day'
      trends[0].query.should == %Q(\"Happy Mothers Day\" OR \"Mothers Day\")
    end

    should "be able to get for specific date (with date string)" do
      stub_get 'http://search.twitter.com:80/trends/weekly.json?date=2009-05-01', 'trends_weekly_date.json'
      trends = Trends.weekly(:date => '2009-05-01')
      trends.size.should == 210
      trends[0].name.should == 'TGIF'
      trends[0].query.should == 'TGIF'
    end

    should "be able to get for specific date (with date object)" do
      stub_get 'http://search.twitter.com:80/trends/weekly.json?date=2009-05-01', 'trends_weekly_date.json'
      trends = Trends.weekly(:date => Date.new(2009, 5, 1))
      trends.size.should == 210
      trends[0].name.should == 'TGIF'
      trends[0].query.should == 'TGIF'
    end
  end
end

So, yeah, nothing earth shattering. It feels a bit repetitive, but I don’t mind some amount of repetition in my tests. The fixture files were created quite simply using curl.

cd test/fixtures
curl http://search.twitter.com:80/trends/weekly.json?date=2009-05-01 > trends_weekly_date.json
# rinse and repeat for each file

The stub_get method is a simple wrapper around FakeWeb and looks something like this:

def stub_get(url, filename, status=nil)
  options = {:string => fixture_file(filename)}
  options.merge!({:status => status}) unless status.nil?
  FakeWeb.register_uri(:get, url, options)
end

def fixture_file(filename)
  file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/' + filename)
  File.read(file_path)
end

I’m lazy and find that stub_get is much shorter than FakeWeb.register_uri blah, blah, blah. The tests use FakeWeb, shoulda and my fork of matchy, in case you are curious.

Example Uses

So what can you do with the new trends addition? Below are some examples of how you can obtain trend information.

Twitter::Trends.current
Twitter::Trends.current(:exclude => 'hashtags')

Twitter::Trends.daily # current day
Twitter::Trends.daily(:exclude => 'hashtags')
Twitter::Trends.daily(:date => Date.new(2009, 5, 1))

Twitter::Trends.weekly # current day
Twitter::Trends.weekly(:exclude => 'hashtags')
Twitter::Trends.weekly(:date => Date.new(2009, 5, 1))

That’s all for now. Enjoy the new trends and build something cool. Oh, and if you want to play with trends, but don’t have an idea, I have one and most likely won’t have time to build it. I’d be happy to collaborate.


more »