Archive for the ‘Ruby on Rails’ Category

You are currently browsing the archives for the Ruby on Rails category.

Bizarre BBEdit 9 bug

Posted September 5th, 2008 in Ruby on Rails | Permanent Link

After upgrading to BBEdit 9, I’ve happily left TextMate behind and returned to my favorite bare-bones IDE for Rails development. (I’m planning to share some tips on this topic this weekend.)

I just came across a very strange bug that crashes BBEdit, and thought I’d share a workaround in case someone else has this problem. The bug report has already been filed.

Operating environment:
BBEdit version 9.0 (1320) of Wed, 27 Aug 2008
Mac OS X 10.5.4 Build 9E17

To reproduce:

  • Create a new file
  • Set its type to Ruby using the menu at the bottom of the window
  • Type (not paste) the following, exactly as you see it here:

    "tag-#

BBEdit will now invoke the spinning beach ball of death, start consuming massive CPU cycles, and never recover. Note that I have text completion triggers turned OFF, so that’s probably not the problem.

The workaround for this is (and any similar string that brings this on) is open the Scratchpad window (Window -> Show Scratchpad), type your string in there, and paste it back into your Ruby file.

I should point out that a couple of minor issues aside, I’m incredibly happy with this upgrade. And given my history with Bare Bones, I expect fixes to be forthcoming quickly.

Getting to know acts_as_versioned

Posted August 27th, 2008 in Ruby on Rails | Permanent Link

I’ve been using Rick Olson’s acts_as_versioned Rails plugin on a project and recently ran into some thngs that I thought might be worth sharing.

In general, acts_as_versioned behaves well and integration has gone smoothly. The object that we wanted to version is called SurveyContent. We created a database table called survey_content_versions with the same columns and data types as survey_contents, plus id (key) and survey_content_id (foreign key to the versioned object). After adding :acts_as_versioned to the class definition for SurveyContent, every save of a SurveyContent object that has changes results in a new survey_content_version entry in the database.

Making acts_as_versioned play nicely with acts_as_state_machine

acts_as_versioned provides methods for things like .revert_to, .latest, and .previous, which is really nice. But we ran into trouble on a few fronts, particularly since SurveyContent also uses acts_as_state_machine. Users can request changes to their associated survey content, but these changes are subject to administrative approval, so we basically care about two versions of a SurveyContent instance: the approved version, and the latest version. Since we have distinct states defined for acts_as_state_machine, we’re used to calling foo.approved? to see if an object is in the “approved” state. Unfortunately, these convenience methods are not available on our survey_content_version object.

We ended up adding the methods to our version by creating anonymous mix-ins in SurveyContent:

class SurveyContent < ActiveRecord::Base
...
  acts_as_versioned do
    def approved?
    	self.status == "approved"
    end
    ...
  end

No real magic here– the documentation for the plugin provided the tip– but hopefully this will be of use to someone else.

Cloning a version

There was another situation in which we needed to act on an actual instance of a different version of SurveyContent, while keeping the existing instance intact. In that case, we opted to clone:

def approved_version
  if @approved_version.nil? && self.survey_content
    _last_approved = self.survey_content.versions.reverse.detect
      {|v| v.status == "approved"}
    unless _last_approved.nil?
      @approved_survey_content = SurveyContent.new
      _temp = _last_approved.attributes
      _temp.delete('survey_content_id')
      @approved_survey_content.attributes = _temp
    end
  end
  @approved_survey_content
end

Maybe a little ham-fisted, but it got the job done. Note that we had to drop the survey_content_id from the attributes, as SurveyContent has no such field.

acts_as_versioned and serialized attributes

The last problem we ran into had to do with the fact that one of the attributes on our versioned object was serialized. Out of the box, acts_as_versioned ended up converting it to a string. Luckily, some Google mining later, we found the solution.

First, we added some code to acts_as_versioned.rb:

def acts_as_versioned(options = {}, &extension)
...
    # Preserve serialized attributes
    self.serialized_attributes().each do |key, value|
      versioned_class.serialize key, value
    end

  end

end

module ActMethods
...

Then, we added another method to our anonymous mix-in on SurveyContent:

class SurveyContent < ActiveRecord::Base
...
  acts_as_versioned do
    def after_initialize
      self.data ||= Hash.new
    end

    ...
    def approved?

(Note that in the snippet above, data is the name of our attribute.)

One last problem with serialization that we ran into was that acts_as_versioned was not detecting changes when we did a merge operation on our serialized attribute. To ensure that a save took place, we had to call will_change!:

def survey_content_date=( survey_content_params )
  self.survey_content.data_will_change!
  ...

Again, substitute the name of your object’s parameter for data.

Go version something!

If you’re looking for the plugin, be sure to download acts_as_versioned from github and not Rick’s site– he’s moved from SVN to Git, and github has the latest version.

The 48 Hour Application Challenge

Posted May 20th, 2008 in General Tech & Development, Ruby on Rails | Permanent Link

I was inspired this week by a post on the Rails Google Group by Melvin at 48hourlaunch.wordpress.com, who was asking for suggestions on a software problem to tackle with 48 hours of effort.

My magic notebook...

I’ve got what I call a “magic notebook”– several, to be precise– full of app ideas in various stages of ideation. So I decided to take on this challenge as well and see what I can do with one of them.

Like Mel, I’m taking on a project that meets one of my immediate needs and is well within my area of expertise. So I’ll be creating something that is useful to me and also has a broader market appeal.

One of my defining characteristics is the inability to do things halfway; unfortunately, this can sometimes manifest as “analysis paralysis”, overengineering, etc. Although Rails inherently short-circuits a lot of these tendencies (as does my business partner, for that matter!) the strict time limit I’m putting on myself makes this something of a competition between completion and perfection. At worst, I will move one of my unrealized applications one step closer to market, and hopefully learn something about my internal process along the way.

So what’s the big project going to be? Can’t talk about that yet, but if all goes according to plan there should be something public in within the next 30 days (48 hours / 2 hours per day = 24 days). I’ll also be posting notes about my progress as I go.


File_column bug with case sensitivity

Posted March 4th, 2008 in Ruby on Rails | Permanent Link

After banging my head against a wall wondering why certain image uploads would silently fail, while others succeeded, I stumbled across a file_column bug (and yes, I reported it).

If you have the following in your model:

validates_file_format_of :image, :in => ["bmp", "gif", "jpg", "pdf", "png"]

And you upload a file with one of these extensions in a different case, for example “SAMPLE.JPG”, the file format validation fails.

Workaround (non-DRY): add case variations, like this:

validates_file_format_of :image, :in => ["bmp", "gif", "jpg", "pdf", "png", "BMP", "GIF", "JPG", "PDF", "PNG"]

Grr.

Captcha Gotcha

Posted February 23rd, 2008 in Ruby on Rails | Permanent Link

While implementing Sur Max’s excellent Simple Captcha plugin, I ran into a very frustrating issue. No matter what was entered, the valid_with_captcha? method always returned false.

After some digging I finally realized that there was a conflict with restful_authentication. The resolution is simple, luckily. You just need to add the following to your user controller:

attr_accessible :captcha, :captcha_key

That did it for me! Hopefully this will help someone else as well.