Fun With Flickr Contacts

One of the fun things about the API I created with the Fleakr gem is that many of the chained associations available were provided "for free." For example, you can find the contacts for a user:

Fleakr.api_key = 'sekrit'

user = Fleakr.user('teamviget')

puts user.contacts.map(&:username)
  # =>  ["benscofield", "Brian Landau", "Brian Williams", "carolynhack", ...
  #       "ryanmoede", "Samanthatoy", "stephay22", "The Mindinator", "whafro"]

puts user.contacts.length          # => 21
puts user.contacts.first.name      # => "Ben Scofield"
puts user.contacts.last.name       # => "M. Jackson Wilkinson"
puts user.contacts.last.location   # => "Washington, DC, USA"

Not really that earth-shattering. But since a "contact" is really an instance of the User class, I can chain the calls to do some strange and useless things:

puts user.contacts.last.contacts.length # => 70
puts user.contacts.last.contacts.first.sets.last.photos.first.url
  # => "http://www.flickr.com/photos/aarongustafson/55410766/"    

There are other attributes available for your contacts as well:

# Attributes available from the API
puts user.class.attributes.map(&:name)
  # => [:id, :username, :name, :location, :photos_url, :profile_url,
  #     :photos_count, :icon_server, :icon_farm, :pro, :admin]

# Additional attributes
puts user.pro?      # => true
puts user.admin?    # => false
puts user.icon_url
  # => "http://farm2.static.flickr.com/1018/buddyicons/11166619@N03.jpg"

And associations:

puts user.photos.map(&:title)[0..1]
  # => ["VigeTurf Loves Boston!", "Turf in Central Cali"]

puts user.groups.map(&:name)[0..1]
  # => ["Refresh DC", "Happy at work"]

puts user.sets.map(&:title)[0..1]
  # => ["Viget South Holiday Dinner 2008", "VigeTurf Shots"]

Use your powers for good, and beware the random strange images.

Fleakr: A Small, Yet Powerful, Flickr Gem

It's been a while since I've posted here, but I've been spending all of my "free" time working on what has become a labor of love (or masochism) for me. What started out as a simple way to sync Flickr photos with my iPod turned out to be a much more full-fledged implementation of the Flickr REST API in Ruby.

Why?

Originally, I was going to use the recently-forked flickr gem as the basis for the download functionality I needed, but I found that:

  • Chaining method calls would lose my API key
  • I didn't like the API that the gem exposed
  • The underlying code wasn't something that I really wanted to touch

While this works for pulling back a user's contacts:

class User
  def contacts
    @client.contacts_getPublicList('user_id'=>@id)['contacts']['contact'].collect { |contact| 
      User.new(contact['nsid'], contact['username'], nil, nil, @api_key) 
    }
    #or
  end
end

What I really wanted was something like this:

class User
  has_many :contacts
end

What Can It Do?

The gem implements all the read-only API calls that I care about at the moment, as well as basic uploading. It also adds some nice features (like quick saving of photos or sets) while exposing a clean, Ruby-like API. Here are some basics:

require 'rubygems'
require 'fleakr'

Fleakr.api_key = 'gobbledygook'

# Find a user
user = Fleakr.user('teamviget')

# Grab a set from the list
set = user.sets[4]

puts set.title        # => "Rails Rumble 2008"
puts set.description  # => "Multiple Viget teams (and friends) ..."

# Inspect a photo
photo = set.photos.first

puts photo.title       # => "Fast and Furious"
puts photo.description # => "Off to a good start on day #1"

# Save an individual image to disk
photo.large.save_to('/tmp/rumble_day_one.jpg')

# Save an entire set (large photos) to a specific directory
set.save_to('/tmp', :large)

# Search photos
rumble_photo = Fleakr.search("rails rumble '08").first

puts rumble_photo.title       # => "rumbling"
puts rumble_photo.description # => "Planificando con Luismi ..."

# Scoped search
team = user.search('team').first

puts team.url       # => "http://www.flickr.com/photos/viget/2987133534/"
puts team.large.url # => http://farm4.static.flickr.com/3040/2987133534_c54b1def4c_b.jpg

# Uploading is supported if you have an auth_token (see the RDoc)

# Upload a single file
Fleakr.upload('/path/to/my.jpg')

# Upload a bunch of files
Fleakr.upload('/path/to/my/photos/*.jpg')

For more detailed documentation, check out the full RDoc on RubyForge or the README file on GitHub.

How Do I Get It?

RubyForge is the place to go for the latest, most stable version. A simple gem installation will work:

  $ sudo gem install fleakr

If you want the bleeding edge, install from GitHub:

  $ sudo gem install reagent-fleakr --source=http://gems.github.com

What's Next?

Check out the "Roadmap / TODO" section in the RDoc for what's to come. I'll also be showing off some cool ways to use the Flickr API in future blog posts.

TextMate ... WTF?

Like any good Ruby developer working on a fresh bit of code, you eagerly start setting up to run your first test and get hit with this in TextMate:

  `blank_slate_method_added': stack level too deep (SystemStackError)

Ouch. Screeching halt. The root cause for this comes from a conflict between TextMate's version of Builder and the system-installed version. There are a couple solutions floating out there - either remove the TextMate-bundled version entirely or drop this in your test file:

  $:.reject! { |e| e.include? 'TextMate' }

I prefer this method as it doesn't have any noticeable side effects anywhere I've used it. This appears to be the official solution to date.

Rails Rumble '08: Redux

It's been a few weeks since Rob, Nic, Doug, and I competed in this year's RailsRumble - after the dust settled from the 48-hour competition we had created an app (Qflip) that went on to take 2nd place. From what I can remember, it was both fun and stressful - check out the Flickr photos for proof.

Doug shared his take from the event on the Viget Inspire blog and described a technique that I think shows a lot of promise for design and development teams working together:

Do you know what all the pages do? How they're structured, and what they communicate? Great, now build them. Using just markup and some gray backgrounds, you can make a working version of your site in no time flat.

I documented my experience over at Extend and reinforced the importance of "shipping":

...Our codes were far from perfect, but we realized that the real value was in getting a product out there that could be used in the way it was intended by a real audience. As a result we now have real feedback and focused ideas on how to make the application even better.

Since the event, we've increasingly seen continued usage and coverage of the application:

QFlip - Get strangers to influence your Netflix queue. Sounds scary, but you might end up with some interesting picks! Developed by the wonderfully named "Scatapult" team.

Rails Inside

Qflip is an interesting service that uses Netflix’s open API to help subscribers make movie choices. What I like about this service:

  • It’s quick and easy
  • It provides value to my Netflix membership
  • It’s fun in that it has an element of surprise and randomness
  • It’s based on people, not algorithms

Tom Willerer

If you are a Netflix user with friends' of the same movie-renting persuasion, and you have an evil sense of humor, this is your app.

NetworkWorld

QFlip is a Netflix hack that enables other people to mess with your queue. I'm not brave enough to try it, but if you do let us know what you think in the comments.

Hacking Netflix.com

We plan to continue working on the app and reacting to feedback that we get from our users. As part of that work, we will be extracting the Netflix-specific functionality into a publicly-available Gem. Stay tuned.

Gems. Simple.

I've written a few Ruby Gems in my time, it's true. In my experience it's always been a bit of a painful process - I could never come to grips with all the dependencies that hoe introduced and I always ended up reconfiguring things after running newgem --simple. So... I decided to write my own little gem that generates, well, gems.

Getting It

This should be all you need:

$ sudo gem install reagent-simple-gem --source http://gems.github.com

If that doesn't work, take a look at the README out on GitHub

Using It

Ready? Go.

$ simple-gem my-gem

Now you have a skeleton:

my-gem/
|-- README.markdown
|-- Rakefile
|-- lib
|   |-- my_gem
|   |   `-- version.rb
|   `-- my_gem.rb
`-- test
    `-- my_gem_test.rb

Write your codes, test them, and release:

$ cd my-gem
$ rake github
$ git add && git commit -m "Perfection."
$ git push origin master

There's more to it, but that's the basic idea. Check out the docs for more information and send me your feedback

Salt on a Slug

Here's a quick way to generate slugs to use as part of a URL for things like posts, categories, etc...

class String
  def sluggify
    slug = self.downcase.gsub(/[^0-9a-z_ -]/i, '')
    slug = slug.gsub(/\s+/, '-')
    slug
  end
end

class NilClass
  def sluggify
    ''
  end
end

Here's what it looks like in action:

$ irb
>> 'This is a post'.sluggify
=> "this-is-a-post"
>> nil.sluggify
=> ""

Developing a Product Sometimes Means Removing Features

As part of my daily job, we're constantly balancing business value with level of effort when planning which features to include in an upcoming iteration. After reviewing an iteration's work with a client, follow-on conversations usually revolve around adding new features to the product. I was glad to see that Digg sometimes has a different approach:

In the next week or so, we’ll be closing down the podcasts section and folding it into the video section of Digg. We’ll also be retiring the old Digg Spy. Both of these features have become outmoded as Digg has grown and as a result they have a very small number of users (under 1,000) each.

I think that the key to this sort of activity is having a clear reason to remove a feature. In Digg's case, they were keeping an eye on their metrics and were able to justify the removal of both features based on quantifiable evidence.

In the early stages of development, it's not always an option to remove a feature. The best way to make this happen is to launch early and often and let your users decide which features are valuable and which ones aren't.

Simple

2 ways:

$ alias irb='irb --prompt simple'
$ echo "IRB.conf[:PROMPT_MODE] = :SIMPLE" >> ~/.irbrc

Tracking Changes in Git

I've always heard people refer to creating 'tracking branches' but never really knew how to set one up. I think I've figured it out - here's my current workflow:

  $ git clone my_git_repo
  $ git remote add other_source other_git_repo
  $ git fetch other_source
  $ git branch --track other_source_master other_source/master

Once I have that set up, I can integrate others' changes:

  $ git checkout master
  $ git checkout -b merge_area
  $ git merge other_source_master

Any time I want to pull upstream changes from the other remote source:

  $ git checkout other_source_master
  $ git pull

Unit Testing Your ApplicationController

Do it!

class ApplicationControllerTest < ActiveSupport::TestCase
  context "An instance of ApplicationController" do

    setup { @controller = ApplicationController.new }

    should "return nil when there is no :user_id in session" do
      @controller.stubs(:session).returns({})
      assert_nil @controller.current_user
    end

    should "retrieve the current user when there is a :user_id in session" do
      user = Factory(:user)
      @controller.stubs(:session).returns({:user_id => user.id})

      assert_equal user, @controller.current_user
    end

    should "return nil when the :user_id in session can't be found in the database" do
      @controller.stubs(:session).returns(:user_id => 1)

      assert_nil @controller.current_user
    end

  end
end