Integrating Flickr into your rails website

Anton Jenkins | April 06, 2009

In this post I’m going to show you how I created the little Flickr stream you can see running down the right hand edge of this site.

Step 1: Get a Flickr API key

Visit this page and follow the instructions to get a key and write it down somewhere safe. Of course you are going to need a Flickr account to do this as well!

Step 2: Install the flickr_fu gem

This is the library that makes all the magic happen. Install the gem on your machine:

1
sudo gem install flickr-fu

Notice that’s a hyphen between ‘flickr’ and ‘fu’, not an underscore. And remember that you’ll need to install this gem on your production server as well, so make a note to do that.

Step 3: Tell your rails app to include the flickr_fu library

Just a quick visit to environment.rb to pull it in:

1
2
3
# config/environment.rb

require 'flickr_fu'

Underscore used this time. Very confusing!

Step 4: Configure the gem using a flickr.yml file

The best place to store this is in your config directory along with all your other config settings. Get that piece of paper handy that you wrote down your API key on because you will need it now:

1
2
3
4
5
# config/flickr.yml

key: "<paste your key in here>"
secret: "<paste your secret in here>"
token_cache: "token_cache.yml"

You won’t need the API key any more so you can eat that piece of paper.

Step 5: Have a play!

Right. Time to mess with it! What we are going to do now is fire up the rails console and retrieve some of your images. However the flickr APIs need to know your Flickr ID. For this we will use a website called idGettr

What you do is paste in the URL for your Flickr photostream and it spits out your Flickr ID which we can then use with the APIs. So for me I typed in http://www.flickr.com/photos/antonjenkins/ and it returned an ID of 12864272@N02.

Write this down somewhere, memorise it and then eat the piece of paper.

So lets have a play in the rails console:

1
2
3
4
# ./script/console

>> flickr = Flickr.new(File.join(RAILS_ROOT, 'config', 'flickr.yml'))
=> #<Flickr::Base:0x395d514 @token_cache="token_cache.yml", @api_secret="oooh, that's a secret!", @api_key="I could tell you but I'd have to kill you">

What we’ve done there is pointed flickr_fu to our little flickr.yml file and it’s given us back a connection to Flickr. So lets use it (and that Flickr ID we looked up a minute ago)....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>> photos = flickr.photos.search(:user_id => "12864272@N02")

# Boom! Loads of output!

>> photos.count
=> 90

# I seem to have an array of photos. Lets look at one...

>> photos[1].title
=> "Quack!"
>> photos[1].url
=> "http://farm4.static.flickr.com/3104/3303703736_ba4bea1dc5.jpg"

# Jackpot!

So we’re very happy now – we’re using ruby to talk to Flickr! What we need to do now is get this on to our website.

Step 6: Writing a flickr helper

It’s a good idea to separate out functionality like this into helpers and partials, rather than weave it directly into your existing code. So we’re going to create a flickr_helper.rb in the app/helpers directory like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# app/helpers/flickr_helper.rb

module FlickrHelper  
  def user_photos(user_id, photo_count = 12)
    flickr = Flickr.new(File.join(RAILS_ROOT, 'config', 'flickr.yml'))
    flickr.photos.search(:user_id => user_id).values_at(0..(photo_count - 1))
  end
  
  def render_flickr_sidebar_widget(user_id, photo_count = 12, columns = 2)
    begin
      photos = user_photos(user_id, photo_count).in_groups_of(2)
      render :partial => '/flickr/sidebar_widget', :locals => { :photos => photos }
    rescue Exception
      render :partial => '/flickr/unavailable'
    end
  end
end

The method which we will be calling from our view to display our flickr photos is:

1
render_flickr_sidebar_widget(user_id, photo_count = 12, columns = 2)

This method will prepare an array of photos and pass them to a partial (which we’ll get to in a minute). You may have noticed the call to in_groups_of which is quite interesting:

1
photos = user_photos(user_id, photo_count).in_groups_of(2)

Because we want two columns of photos in our little sidebar we need to group the array of photos. This is where in_groups_of comes into play. Let’s see how it works on the rails console:

1
2
3
4
5
6
7
8
>> Array(1..10).in_groups_of(4)
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, nil, nil]]

# Notice how it pads out the last array with nils

# We want to do group our flickr photos into groups of 2, one for each column
>> Array(1..6).in_groups_of(2)
=> [[1, 2], [3, 4], [5, 6]]

So our photos will group like so:

This grouped array gets passed to the flickr/sidebar_widget partial. Let’s take a look at that now:

1
2
3
4
5
6
7
8
9
10
11
12
13
# app/views/flickr/_sidebar_widget.html.erb

<div class="flickr">
  <ul>
    <% photos.each do |row| -%>
      <li>
        <% row.each do |p| %>
          <%= link_to(image_tag(p.url(:square), :class => 'flickr_photo', :title => p.title, :border => 0, :size => '75x75'), p.url_photopage) %>
        <% end %>
      </li>
    <% end -%>
  </ul>
</div>

I’m then styling this with the following CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.flickr ul
{
        list-style-type: none;
        margin: 0;
        padding: 0;
}
.flickr ul li 
{
        border-bottom: 0;
        margin: 0;
        padding: 0;
}

.flickr_photo
{
        width: 75px;
        height: 75px;
        padding: 12px 10px 32px 13px;
        margin: 0;
        background: #fff url(../images/pixellated/polaroid.jpg) no-repeat;
}

Each flickr thumbnail is placed on top of a little fake polaroid image I knocked up:

You may have also noticed a call in our flickr helper to an unavailable partial if there was an exception whilst trying to speak to flickr:

1
2
3
rescue Exception
  render :partial => '/flickr/unavailable'
end

I’m leaving that partial blank so if flickr is down it just doesn’t render anything. But if you want you can display something else instead of the flickr output then place it in the _unavailable.html.erb partial.

Step 7: Call the helper in your application.html.erb layout template

Just a simple call to…

1
<%= render_flickr_sidebar_widget('12864272@N02') %>

Of course you’ll put your own flickr ID in there instead of mine. Unless you really like my photos?! ;o)

Are we done yet?

Ah. Not quite! You may have noticed something about this when you started testing it…... it’s slow as hell! Every single page render now has to wait for a round trip to flickr. Not cool!

Step 8: Fragment caching to the rescue!

This is a perfect candidate for fragment caching. The flickr photos aren’t going to be changing too often so we can cache them and expire them when we know they have changed.

So we’re going to surround the call to the flickr helper in a fragment cache block:

1
2
3
<% cache ("flickr_sidebar") do %>
  <%= render_flickr_sidebar_widget('12864272@N02') %>
<% end %>

Remember this won’t speed up your renders on your development server as caching is disabled by default in development, but on production it will fly. It won’t cache the images (and why would you want to as flickr is built to serve images?) but it will cache the actual HTML code which required the costly API calls to flickr in order to generate.

Now we need some code to expire this cache when you’ve added some photos to flickr and you want to update the photos on the website. In fact, it would be really cool if you could expire this cache using capistrano, so you wouldn’t even need to ssh into your server to do this. For this I refer you to my previous post on expiring page and fragment caches using capistrano.

Comments

  1. Darren |

    Thanks for this, very useful.
    I use Flickraw myself (for http://filmdev.org) but didn't really know enough about partials and fragment caching.
    I implemented my own Flickr results caching by storing photo ids in the database etc.
    What a faff. :-)

    Will look into doing it this way when I have a spare few hours.

  2. https://me.yahoo.com/a/U8X_1xcnjIcUzdDa55JOzpPnSJNvkN88o2U- |

    This is a great little flickr API. It's much easier to set-up than rflickr which is what i was using before. The only problem is that flickr-fu does not have the the photosets methods, which i need (rflickr does have these). I'd like to continue using flickr-fu. any suggestions?

  3. http://antonjenkins.myopenid.com |

    Looking through the flickr_fu rdocs I can't find anything to do with photosets. The ideal place for it would be the search functionality so that you could do something like...

    1
    
    flickr.photos.search(:user_id => "12864272@N02", :photo_set => 'explored')
    

    Unfortunately it's just not there. Sorry!

  4. Scott |

    I was getting this error when trying to start WEBrick:

    c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- mime/types (MissingSourceFile)

    Fixed it by installing mime-types (1.16) as a gem:

    gem install mime-types

  5. Rob |

    I get this error when following this tutorial;

    /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- mime/types (LoadError)
    from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'

    Can you please confirm where to place the require 'flickr_fu' in the environment file please?

    Could you possible help me with my problem. Thanks!

  6. http://antonjenkins.myopenid.com |

    @Rob, I place the flickr_fu require at the end of the environment.rb file.

    I think Scott has solved the problem for you in the post above - apparently you need to install the mime-types gem.

    1
    
    sudo gem install mime-types
    

    Nice one Scott!

  7. minikin |

    Hi,

    Thanks you for tutorial.

    I find limitation : I can work only with amount of photos less then 100.

    photos[1002].title
    NoMethodError: You have a nil object when you didn't expect it!
    The error occurred while evaluating nil.title
    from (irb):14
    from :0

    Any ideas?

  8. http://antonjenkins.myopenid.com |

    @minikin : I'm guessing flickr_fu will only retrieve data for a certain amount of photos at a time to make it more efficient. I'd hazard a guess that 100 is that limit but I'm sure you could retrieve the next 100 by passing some parameters to the search method.

    Unfortunately I can't access the flickr_fu documentation at the moment so I can't look it up and I haven't got time to dig through the source code at the moment. If you've got the time then checking the source out might be the answer. If not we'll just have to wait until the documentation comes back online.

    Apparently the documentation should be here :

    http://www.commonthread.com/projects/flickr_fu/rdoc/

    But I'm getting a page not found on that link at the moment.



  9. minikin |

    @antonjenkins.myopenid.com

    thank you for your answer! )

    You can try this if you'll need:

    photos = flickr.photos.search(:user_id => "your id", :per_page => 500)

    From Flickr Api doc:

    per_page (Optional)

    Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.

    http://www.flickr.com/services/api/flickr.photos.search.html
    ________

    Now I have another problem (like you): I can't get a list of photosets.
    Seems like flickr-fu don't support this method

  10. Kendall |

    super clean work, thank you.

    Kendall

  11. Jay |

    Hi, thanks so much for the excellent tutorial, it helped me alot.

    For those of you looking to handle photosets, I found out the latest version of flickr_fu on github has a Photosets class. I installed it with:

    # gem install commonthread-flickr_fu -s http://gems.github.com

Post a comment


(lesstile enabled - surround code blocks with ---)