Posts filed under "Ruby"

Using Helper Methods in Your Model? Yes.

At one point I found myself wanting to use Rails' auto_link helper in a to_html method on one of my models. If you need this functionality, here's how you would do it:

class Formatter

  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::TextHelper

  def initialize(content)
    @content = content
  end

  def to_html
    auto_link(@content)
  end

end

I had to include TagHelper to satisfy dependencies for the h() and content_tag() methods. Though I ended up writing a custom formatter class instead in this case, this is still a useful approach.

Passing Parameters to Rake Tasks

I recently discovered how to correctly pass parameters to Rake tasks from the command-line instead of the old, busted way of passing in environment variables. The post is up on the Viget Extend blog, but here's the short of it.

If you have a task that you want to parameterize:

task :echo do
  puts "Hello"
end

You can specify the parameters that it accepts and receive them as a hash inside of the task:

task :echo, :message do |t, args|
  message = args[:message] || 'Hello'
  puts message
end

It also honors task dependencies (using a different syntax):

task :echo, :message, :needs => :environment do |t, args|
  ...
end

Check out the post for the full details.

Coding with Confidence: The Talk

Last weekend I gave a talk on Test-Driven Development at the Fall 2009 CMAP Code Camp. Since this was a .NET event (and I'm a Ruby guy), I took a look at some previous talks I had given on the topic to focus on why someone might want to use a Test-Driven approach when developing software. It didn't hurt that there had been a lot of debate recently on the value of both unit testing and a test-first software development approach. The first part of my presentation focused on the benefits:

For the second portion of my talk, I did about 45 minutes of live coding (in Ruby) to demonstrate the process I go through when using tests to drive the code I write. During that time, we built a small working wrapper to the TwitterCounter API from the ground up. There was some good discussion afterwards about removing the remaining duplication and how best to evolve the design to better model the interaction with the API.

It was good to get in front of a different audience to talk about the value of testing as a design tool. Thanks to Chris Steen for the invitation and the rest of the CMAP group for organizing a great conference.

Autotest and Me

Today, Mark and I were talking about some of the open source projects we've been working on. During our conversation, he mentioned how David's recent post about using Autotest on OSX was the catalyst for him to start using it on his projects. I decided that I would do the same, so I went about using it on a Ruby gem that I'm developing.

From the original post, using it on Rails is pretty straightforward, but getting it to work on a vanilla Ruby project requires a bit of tweaking. There's some outdated information out there, but I was able to find a solution that worked for me. My resulting .autotest file:

require 'autotest/growl'
require 'autotest/fsevent'

Autotest.add_hook :initialize do |at|
  at.clear_mappings

  at.add_mapping %r%/^lib/(.*).rb$% do |_, m|
    possible = File.basename(m[1])
    files_matching %r%^test/.*(#{possible}_test|test_#{possible}).rb$%
  end

  at.add_mapping(%r%^test/.*.rb$%) {|filename, _| filename }
end

With that patch in place, I get the results I want.

Access The Etsy API in Ruby

Long ago, my wife found a love for Etsy. When I recently discovered their public API, I figured that it was meant to be that I begin working on a clean implementation of the Etsy API in Ruby. So I did.

I'm releasing the first version to the public tonight that includes support for retrieving users, their shops, and associated listings.

Installation

A simple gem installation is all you need:

sudo gem install etsy

If you prefer to bleed:

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

Usage

Once installed, set up your API key and you're ready to go:

require 'rubygems'
require 'etsy'

Etsy.api_key = 'snipsnap'

At the time of this post, all API calls are read-only so that's all you need to do. Let's get started by finding a user's shop:

user = Etsy.user('littletjane') # => #<Etsy::User:0x1057624 ... >
user.seller?                    # => true
user.shop.name                  # => "littletjane"

A shop can have multiple listings:

user.shop.listings  # => [#<Etsy::Listing:0x1001788 ... >, ... ]

listing = user.shop.listings.first

listing.title       # => "hanging with the bad boys matchbox"
listing.description # => "standard size matchbox, approx. 1.5 x 2 ... "

Each listing, in turn, can have one or more images:

image = listing.images.first

image.small_square # => "http://ny-image2.etsy.com/il_25x25.67765346.jpg"
image.large        # => "http://ny-image2.etsy.com/il_430xN.67765346.jpg"

For more information and usage examples, check out the documentation on Rubyforge. If you have feature requests or comments, open an issue on Github or send me an email.

Grab Flickr Tags and Comments With Fleakr

I just pushed out version 0.4.3 of the Fleakr gem that provides support for retrieving tags and comments for users, photos, and photosets. These are non-authenticated methods, so all you need is your API key:

Fleakr.api_key = 'your_api_key'

Once that's set, you can retrieve tags for users and photos:

user = Fleakr.user('teamviget')
user.tags.first.value
user.sets.first.photos.first.tags.first.value

Given a single tag, you can also retrieve a list of related tags:

user.tags.first.related.first

Tags associated with a photo have more information associated to them - see the documentation for what's available. Comments have no such limitation and are available for both sets and individual photos:

user = Fleakr.user('the decapitator')
user.sets.first.comments
user.photos.first.comments

Give it a try for yourself - check out the docs or send me an email if you get stuck. Enjoy

Am I Ruby 1.9?

Kevin and I were chatting about how to tell if your locally installed gems were compatible with Ruby 1.9. He was looking for a tool that would scan the list and figure out which ones worked. I pointed him to Is It Ruby 1.9?, he found the API and whipped up a gem that would tell you which of your gems would likely work (or not work) with the latest version of Ruby. Check it out:

$ sudo gem install n3bulous-amiruby19 --source=http://gems.github.com

Then let it loose on your system:

$ amiruby19

Oh, and it was built with simple-gem (shameless plug).

Upload Images to Flickr With The Fleakr Gem

I just released version 0.4.2 of the Fleakr gem which includes better support for options when uploading files. To get started, install the gem from RubyForge:

$ sudo gem install fleakr

Then make sure that you have an API key, shared secret, and auth token for the Flickr API. You can find instructions on how to do this in the Fleakr documentation (see the "Authenticated Calls" section). Once you have that data, you can configure the gem pretty quickly:

Fleakr.api_key       = 'ABC123'
Fleakr.shared_secret = 'sekrit'
Fleakr.auth_token    = 'DEADBEEF'

Now, let's start uploading.

Single Files

The simplest way to upload is using a single file:

photos = Fleakr.upload('/path/to/my/image.jpg')

Once uploaded, you now have a reference to the new file through the photos array. By default, this image has a title based on the filename and is only viewable to you. Let's change that:

photos = Fleakr.upload('image.jpg', :title => 'My Mug', 
                                    :viewable_by => :everyone)

This new image is available to anyone and has a customized title.

Multiple Files

In addition to specifying a path to a single file, the upload method accepts a fileglob. This way, you can push out a full directory of images in a single call:

photos = Fleakr.upload('/party/images/*.jpg', :title => 'Party Photo',
                                              :viewable_by => :everyone)

Once the upload finishes, photos will contain a collection of the newly-uploaded images.

Additional Options

We saw how to set titles and permissions, but here are the full options that are available:

  • :title - Duh.
  • :description - The description of the image / photo.
  • :tags - A list of tags. This should be supplied as a single string value or an array of strings.
  • :viewable_by - A list of who can view this photo. Can be either :everyone or a combination of :friends and :family (e.g. :viewable_by => [:friends, :family]).
  • :level - The "safety level" of the photo. Can be one of :safe, :moderate, or :restricted
  • :type - What does this image represent? Can be one of :photo, :screenshot, or :other
  • :hide? - Should we hide this image from public searches? Set to true or false.

This list is also available in the documentation for Photo#upload.

Replacing Files

Remember when I said that the upload method returned a list of newly-uploaded photos? You can use that to replace the image if you'd like:

photos = Fleakr.upload('/path/to/image.jpg', :viewable_by => :everyone)
photos.first.replace_with '/path/to/other_image.jpg'

The same applies to any photo in your photoset. For example:

user = Fleakr.user('my_username')
photo = user.photos.first.replace_with('~/embarrassing_photo.jpg')

Enjoy!

Additional Resources

Sync Your Flickr Photosets With Your iPod

One of the main reasons I wrote the Fleakr gem was to pull down my latest photosets from Flickr and sync them with my iPod. To get started, create a directory that you'll use to store all your sets:

$ mkdir /tmp/flickr

Connect your iPod, open iTunes, and navigate to the "Photos" configuration tab. Select the option to sync photos from a local directory (first checkbox) and choose the directory you just created (/tmp/flickr):

iTunes Configuration

To download your latest set, you can use this code. Go ahead and name this file 'flickr-download', but make sure to install the fleakr gem first:

#!/usr/bin/env ruby

require 'rubygems'
require 'fleakr'

Fleakr.api_key = 'sekrit'

user = 'the decapitator'
target_dir = '/tmp/flickr'

Fleakr.user(user).sets.first.save_to(target_dir, :medium)

Once this file is saved, make it executable and run it:

$ chmod +x flickr-download
$ ./flickr-download

Once your script completes, you'll have the latest photoset saved to your target directory:

$ ls -F /tmp/flickr/
The Decapitator/
$ ls -1 /tmp/flickr/The\ Decapitator/
01_2117922283_715587b2cb.jpg
02_2125604584_9c09348fd6.jpg
03_2118696542_8af5763bde.jpg
04_2146369495_e266cf3c18.jpg
05_2146384313_a8dbfb589c.jpg
...

You'll notice that each file is prefixed with a series of digits - this is used to preserve the original order of the photos in the set. Otherwise your photos would display in a seemingly random order when played as a slideshow on your iPod.

That's it! Your photos will now be available on your iPod after the next synchronization. If you want to retrieve other sizes, you can specify that size in place of where I put :medium above. See the documentation for all available sizes.

You can run this script each time you add a new set to your Flickr photostream, or even schedule it to be run periodically using cron or a tool like Lingon.

Using Fleakr to Log API Calls

I just added a new feature to the fleakr gem that gives you the ability to log all calls to the API. To get started just point it to a logger:

Fleakr.logger = Logger.new('/tmp/fleakr.log')

This is great when just using the gem, but it's more useful when you're using it as part of your Rails app:

# config/initializers/fleakr.rb
Fleakr.logger = RAILS_DEFAULT_LOGGER

By default, this logs all traffic (including file data if you're doing uploads), so you may want to turn down logging in development to see just the requests:

# config/environments/development.rb
config.log_level = :info

If you missed it, check out my recent post to get started using Fleakr in your Rails app.

Connect Your Rails App to Flickr With The Fleakr Gem

Fleakr started out as a simple way for me to download photos through the console (something it does quite well), but it is also really easy to use inside your Rails application.

Let's build a sample app that displays the latest photos for a specific Flickr user. First, install the gem and create your Rails app:

$ sudo gem install fleakr
$ rails fleakr-demo

Configure the gem inside your application:

# config/environment.rb
config.gem 'fleakr'

Since we're doing read-only calls, we only need an API key - let's set that in an initializer:

# config/initializers/fleakr.rb
Fleakr.api_key = 'your_api_key_here'

Now we'll create a simple action that will accept a Flickr user as a parameter and pull back the recent photos:

$ script/generate controller users show

Then, add a route (and remember to delete public/index.html):

# config/routes.rb
map.user ':username', :controller => 'users', :action => 'show'

Add a line to retrieve the specified user:

class UsersController < ApplicationController

  def show
    @user = Fleakr.user(params[:username])
  end

end

Finally, create some simple view code to pull back the latest photos (limited to 100) in groups of 10

<% # app/views/users/show.html.erb %>
<% @user.photos.each_slice(10) do |photos| %>
  <div>
  <% photos.each do |photo| %>
    <%= link_to image_tag(photo.square.url), 
          photo.url, 
          :width => photo.square.width, 
          :height => photo.square.height %>
  <% end %>
  </div>
<% end %>

You're all set - start up your server and point your browser to a URL containing a Flickr username (e.g. http://localhost:3000/teamviget).

Warning: This is extremely inefficent and is designed only to show you what's possible and shouldn't be used in a production application. The rendering of this view can take up to a minute because it has to do N + 1 API calls (where N is the number of recent photos, capped at 100).

Multi-line Memoization in Ruby

During a recent code review, David pointed out a quick tip to do multi-line memoization. It's a cool technique that I've used in my own code:

class Search
  def results
    @results ||= begin
      response = Fleakr::Api::MethodRequest.with_response!('photos.search', parameters)
      (response.body/'rsp/photos/photo').map {|p| Photo.new(p) }
    end
  end
end

Check out the post on the Viget Extend blog for more details.

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