Browsing all articles in ruby

Yay! My copy of Agile Web Development with Rails came in the mail today from Amazon. Time to learn _proper_ Ruby on Rails now.

: )

If you’ve been hosting your app with Textdrive using Apache and FastCGI, you may have noticed that FastCGI seems to crash every few minutes with these kinds of error messages:

[Mon Aug 08 03:40:25 GMT 2005] Dispatcher failed to catch: SIGHUP (SignalException)
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:597:in `each’
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:597:in `each_cgi’
dispatch.fcgi:18
FCGI process 28217 killed by this error

[Mon Aug 08 04:07:55 GMT 2005] Dispatcher failed to catch: exit (SystemExit)
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:10:in `exit’
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:10
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:10:in `call’
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:597:in `each’
/usr/local/lib/ruby/site_ruby/1.8/fcgi.rb:597:in `each_cgi’
dispatch.fcgi:18
FCGI process 38625 killed by this error

These crashes really don’t have _that_ much of an effect on your website, except that the next visitor to your website will have to wait a few seconds before anything appears while the web server caches some information. But my Ruby on Rails website gets quite a bit of traffic, so this happens to visitors every 5 minutes throughout the day.

So, I decided to move to Lighttpd from Apache, which should stop these types of errors from occuring.

The Lighttpd setup was extremely painless and I had everything setup within 30 minutes. The first step is to send a ticket to the support staff at Textdrive, requesting them to setup a Lighttpd port for you. In the reply from the Textdrive support staff, they’ll reference a couple great webpages that contain Lighttpd tutorials to follow. Specifically I followed these tutorials:

On the Deploying Application On Textdrive wiki page, skip halfway down the page and check out the section called Lighttpd For Idiots. This is a great tutorial that will get you nearly complete. (I did find one small problem with this tutorial though. When I ran the lighttpd command over SSH to start up the Lighttpd server, I got an error message saying that my lyricsdb.socket file didn’t exist. To fix this problem, just create a tmp/ folder in the lighttpd/ folder.

On you’ve completed this tutorial, head to the 2nd webpage that I linked to above. The 2nd link shows you how to set up a cron job so that your Lighttpd server starts every time the webserver is restarted.

Now, my Ruby on Rails website is being served by Lighttpd. I just hope that I didn’t miss anything, considering that my app was initially running over Apache and FastCGI. Do I need to turn anything off that I may have turned on to get Apache and FastCGI going in the first place?

I found this great post on the Ruby on Rails wiki that shows you how to run background jobs in Rails. Specifically, for the nightly maintenance that I’ll be doing on my website, I’ve chosen to follow the “Use cron or the like to run ruby code” method. There is some example code available on the wiki page that shows you how to access your Rails app from a script.

So far, all I’ve done is use the supplied example code as a base for my maintenance script. I’m in the process of adding the actual “maintenance” code at the moment and, as I do, am testing it by running the script from the command prompt: ruby nightly_maintenance.rb.

Are there any “gotchas” that I should be weary of when following this process?

Duane Johnson announced the release of two new Ruby on Rails gems today in the Ruby on Rails mailing list: rails_product and site. Combined, they give you the ability to use one generic web application installation for more than one website, and each site can be customized as per client requirements.

I think this is great news, and although I don’t have a need for the Rails Product Generator at this time, when I do, I’ll have to look at it in more depth. So far, though, the feedback on the mailing list has been overwhelmingly positive! Great work, Duane.

Rails Product Generator:

A “productized” application is a two-tiered application. The first tier is a single base application that encapsulates common functionality for your product. The second tier consists of several customized applications that inherit and extend functionality from that base. It is useful for web developers who want to create a base application with some sort of generic functionality (e.g. a shopping cart) and then extend or modify that functionality for specific clients only.

Installation information is available via the Rails Product Generator page on the Ruby on Rails wiki.

If you’re looking to make use of the Amazon Web Services through your Rails app, here’s a tip: avoid trying to communicate with them using the Ruby on Rails ActiveWebServices classes. I spent a couple hours trying to patch together a solution, but nothing would work. I believe what currently exists is geared towards making your Rails app accessible through Soap or XMLRPC, rather than consuming exisiting web services.

Instead, use Ruby/Amazon, a “Ruby language library that allows programmatic access to the popular Amazon Web site via the REST (XML over HTTP) based Amazon Web Services.” Installation of the library on my development box was a breeze (just read the installation document to learn how) and, plus, it’s already preinstalled on the servers at TextDrive, so getting going using the Ruby/Amazon library is not a problem at all. For a quick tutorial on how to use the library, check out the documentation on the top-level Amazon module or read this entry on the GleepGlop blog. Both contain examples that show you how to make a basic connection to Amazon through the web services and how to query for products.

Once you get this far, though, you’ll realize that making calls to the Amazon Web Services with every page view is going to decrease the speed at which your website will render for your users. So what can you do instead? Cache the results in your database, that’s what. On my lyrics website, I want to have links back to Amazon for every artist that I have lyrics for. So, what I did was create an administrative function that will connect to Amazon and return a list of items for a given artist. This process is repeated over and over so that I have a huge archive of products for each of the artists. Then, instead of querying Amazon with each page view, I just need to call this administrative function every month or so, to update the products in my database.

Below, you’ll see a body of code that updates all products for all artists whose names begin with a particular letter (for example, “A”). For each artist, all existing products are deleted from the database. Then, a new list of products is retrieved and each product is added to the database.

amazon_controller.rb
require 'amazon/search'

class AmazonController < AdminController
   include Amazon::Search

   ASSOCIATES_ID = "amazonaffiliateid-20" # Your Amazon Affiliate ID
   DEV_TOKEN = "0222NLPJQD0A7633Q182" # Your Amazon Web Services Key

   def update
      artists = Artist.find_by_first_letter(@params["letter"])

      artists.each do |artist|
         # Delete all existing "out-dated" products
         existing_products = Product.find_all_by_artist_id(artist.id)
         existing_products.each do |existing_product|
            existing_product.destroy
         end

         # Query Amazon
         request = Request.new(DEV_TOKEN, ASSOCIATES_ID)
         response = request.artist_search(artist.artist_name)
         products = response.products

         # Loop over each product and add them to the database, one at a time
         products.each do |product|
            new_product = Product.new

            new_product.artist_id = artist.id
            new_product.product_name = product.product_name
            new_product.url = product.url
            new_product.image_url_small = product.image_url_small
            new_product.image_url_medium = product.image_url_medium
            new_product.image_url_large= product.image_url_large

            new_product.save
            new_product = nil
         end
      end

      redirect_to(:controller =< "admin", :action => "index") # Return to the list page if it suceeds
   end
end

What I’d like to do now is to have a Ruby script run nightly that updates a portion of the products in the database. For example, at midnight on the first day of each month, all artists whose names begin with A would have their products updated from Amazon. At midnight on day two, all B artists would have their products updated. I just have to figure out how to do this… but once I do, I’ll post it here.

I’m proud to develop websites using Ruby on Rails, so at the bottom of my LyricsDB website, I’m displaying a shrunken down version of the Ruby on Rails logo. But, to jazz things up a little bit, I’ve created two versions of it: a grey version and a red version.

Ruby on Rails: Off Ruby on Rails: On

Combined with some simple Javascript rollover code, and you’ve got a sweet little effect. You can see it in action at the bottom of the webpage here.

Yep, it’s now online and viewable by everyone. Check it out here: http://www.lyricsdb.ca

It isn’t much to behold yet, as I haven’t really put much time into the presentation of the site, or even the front-end at all. But I’ve got some more important things to deal with right now, such as adding content to the site quickly.

I have two goals for the LyricsDB.ca website. First, I want to use it as a project through which I’ll learn Ruby on Rails, and that is already happening. With every hour that I put into the site, I feel more comfortable with the Ruby language and the Ruby on Rails framework, and I know that the experience I’m gaining will lead to better constructed projects in the future. Second, I hope to generate enough ad revenue from the site to cover the costs of my TextDrive hosting account. Sure, $12 a month isn’t that much of a burden, but if this one site can cover that monthly cost, then I can take my time with the two additional sites I’m allowed to host on the same account.

So, there ya have it. Anyway, here’s one last link to the website.

The first Ruby on Rails book, Agile Web Development with Rails, has left beta and is on it’s way to the printers, says David on the Ruby on Rails weblog. The book has received great reviews by many in the industry, and Rail programmers generally say that the book gives you a more complete understanding of Rails than when compared to just reading online tutorials and code samples.

Update: While searching like this works, it’s probably not the most efficient way. In the near future I’ll be looking into setting up Ruby/Odeum, which is a ruby binding to the QDBM Odeum inverted index library. Basically, every night a new search index would be generated, and searching would be done over this index, rather than dynamically over the database. This should greately improve performance. But until then, this is what I’m using …


I found this great post on Brandon Philips blog, which showed an example of how to search your database for relevant data, given a search string from the user. He got his inspiration from Tobias’ typo source tree, and, in turn, so did I. (Actually, mine is basically the exact same search, with some of the database column names changed. Thanks Tobias.)

song.rb
def self.search(query)
   if !query.to_s.strip.empty?
      tokens = query.split.collect {|c| "%#{c.downcase}%"}
      find_by_sql(["select s.* from songs s where #{ (["(lower(s.song_title) like ? or lower(s.song_lyrics) like ?)"] * tokens.size).join(" and ") } order by s.created_on desc", *(tokens * 2).sort])
   else
      []
   end
end

Once you’ve added the search method to your model, it’s even simpler to use.

search_controller.rb
def songs
   @query = @params["query"]
   @songs = Song.search(@query)
end

You’ll notice that I’m saving the search query into an instance variable called @query. This is so that I can output the query back to the user on the search results page, phrased like, Search results for “search query here”. If you’re going to do the same, don’t forget to sanitize the search query manually, or escape the HTML like so:

<%=h @query %>

Deploying my first Ruby on Rails application to TextDrive took about an hour, which is about how long I figured it would take. I followed bits and pieces of three tutorials from the Ruby on Rails website to complete the deployment, but I still ran into a few problems.

If you’re looking to deploy your app to TextDrive, check out these three links:

Some extra tips that I’ve picked up during the process:

  1. After you make changes to the Virtual Server Options in the Webmin control panel, don’t forget to press the Apply Changes link in the top right-hand corner so that the changes actually go through.
  2. After you have uploaded your files to the server, if you get the error “Rails application failed to start properly”, it may be that you’ve forgotten to do one of the following:
    • Upload the files in ASCII mode
    • CHMOD all of the dispatch.* files 755
    • Change the location of the Ruby binary to #!/usr/local/bin/ruby

Also, I was VERY impressed with how fast my application ran in FastCGI mode when compared to the regular mode (where it was actually quite sluggish). On TextDrive, it’s very easy to get your program running in FastCGI mode; just uncomment the fcgi line in your .htaccess file and comment the cgi line, as so:

# Example:
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
RewriteEngine On
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^(.*)$ dispatch.cgi [QSA,L]

Anyway, in the next few days I’ll actually announce the launch of the website. It’s nothing to be proud of, but it works quite well. I’m most proud of my administration area, which has a few snippets of Ajax sprinkled in. Until the launch, though, I’ll be hard at work fixing up some UI issues, as well as adding content.