All posts filed under Programming. You can view a list of all the categories here.

Serialized Attributes in Rails

Over the last couple days I've been playing around with the Marshal library in Ruby for the serialization of objects. The idea came to me when I was snooping around Mint's database structure. It stores application settings and certain statistics in serialized format. I experimented for a while in pure Ruby, and after having reasonably success, moved to Rails.

Now for some great advice, always search first! I ended up implementing my Ruby Marshal technique in Rails, only to later find out about Rails's serialize() method. While it uses YAML in place of Marshal to serialize the data, it was a perfect solution.

# serialize(attr_name, class_name = Object)

If you have an attribute that needs to be saved to the database as an object,
and retrieved as the same object, then specify the name of that attribute
using this method and it will be handled automatically. The serialization is
done through YAML. If class_name is specified, the serialized object must be
of that class on retrieval or SerializationTypeMismatch will be raised.


# Parameters

attr_name - The field name that should be serialized.
class_name - Optional, class name that the object type should be equal to.


# Example

class User
  serialize :preferences
end

This worked great, except for one part. It was difficult to write forms to create/update the serialized attributes, since the application had no idea what fields I wanted to store. So after a bit of playing, I came up with some getter/setter methods.

class Article < Content
  serialize :data

  def title
    data[:title]
  end

  def title=(value)
    self.data[:title] = value
  end
end

That solved the form.text_field :title problem, but it still didn't work because when I created a new Article, there was no such thing as data[:title]. The solution was to initialize the :data attribute when creating the object. Here's an example:

class ArticlesController < ApplicationController::Base
  def new
    @article = Article.new(:data => { :title => nil })
  end

  def create
    @article = Article.new(:data => { :title => params[:article][:title] })
    # Code to save record...
  end
end

That's it. It's not the cleanest solution I think, but it did the job just fine for the application I was writing.

Filed in Programming.

String#gsub! Snippet

Here's a little snippet in Ruby that turns this:

Boy: Hello world!
Girl: Welcome!
Boy: How are you?
Girl: Good!

In to this:

<b>Boy</b>: Hello world!
<b>Girl:</b> Welcome!
<b>Boy:</b> How are you?
<b>Girl:</b> Good!

And here it is:

string.gsub!(/\w+:/) { |match| match = content_tag(:b, match) }

Great for oh, maybe IRC quotes on a tumblelog? ;)

Filed in Programming.

Updated Design

Since Apple just released a bunch of new products, I thought it would be right for me to release my latest product. So here it is, a new design! It doesn't quite deserve the term “product”, I know.

The only product I'm working on is a tumblelog, which I've hinted to in previous posts. Everything is basically complete, it just needs a final polishing before it's good to go. Look for it by the end of this week.

Update: Currently experimenting with my recently listened tracks from Last.fm in the sidebar. It's parsing the feed and displaying it at the moment, but I might experiment with aggregation if it impacts performance too much (which it seems to be doing). Update Two: It was too slow, working on an aggregator.

Filed in The Site and Programming.

Thinking About Sphinx

I just finished installing Sphinx on my slice, and setting up Thinking Sphinx on my website. Previously I was just using a simple search method in my Post model, but it was always a little on the slow side, especially now since I've started to publish more posts. Getting Sphinx running on my development machine was simple thanks to this article. Installing it on my web server was also easy (./configure; make; make install).

The only down-side was the need to manually index and start the Sphinx server. Hello crontab, rake and capistrano.

Capistrano is mainly just for utility, so I can quickly start/stop/index from my local machine.

I put these tasks under the deploy namespace:

desc "Index Sphinx"
task :sphinx_index do
  run "/opt/ruby-enterprise-1.8.7-20090928/bin/rake -f /var/www/matharvard.ca/current/Rakefile thinking_sphinx:index RAILS_ENV=production"
end

desc "Start Sphinx"
task :sphinx_start do
  run "/opt/ruby-enterprise-1.8.7-20090928/bin/rake -f /var/www/matharvard.ca/current/Rakefile thinking_sphinx:start RAILS_ENV=production"
end

desc "Stop Sphinx"
task :sphinx_stop do
  run "/opt/ruby-enterprise-1.8.7-20090928/bin/rake -f /var/www/matharvard.ca/current/Rakefile thinking_sphinx:stop RAILS_ENV=production"
end

Here's the line in my crontab file.

* 0 * * * /opt/ruby-enterprise-1.8.7-20090928/bin/rake -f /var/www/matharvard.ca/current/Rakefile thinking_sphinx:index 
RAILS_ENV=production

This runs the rake command every day at midnight. As you can see, I'm using Ruby Enterprise Edition, though it probably won't make a difference if I used regular Ruby for this.

I also had to switch from SQLite3 to MySQL, in order to use Sphinx, which was easily accomplished by YamlDb!

Filed in Programming and The Site.

Submitting a CodeMirror'ed Field with JavaScript

This issue arose the other day while working on a code editor for a content management system. A little background information: it was just a single textarea that was submitted using AJAX to save the data; previously it was running CodePress, but it was decided to switch to CodeMirror to integrate with Tobias Lütke's Liquid Editor.

CodePress worked fine, it provided great syntax highlighting and saved properly with AJAX. Upon switching to CodeMirror, the saving process started malfunctioning. To sum it up, a user had to submit the form twice in order to send the correct params from the form. CodeMirror attaches a function onto a forms submit event called updateField, which as can be guessed, updates the form's field that CodeMirror is applied to, with the proper value (since CodeMirror hides the field and shows its own stuff in an iframe). So the problem was updateField wasn't being called, therefore, the old data was being submitted again.

The Fix

Let's get into some code! Here's the bit of code used to submit the form, which is called with the onsubmit= attribute on the form tag (note this is Prototype).

function submit_form(form) {
  new Ajax.Request('/admin/update', {
    asynchronous:true,
    evalScripts:true,
    parameters:Form.serialize(form)
  });
}

When a user submits the form, it send an AJAX request to /design/update which handles the data and presumably saves it to the database. To get it all working properly, the form's textarea's value needed to be updated with the new (modified) one from CodeMirror. Here's the code used to initialize CodeMirror.

var editor = CodeMirror.fromTextArea("editor", {
  parserfile: "parseliquid.js",
  stylesheet: ["/codemirror/css/xmlcolors.css", "/codemirror/css/liquidcolors.css"],
  path: "/codemirror/"
});

This puts the editor in place of a field with the ID of "editor". So from that example we know that we have access to an object called editor. CodeMirror provides a function called getCode which can be sent to editor. So essentially all that needs to be done is updateField manually!

function submit_form(form) {
  $("editor").value = editor.getCode(); // The magic dust
  
  new Ajax.Request('/admin/design/update', {
    asynchronous:true,
    evalScripts:true,
    parameters:Form.serialize(form)
  });
}

We set the field's value to the new value from CodeMirror, and continue on as normal, simple as pie! (Pie is not easy to make by the way.).

Hopefully this helps others who are encountering the same issue, and does a good job explaining the problem and solution!

Filed in Programming, Web, and Tutorials.

Converted to jQuery

The average reader (if there is such a thing) won't notice anything different. The few (if any) perceptive individuals will notice I've converted to jQuery. I don't have a huge well thought-out list of reasons why, the ones I can think of right now are simply because it's nicer. I mean, GitHub uses it, and they are logically awesome!

There isn't much to this blog, so making the switch was quite simple. The only bits that required actual work would have been the comment preview feature. Various other small bits were converted around the administration panel, but otherwise nothing required an overly large amount of time and dedication to get running. That is all.

Filed in The Site and Programming.

More Verbose Migrations

Migrations are a simple way of manipulating a database schema using pure Ruby. Rails migrations aren't tied to a specific database, which makes them very powerful. The same migrations and schema can be applied to MySQL, SQLite, Oracle, and a number of other databases.

This post is aimed at those who are already familiar with migrations and just want to add a bit of spice to them. I'm only writing about two simple methods that can make migrations a little more verbose. When rake db:migrate is called, it prints out more or less what is happening (tables or columns being manipulated). If you're running a migration that for instance cleans up records, Rails won't offer much information other than the migration's class name.

The first method is say_with_time(message). Here's a sample usage snippet.

say_with_time "Populating published_at column..." do
  # Sprinkle magic dust here
end

This will simple print the time elapsed since you ran the migration script and the provided message before executing the code inside the block.

Second, say(message, subitem = false). It basically does the same as the first method, prints a message to the console, but with the added bonus of the subitem option. The subitem option just indents the output, so it looks like it's part of the parent migration.

say "Adding default user to database...", true
# Sprinkle magic dust here

This would print the message indented in the console. Of course with the addition of a database seed file in Rails, putting default data in a migration is no longer needed.

Now, let's bring these fantastic methods together in one example.

say_with_time "Destroying users with nil account_id..." do
  if user.account_id.nil?
    say "Destroying user #{user.id}", true
    user.destroy
  end
end

Filed in Programming and Tutorials.

Named Scope Utilities

One of (many) cool features in Rails is the named_scope method (in Rails 3.0, this is called scope!).

Article.find(
  :all,
  :conditions => ["published_at IS NOT ?", nil],
  :limit => 5,
  :order => "published_at DESC"
)

Here's a perfectly ordinary and valid use of the find method to query all records in the database with values in their published_at column with a maximum limit of 5 records.

With a named_scope, this could be:

Article.recently_published

In the model:

class Article < ActiveRecord::Base
  named_scope :recently_published,
              :conditions => ["published_at IS NOT ?", nil],
              :limit => 5,
              :order => "published_at DESC"
end

It keeps controllers looking clean and lean. In a completely hypothetical situation, a problem could arise when for instance a varying number of records need to be pulled from the database. Instead of 5, maybe 10, or 11! named_scope's could be written for all of them, but a more simple solution is to just make a utility scope.

named_scope :limit, lambda { |limit| { :limit => limit } }

With this it's possible to do things like: Article.recently_published.limit(10), Article.find(:all).limit(9) or even Article.unpublished.limit(10000). There are multitudes of ways named_scope can be used. Check out Railscasts episode #108 for more tricks.

Filed in Programming.