Entries tagged with “Ruby on Rails”

Requiring SSL Using Route Constraints in Rails 3

The new router in Rails 3 makes it super easy to require SSL for certain routes. Just use the following in your config/routes.rb:

MyApp::Application.routes.draw do
  class SslConstraint
    def self.matches?(request)
      request.ssl?
    end
  end

  scope :constraints => SslConstraint do
    resources :payments
    # Other SSL routes go in here
  end
end

Now, this is a pretty simple example–you’ll likely want to also have routes to redirect if a user tries to access without SSL, but it definitely shows off the power of the new router.

Posted on August 13, 2010 2 Comments
Tagged with: , , ,

Improving on Related Entries

A little while back, I posted about how I was determining related entries for my site. That method worked, but once I redid my site and added my 250+ Flickr photos, it started to really slow down when finding related photos, because of the increase in tags and posts. The real issue was that I was doing most of the work in Ruby, when it really should have been done with SQL. So, I decided to rewrite it.

Note: If you haven’t looked at my previous entry on the subject, you might want to take a look at it, just for the general idea of what I’m trying to accomplish. Essentially, I’m trying to find related posts by comparing tags. Here’s my new Post#related code:

def related(limit=5)  
  return [] if tags.empty?

  join_array = tags.collect {|tag| "posts_tags.id = #{tag.id}"}

  tags_join = "AND (#{join_array.join(' OR ')})"

  self.class.find(:all,
                  :joins => "INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id" +
                            "INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id #{tags_join}",
                  :conditions => ["posts.id != ?", id], :group => "posts.id",
                  :order => "COUNT(*) DESC",
                  :limit => limit)
end

You can see, as I mentioned, that all the work is being done in the SQL now. I first create a list of tags from the current post, which I then feed into the query to search for other posts with similar tags. The SQL instructs the database to search for any posts with any of these tags, and then orders them based on how many tags match between the 2 posts. The SQL ended up being fairly complicated, with a lot of joins, but it’s now a whole lot faster, because I’m not creating a lot of overhead by dealing with the computation in Ruby. If you’re interested, here’s an example related entry query:

SELECT `posts`.* FROM `posts`
INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id
INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id
    AND (posts_tags.id = 695
        OR posts_tags.id = 192
        OR posts_tags.id = 195)
WHERE (posts.id != 4322) AND ( (`posts`.`type` = 'FlickrPhoto' ) )
GROUP BY posts.id
ORDER BY COUNT(*) DESC
LIMIT 5

Posted on April 13, 2009 Leave a Comment
Tagged with: , , , , , , ,

Making Better Use of named_scope

In Ruby on Rails 2.1, a great little feature called named_scope was added that really makes complicated finds a whole lot easier. I’m going to walk through one way you can use scopes to clean up your code, and if you want more information, Ryan Daigle’s post is a good starting point.

The Situation

For a project I’m working on, users can submit reviews, and I needed a way to access a user’s friends’ reviews. At first, I considered adding a method like the following to my User model:

class User < ActiveRecord::Base
  def friends_reviews
    Review.find(:all,
      :joins      => "JOIN friendships ON user_id = #{self.id}",
      :conditions => "friendships.friend_id = reviews.user_id"
    )
  end
end

Named Scopes to the Rescue

While this method would work fine, there are several issues. For one, what if I want to change parameters on the find, like limiting it to 5 entries, or sorting by date? I would have to add parameters to the method, and it would start to get complicated. Instead, I created a scope in my Review model:

class Review < ActiveRecord::Base
  named_scope :by_friends_of, lambda { |user|
    {
      :joins      => "JOIN friendships ON user_id = #{user.id}",
      :conditions => "friendships.friend_id = reviews.user_id"
    }
  }
end

Now, to get the reviews I want, I can call Review.by_friends_of(user) and it will get me reviews by user’s friends. What’s even better, since it’s a scope, I can modify it. For instance, Review.by_friends_of(user).all(:limit => 10, :order => "created_at DESC") will limit it to 10 reviews, sorted by creation date. I can even use other scopes I might have created, like: Review.by_friends_of(user).published.all(:limit => 20), and so on.

Scopes are super useful, and recently I’ve started to use them for just about everything. Definitely try them out, they’ll make your code a lot more efficient and useful.

Posted on January 6, 2009 1 Comment
Tagged with: , , , , , ,

Finding Related Entries Using Tags with Ruby on Rails

One of the cool features that I built into my new site is the “related” sidebar box on every entry and link. Using a not-so-sophisticated algorithm, my site automatically picks out other entries that seem to be related to the current entry, which hopefully helps readers navigate to my other content. It really wasn’t too difficult to implement, so I figured I’d go through my thought process and the code that makes it happen.

The Not So Fancy Algorithm

It took me a little while to come up with a way to determine if an entry is “related” that was both accurate and relatively efficient–I could have used some complicated tool that parses the content of my entries, but instead I decided use something that’s a little simpler: tags. To understand how it works, pretend I have three entries:

  • Entry A - Tagged with: Turkey, Roast Beef, Cheese, Bread
  • Entry B - Tagged with: Bread, Baking
  • Entry C - Tagged with: Turkey, Cheese, Bread, Lettuce

Let’s say we’re looking for entries related to Entry A. From just looking at B and C, it’s clear that C should be closest, as they A and C both have something to do with sandwiches, whereas B only talks about bread. To rank the entriesprogramatically, I first do a query to find entries with any of the tags from Entry A. Then, once I have that list, I sort the entries by how many of Entry A’s tags are used. So, in the above example, Entry C would have 3 matched tags, and Entry B would have 2. It’s not a perfect system, but so far, it seems to be working pretty well.

The Code

So, here’s the code that’s performing all the magic:

class Post < ActiveRecord::Base
  def related(limit=5)
    @related ||=
      returning self.class.find_tagged_with(tag_list, :conditions => ['posts.id != ?', self.id], :limit => limit) do |posts|
        posts.sort_by do |p|
          matched_tags = p.tags.find_all {|t| self.tags.include?(t)}
          matched_tags.size
        end.reverse
      end
  end
end

The real meat of the method is in the returning block, so let’s take a look at that:

returning self.class.find_tagged_with(tag_list, :conditions => ['posts.id != ?', self.id], :limit => limit) do |posts|
  posts.sort_by do |p|
    matched_tags = p.tags.find_all {|t| self.tags.include?(t)}
    matched_tags.size
  end.reverse
end

What’s happening here is I’m first searching for any entries tagged with the current entry’s tags (I’m using acts_as_taggable_on_steroids), then, with the data that’s returned, I use Ruby to sort the entries by the number of “matched” tags, which then gets returned from the method. Conventional wisdom suggests moving the matched tags part into SQL, since MySQL is more efficient than Ruby at handling data. However, I’m using relatively small sets of information, and I haven’t run into any performance issues yet.

Overall, this method’s working pretty well for me, but I’m sure as I accumulate more posts, I’ll need to refine it some. I’d really like to incorporate some sort of popularity ranking, based on number of comments and views, but that’s not something I’m too worried about at the present.

Posted on November 11, 2008 2 Comments
Tagged with: , , , , , ,

Thoughts on Affordable Web Hosting

I’ve created my share of websites, and in doing so, I’ve had experiences with a whole lot of web hosts, some of which I’ve loved, others I’ve really disliked. Below, I compiled a list of four of my favorite hosts, two that offer shared hosting and two VPS hosts. Being a college student, all four are affordable, and I’ve had a good experience with all of them.

Dreamhost

The Basics: Super-cheap, feature-rich shared hosting.

Pricing: $9.95/mo. for 1 domain registration, 500GB disk space, 5TB bandwidth, unlimited emails and databases. If you use the code KS50 when signing up, you’ll get $50 off your first year.

My Thoughts: I love Dreamhost. I hate Dreamhost. I’ve gone back and forth between these two emotions numerous times, but ultimately I’ve been really satisfied with my Dreamhost experience. It’s impossible not to like all the developer friendly features, like Subversion hosting and full SSH access. However, they don’t have the the greatest history of downtime, and they even accidently overcharged customers by over $7 million. All that being said, I’m still a happy customer, because, well, they’re really, really cheap, and, for the most part, I haven’t had any issues with them. I wouldn’t host anything mission critical with them, but for the average blog or personal site, they more than suffice.

EngineHosting

The Basics: No frills, high quality shared and dedicated hosting, aimed mostly at ExpressionEngine users.

Pricing: $10/mo. for their cheapest plan (400MB disk space, 10GB bandwidth, 100MB database, 15 emails)

My Thoughts: If you’re looking for a feature-rich host with gigabytes upon gigabytes of storage space, EngineHosting definitely isn’t it. On their lowest plan, you get 400MB diskspace, 10GB bandwidth, and only 15 email addresses, which means if you compare them solely on numbers to hosts like Dreamhost, they will likely always come out on bottom. However, their customer service and quality is excellent, especially if you’re building an ExpressionEngine site. I’ve used them for a client site for about 10 months now, and they’ve been absolutely stellar: the site hasn’t been down once, and I haven’t had to contact support once. I won’t think twice about using them for any future ExpressionEngine sites.

RailsPlayground

The Basics: Affordable VPS hosting, specializing in Ruby on Rails.

Pricing: $14.95/mo. for the cheapest VPS plan (10GB diskspace, 256MB RAM, 100GB bandwidth)

My Thoughts: Although I only used RailsPlayground for a couple months, I really enjoyed their service. I had never run a VPS (virtual private server) before, and I was able to pick it up pretty quickly, thanks to their excellent support: I would come across an issue, email them, and they would respond right immediately. Even though I no longer use them, it’s not due to a poor experience, but rather due to my desire to try out Slicehost. I wouldn’t hesitate recommending them to anyone looking for an affordable VPS.

Slicehost

The Basics: Cost-effective VPS hosting, with an amazing community.

Pricing: $20/mo. for the cheapest plan (10GB diskspace, 256MB RAM, 100GB bandwidth)

My Thoughts: Slicehost is currently hosting this site, and I couldn’t be happier. I’m no sysadmin, but following their excellent articles has helped me to build a fast, secure, and stable VPS. I was further amazed by their community when I mentioned an issue at 1AM on their IRC channel, and within 20 minutes, it was fixed. They seem to be completely focused on providing really reliable hosting, and it really shows. The result is rock-solid hosting, and I highly recommend them for anyone looking for VPS hosting.

Posted on September 21, 2008 Leave a Comment
Tagged with: , , , , , , ,

RESTfully Forward Users to FeedBurner in Rails

After launching my new site, I realized I had forgotten to use FeedBurner to track my RSS feed subscribers. Because FeedBurner requires that you forward users to your feed on their servers, I needed a way to forward users along, but I also wanted to make sure that if FeedBurner’s spider came to the same feed URL, they would be served the source RSS instead of being forwarded. My solution ended up like this:

class NotebooksController < ApplicationController
  def show
    respond_to do |format|
      format.html
      format.rss do
        unless request.env['HTTP_USER_AGENT'] =~ /feedburner/i
          redirect_to 'http://feeds.feedburner.com/KyleSlattery'
        end
      end
    end
  end
end

If you take a close look at the, all I’m doing is checking the user agent of the request, and unless it’s FeedBurner, I redirect the user to the “burned” feed. If it is FeedBurner, things go ahead normally, and the RSS feed gets rendered.

This means that instead of creating separate actions, one that forwards to FeedBurner, and one that renders the RSS, I just have one action/URL that does both: http://kyleslattery.com/notebook.rss. So far, it’s worked great, and doing it this way really helped to keep my code clean and RESTful.

Posted on August 26, 2008 3 Comments
Tagged with: , , , ,

Posting to Brightkite using ActiveResource and REST

The other day, I came across Brightkite’s REST API. After taking a look at it, I decided it was the perfect opportunity to try out ActiveResource, the dead simple way to consume RESTful resources. In 20 lines, I was able to put together a simple script to find your most recent check-in on Brightkite and then post to that place.

A Quick Intro To Active Resource

First off, here’s an idea of just how easy it is to connect to a REST API using ActiveResource. Take, for instance, the “places” resource in Brightkite; these URLs all have the base http://brightkite.com/places. To interface with places, all it takes is this:

class Place < ActiveResource::Base
  self.site = 'http://brightkite.com'
end

That’s it. To get all the places, just do Place.find(:all). To create a new place, all it takes is Place.new. Amazing, to say the least. There’s a lot more that’s possible, so I recommend you check out the ActiveResource page on the Rails wiki.

The code

Below are the 20 lines necessary to post a note to Brightkite through the API. Just change USERNAME and PASSWORD to your username and password and change the note text at the bottom, and you’re ready to go. While this is a pretty simple example, it shows just how powerful a well constructed REST API can be.

Posted on August 11, 2008 21 Comments
Tagged with: , , , , ,

A Fresh Start

I never thought I’d actually get to this point, but, today, I am releasing a complete ground-up reworking of my site. With Ruby on Rails as a starting point, I was able to develop my very own content management system, and it is now powering everything here (with the help of a few plugins). I’m also excited to have a brand new design up, and it’s one I really have grown to enjoy. Read on for more details on the specifics of the new site.

The Design

Ever since I started using Twitter, I always used a stunning picture of the Crab Nebula as my background. When I started redesigning my site, I used the photo as a placeholder, but after a while it stopped being a placeholder and started driving the rest of the design, so I decided to keep it. The colors in the layout were directly influenced by the image: nearly every color comes directly from the Crab Nebula photo.

One thing I attempted with this was to not hold myself to the notion that there has to be a sidebar on every page. If you take a look at my About and Work pages, you can see where I switched things up. Personally, I think this helps to make it feel a little less like a blog, something I was definitely aiming for.

Even though I’m launching the new design today, there’s still quite a bit of refining for me to do. For one, the notebook page is pretty cluttered, so I really need to go through and organize that a lot better. In addition, several people I’ve shown the site to have mentioned that the header is a bit awkward; having the navigation attached and the title detached throws them off. I’m definitely going to need to find a better way to have that set up.

The Development

Developing the site is something I really enjoyed, and it allowed me to try a bunch of things I’ve been thinking of for a while. The site is built using Ruby on Rails, several plugins, and a whole lot of my own code.

One thing I’m proud of on the site is that every single page is cached. This way, things stay speedy, and the load on the server is lessened. Adding caching in Rails is really easy, and if you’re looking to do it yourself, you’ll probably want to check out this Rails Envy tutorial as well some Railscasts episodes.

For the server setup, I originally was planning to use Dreamhost and Phusion Passenger, but after using it for a bit, I felt like I needed a little more freedom, so I ended up buying a VPS from Slicehost. So far, I’ve been thrilled with Slicehost’s service and performance, and I’m definitely considering moving more projects over to them. As far as the technical side of things, I’m still using Passenger to run the site, though I might test out using Mongrel to see if that’s any faster.

Next Steps

I’m really excited about the new site, but I’m not done yet. Like I mentioned before, I still have some design tweaking to do, but I also have a lot I want to implement under the hood. There are bound to be issues that come up, and I’ll be hopefully fixing them as fast as possible, but if you run into something, please let me know.

I would love to hear any feedback (negative or positive) you have about the new site, so please leave a comment below!

Posted on August 8, 2008 2 Comments
Tagged with: , , , ,

Pass Blocks to Your Markdown Helper

For the new site, I’m going to be using Markdown to handle the formatting of all of my content. I created a simple helper to make things easy:

def markdown(str)
  RDiscount.new(str).to_html
end

(Note: I’m using the RDiscount library instead of BlueCloth, for reasons discussed here)

However, what if I wanted to add a “Read more” link at the end of the entry excerpt? I’d have to do something like this:

<%= markdown entry.excerpt + link_to("Read more", entry_path(entry)) %>

This way didn’t really appeal to me. What I really wanted to do was just pass a Ruby block, like this:

<% markdown do %>
  <%= entry.excerpt %> <%= link_to "Read more", entry_path(entry) %>
<% end %>

In order to do this, I just had to modify my markdown helper a bit:

def markdown(str='', &block)
  str    = capture(&block) if block_given?
  result = RDiscount.new(str).to_html

  block_given? ? concat(result, block.binding) : result
end

And, just like that, I can now pass blocks to my Markdown helper.

Posted on July 15, 2008 Leave a Comment
Tagged with: , ,

Redevelopment Update

I put together a quick video showing where I’m at with the new design and backend for KyleSlattery.com. Take a look, and let me know what you think!

Posted on July 12, 2008 3 Comments
Tagged with: , , ,

Organize Nested Resource Controllers More Efficiently in Rails

Nested resources are awesome. If you want to have a blog with entries that have comments, the resources are dead simple:

map.resources :entries do |entry|
  entry.resources :comments
end

Awesome, now you have set up paths like entries/3/ and entries/3/comments/ using an EntriesController and a CommentsController. What if, however, you want to have a base comments resource (/comments/) to show all comments? You already have a CommentsController, and it doesn’t do what you want. To solve this, I do something like this:

map.resources :entries do |entry|
  entry.resources :comments, :controller => 'entries/comments'
end

map.resources :comments

Now, you have all your routes set up, and you can have separate controllers, Entries::CommentsController (which lives in controllers/entries/comments_controller.rb) and CommentsController. Just remember when generating the nested controller to use the proper name, like so:

ruby script/generate controller entries/comments

Posted on June 24, 2008 Leave a Comment
Tagged with: , , ,

Advanced Rails Recipes

Advanced Rails Recipes

A couple weeks ago, I received my copy of Advanced Rails Recipes by Mike Clark (and others), and so far, I’ve really been enjoying it. The first book by Chad Fowler was easily my favorite Rails book at the time, and the new edition has definitely continued the trend. I’ve always learned best from examples, and the recipes in the book are all top-notch, real world scenarios that can be quickly and easily applied to your application. Already, I’ve used many of them while working on my site, and below are a few of my favorites.

  • Respond To Custom Formats
  • Freshen Up Your Models with Scope
  • Handle Multiple Models in One Form
  • Upload Images with Thumbnails
  • Send E-mail via Gmail
  • Preserve Files Between Deployments
  • Give Users Their Own Subdomain

I definitely recommend the book to any Rails developer, new or old, because the book truly has something for everyone.

Posted on June 7, 2008 Leave a Comment
Tagged with: , , ,