Browsing all articles from July, 2005

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.

Even though I followed this tutorial to get AWStats running on TextDrive, it still took about an hour to complete. It’s such a long process!

By the way, to save you some time, skip the third part of the tutorial on user authorization. Instead, just password protect the AWStats folder using the “Protected Web Directories” option from the Webmin main menu.

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.

If you haven’t heard already, Rails 0.13 has been released. Contained within the release are over 225 fixes and new features covering areas such as: Ajax, migrations, routes, rendering, and much more.

For a more detailed list, check out the release notes on the Ruby on Rails weblog.

I learned about Sparklines today through Brad Feld’s blog. Sparklines, as devised by Edward Tufte (of The Cognitive Style of PowerPoint fame), are small, word-sized symbols and graphs that appear inline within your sentence. The goal is to increase the meaning of what your saying, in ways that words along just can’t. For example, you might see a tiny, 30-day graph for a certain stock beside the stock symbol itself in a newspaper, just to give you an idea of the stock’s trends over the last little while.

If you know PHP, you can add Sparklines to your website using the PHP Sparklines Graphing Library.