INCLUDE_DATA

Category ruby on rails

FIX: Blank webpage problems when first setting up Code Igniter, PHP, MySQL, and Apache 1

Oct29

If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!

Today, I created my first website using the Code Igniter PHP framework and, oh boy, do I like it a lot more than Ruby on Rails. But I’ll dig into that further in a future post.

The purpose of this post is to shed some light on a problem that I wracked my brain over for a few hours last night.

The problem: After installing Apache, PHP, and MySQL and configuring the Code Igniter framework, you get a blank web page when testing your first controller, model, and view.

What I discovered were two issues that, when combined, create the perfect storm of an installation problem:

  1. First, MySQL is no longer enabled by default with PHP 5. Says the PHP website of MySQL in PHP 5:

    MySQL is no longer enabled by default, so the php_mysql.dll DLL must be enabled inside of php.ini. Also, PHP needs access to the MySQL client library. A file named libmysql.dll is included in the Windows PHP distribution and in order for PHP to talk to MySQL this file needs to be available to the Windows systems PATH.

    It’s been a LONG time since I’ve programmed in PHP let alone installed PHP, so this was news to me. Anyway, if check out the MySQL installation notes on the PHP website if you need help enabling MySQL.  

  2. Unfortunately, that was the easy part. Once I had an error message to work with, Google lead me to the PHP website, and I was in the clear. But, for the longest time, I wasn’t even getting an error page. Remember, all I was getting was a blank HTML page. So what was the cause? Code Igniter…
  3. Using some creatively placed die() calls, I found out that the application was dying when trying to connect to the database. (Previous to this, I had already double- and triple-checked my MySQL username and password in the Code Igniter configuration file, and had even tried out alternate accounts, like my MySQL root account.)What I found out was that the MySQL drivers in Code Igniter were suppressing errors when attempting database connections.

This is what the code looks like in the Code Igniter mysql_driver.php file:

function db_connect()
{
   if ($this->port != '')
   {
      $this->hostname .= ':'.$this->port;
   }
   return @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
}
The “@” symbol before the mysql_connect() function call supresses any errors that may be returned. It is that symbol that wasted a good 2 hours of my life.Anyway, after I removed the “@” symbol from the code and re-tested my web application, PHP spit out the error message that I should have been presented with hours ago, and I was well on my way to fixing the problem.
 If you’re experiencing the same problem that I was, I hope this helps you correct it faster than I did! Best of luck.

Oh, and did I mention how much I prefer Code Igniter over Ruby on Rails? It’s messier, but with my C and Java background, it makes more sense than Ruby and Ruby on Rails does!

Zebra-striped tables using Rails 6

Nov30

Zebra-striped tables are tables where row colors alternate between a light color and a dark color and can be done both using server-side code (ie. Rails) or client-side code (ie. JavaScript). I posted a question asking for the most convenient way to create zebra-striped tables to the Rails newsgroup some weeks ago and the responses I received were actually quite scathing. I’ve normally done this type of thing using server-side code when working with other programming languages (ASP, PHP, JSP, ColdFusion, etc.), but many people feel that this is an extreme no-no (and aren’t afraid to tell anyone that’ll listen to them), as it muddles up the separation of business logic from presentation.

Anyway, my point is that, regardless of what others think, I want to continue doing this type of design using server-side code and a recent post on the Code Snippets website highlights a Rails helper tag called cycle that makes this very convenient. (The name will be familiar for those that have used the Smarty templating engine for PHP.) The cycle tag is exactly the type of suggestion I was looking for when I posted my initial question to the newsgroup. Before that, I’d been using some nasty combination of if statements and the mod operator to determine the row color and that had completely uglified my code.

This is how you use the cycle helper method:
<tr class="<%= cycle("even", "odd") %>">
... use item ...
</tr>

You can read more about the cycle helper method in the Rails API.

Amazon Web Services on Rails 9

Jul18

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.

Implementing Search in Ruby on Rails 12

Jul7

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 %>

Ruby on Rails find_all Example 2

Jul3

In an earlier blog post, I listed an example of using the find_by_sql function to return a collection of Artist objects. I have since changed my search method to use the find_all function, instead.

artist.rb (old code)
def self.find_by_first_letter(letter = "A")
   find_by_sql ["select a.* from artists a where ucase(left(artist_name, 1)) = ?", letter]
end

artist.rb (new code)
def self.find_by_first_letter(letter = "A")
   find_all ["ucase(left(artist_name, 1)) = ?", letter]
end

I think using the find_all function instead of the find_by_sql function follows the “Ruby on Rails way” more closely. Plus, it’s shorter and, perhaps, easier to understand for people who aren’t familiar with SQL.

Ruby on Rails find_by_sql Example 3

Jul1

Note: Read this post to learn how to implement the same method using the find_all function. - Rory, July 3, 2005

In the lyrics website I’m developing, I’d like to be able to use URLs like this in my administrative back-end:

http://www.website.com/admin/artists/list/A

where, when I visit this URL, a list of artists who’s name begins with the letter A is displayed. This requires the use of the Ruby on Rails find_by_sql method, as there is no way of querying for artists in this manner using the built-in Active Record.

The first thing I did was add a new method to my Artist model that will allow me to search for artists by the first letter of their name.

artist.rb
class Artist < ActiveRecord::Base
   has_many :songs, :order => "song_title", :dependent => true
   has_many :albums, :order => "album_name", :dependent => true

def self.find_by_first_letter(letter = "A")
      find_by_sql ["select * from artists where ucase(left(artist_name, 1)) = ?", letter]
   end
end

As you can see, the method find_by_first_letter takes in an argument called “letter” and uses this argument in the function call to find_by_sql. You’ll notice that a question mark appears in the SQL statement where we would actually like the value associated with “letter” to go. Queries created this way are called Parameterized SQL Queries, and by using them, we avoid the possibility of having someone tamper with our query using SQL injection.

Then, in our action, all we have to do is call our new method.

admin_controller.rb
@artists = Artist.find_by_first_letter(@params["letter"])

Related Drop-down Lists with Ruby and Ajax 50

Jun21

Note: I have been meaning to re-write this post to make better use of Ruby on Rails helpers, etc. When I first wrote this I was extremely new to Rails and my unfamiliarity with the framework showsUntil I have the free time to re-write this post, please bear with what is currently here and leave comments to help others that follow after you.

Finally! I’ve been working on this little bit of Ruby code for hours now, all because of some odd bugs (which, when I learn Ruby better, will probably cease to become odd) and a lack of good Ruby on Rails documentation.

What I’ve been working on is getting two related HTML drop-down lists to update properly using the built-in Ajax support in Ruby on Rails. Specifically, when the user selects an Artist from the first drop-down list, only that artist’s Albums should be listed in the second. I wanted to do this without having to retrieve all of the albums during the initial page load and without having to do a post back when the user selects an artist. So, that’s where Ajax comes in. Ajax uses the JavaScript XMLHTTPRequest routine to retrieve new data from the server without requiring the user to refresh the webpage.

Some gotchas for the new Ruby programmer / web developer:

  • You cannot modify the InnerHTML of a select drop-down list. Instead, you have to modify the InnerHTML of the div that _contains_ the select drop-down list, and when you do you have to recreate the whole drop-down.
  • When you are using instance variables (those that begin with the @-sign), ensure that the instance variable exists before trying to use:
    @variable += “value”
    syntax or else Ruby will throw up an error. For example, before that statement, add a line that reads:
    @variable = “”
  • When concatenating strings that contain instance variables, Ruby is picky in a way that I can’t fully describe. For example, the following does not work:
    @html += “<option value=’” + @album.id + “‘>” + @album.album_name + “</option>”
    But this does work:
    @html += “<option value=’#{@album.id}’>#{@album.album_name}</option>”

Anyway, this is the troubled code that _did not_ work:

admin_controller.rb
@albums = Album.find_all_by_artist_id(@params["artist_id"])
@html = "<select id='album_id' name='album_id'>"
@html += "<option value=''>No Album</option>"
@albums.each do |@album|
   @html += "<option value='" + @album.id + "'>" + @album.album_name + "</option>"
end
@html += "</select>"

After hours I got it working with this code:

admin_controller.rb
@albums = Album.find_all_by_artist_id(@params["artist_id"])
@html = "<select id='album_id' name='album_id'>"
@html += "<option value=''>No Album</option>"
@albums.each do |@album|
   @html += "<option value='#{@album.id}'>#{@album.album_name}</option>"
end
@html += "</select>"

But, this has to be coupled with the RHTML code in the template, as follows.

add_song.rhtml
<%= javascript_include_tag "prototype" %>

Artist
<select name="new_song[artist_id]" id="new_song[artist_id]">
   <option value="">Select Artist</option>
   <% @artists.each do |artist| %>
      <option value="<%= artist.id %>">
         <%= artist.first_name %> <%= artist.last_name %>
      </option>
   <% end %>
</select>

Album
<div id="album_id_container">
   <select name="new_song[album_id]" disabled="disabled">
      <option value="">No Album</option>
   </select>
</div>

<%= observe_field("new_song[artist_id]",
   :frequency => 0.25,
   :update => "album_id_container",
   :url => { :action => :add_song_artist },
   :with => "'artist_id='+value") %>