Posts filed under "Ruby"

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.

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
=> ""

Simple

2 ways:

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

Overwriting "Constants"

In Ruby, constants aren't the same as in other languages since you can change them at runtime. This code:

ENDPOINT_URL = 'http://mysubdomain.unfuddle.com/api/v1/'
ENDPOINT_URL = 'http://othersubdomain.unfuddle.com/api/v1/'
puts ENDPOINT_URL

produces this output (including the warning):

warning: already initialized constant ENDPOINT_URL
http://othersubdomain.unfuddle.com/api/v1/

If you need to override a constant and don't want to trigger a warning (in a test environment, for example) you can use the replace method:

ENDPOINT_URL = 'http://mysubdomain.unfuddle.com/api/v1/'
ENDPOINT_URL.replace 'http://othersubdomain.unfuddle.com/api/v1/'
puts ENDPOINT_URL

This will only work with those objects that have a replace method defined (e.g. Array, Hash, and String) and the new value must have the same datatype.

Fun With Association Proxies

Not only are tags great, they're a requirement of Web 2.0 (read the handbook). Here's a quick way to pull out those tags in a meaningful way:

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, :through => :taggings do
    def to_s
      self.map(&:name).join(', ')
    end
  end
end

Now, calling @post.tags.to_s will return the list of tag names separated by commas. The real magic is when you do this:

puts "#{@post.tags}"