How to Avoid and Infinite Loop with before_save and after_save

I have an entity that looks something like this:

journal
author_first_name
author_last_name
journal_name
journal_email_address

where
journal_name = author_first_name + ' ' + author_last_name
journal_email_address = journal_name + '-' id

I had journal_name and journal_email_address implemented as attributes on the Journal model, but the time came along where, for various reasons, it has become valuable to also store that information in the database. So, what I thought I’d do, in app/models/journal.rb:
def make_journal_name
self.journal_name = self.author_first_name + ' ' + self.author_last_name
end

def make_inbound_email
self.inbound_email = self.journal_name.gsub(/[^a-zA-Z0-9]/, '') + '-' + self.id.to_s
end
def before_save
make_journal_name
make_inbound_email
end

which works fine in the case of an update of an existing record. However, it does not work in the case of a creation of a new record because before saving self.id does not exist. The result was my e-mail addresses are looking like “SallyJones-” as opposed to “SallyJones-64”.

Okay, I thought, “since I need an ID to work with I’ll move this to after_save,” like so:
def after_save
make_journal_name
make_inbound_email
self.save
end

but that creates an infinite loop.

The solution to this problem is to use an undocumented method: update_without_callbacks.
def after_save
make_journal_name
make_inbound_email
self.update_without_callbacks
end

update_without_callbacks is an instance method on ActiveRecord::Base, but for some reason it’s undocumented. In fact, it explicitly has :nodoc set in the source, although I can’t figure out why. To me, it seems that having more documented is better than having less.

The Rails Way

A big part of using Rails is understanding that if you conform to "The Rails Way" things are much easier. Of course, The Rails Way might not work for your project, and in that case, Rails might not be the right framework choice. But, you have to learn The Rails Way in order to know.

I can't claim to explain The Rails Way completely, but I've been helping a new (to Rails) developer on my team get up and running, so I've been having to coach him to see The Rails Way.

I started by talking about "entities". An entity is a thing like a "widget"...some object in our application domain that we need to represent. By convention, the names of the database table, unit tests, models, controllers, views, layouts and helpers must always coordinated in Rails to the name of the entity. So, with our "widget" entity, this is how they should be:


  • DB Table: widgets
  • Model: app/models/widget.rb
  • Controller: app/controllers/widgets_controller.rb
  • View: app/views/widget/...
  • Layout: app/views/layouts/widgets.rhtml
  • Helper: app/helpers/widgets_helper.rb

The beautiful thing about Rails is that by using generators, this gets managed for you. Here’s the approach I always take when creating an entity. This can be done through an IDE (such as RadRails, which is what I use) or on the command line...I’ll use the command line here:

  • cd path/to/rails/app
  • ruby ./script/generate model Widget which will generate your model file and your migration file with the proper table name.
  • Open up the migration, add the appropriate columns and save.
  • rake db:migrate to apply your migration to the DB.
  • ruby ./script/generate scaffold Widget which will create your controller, views, layout and helper.

Generators create a starting point to start editing, and it also gets all the file names correct. Let’s talk about the editing that needs to happen. Personally, I always do my editing in this order:

  1. Migration
  2. Model
  3. Controllers
  4. Views
  5. Layouts

This makes the most sense to me because I’m starting with the raw data, and working my way forward to what the user sees on the web page.

A little background on controllers (a good article to read is this: http://www.softiesonrails.com/search?q=rest+101): Your standard CRUD controller has 8 actions:

  • Create (Create): /widgets/create
  • Show (Read): /widgets/show/:id
  • Update (Update): /widgets/update/:id
  • Destroy (Delete): /widgets/destroy/:id
  • New (precursor to create): /widgets/new
  • Edit (precursor to update): /widgets/edit/:id
  • List (lists all entities): /widgets/list
  • Index (the default action): /widgets

When, you generate your scaffold, it’s going to automatically generate the 8 actions above. When designing entities if you have methods that don't fit within one of those actions then it's typically an indication that your entity model could our should be designed differently.

(A great example of how it's all about entity design is the login and logout actions for a User. I was thinking the other day about where the login and logout methods belong because they make sense on a User, but they aren't one of the above eight actions. Of course, I was thinking about it all wrong: login and logout are really the Create and Destroy methods on the Session entity, and Session entities belong to a User.)

Finally, a note in regard to the relationships between views, controllers and models in the context of validation. The typical user pattern in relation to creating an entity is:

  • User visits /widgets/new page
  • Enters data, click submit.
  • /widgets/new POSTs to Widgets#create
  • Widgets#create puts the data in the DB, then (if you look at the scaffold generated controller)

    • If success, calls redirect_to
    • If fails, calls render :action => :new

Why in one case does it redirect, and the other does it render? It all has to do with a little method call that should appear in your new widgets view, i.e. /widgets/new:
<%= error_messages_for 'widget' %>

See, a model has the capability to validate it’s attributes (e.g. lookup validates_presence_of in the Rails docs for examples of these capabilities). When you call Object#save the validation routines are executed and if the model doesn’t validate (i.e. Object#save returns false) we render the new action again. error_messages_for looks up the failures and automagically renders error messaging.

The beauty of generators is that script/generate scaffold Widget sets all of this up for you.

The Problem with Rails Plugins

The other day Mark asked if I was using active_scaffold. The answer is that I have a perfect use for it, and I installed it, and it works perfectly for what I need it for. However, it also overrides some core Rails functions, and when I execute functionality in my portion of the app the active_scaffold version of those functions are throwing exceptions. For example:

class ActionController::Routing::RouteSet 
def generate_with_nil_id_awareness(*args)
args[0].delete(:id) if args[0][:id].nil?
generate_without_nil_id_awareness(*args)
end
alias_method_chain :generate, :nil_id_awareness
end

Now, to me, ActionController::Routing::RouteSet seems like a pretty core function in Rails. Of course, it may be the case that I’ve done something wrong and their updated version is catching that error. (You can read more on that here: http://groups.google.com/group/activescaffold/browse_thread/thread/1c4e2f30e8b00ac6/309665c0a85d65c8).

However, my thinking is that I don’t need some plugin f’ing with my app. In fact, this points to a major issue with plugins…they’re not these self-contained units, i.e. components...but, rather, they are mixed in with the core Rails framework and our apps. This means we must think very carefully about which plugins we’ll use because, effectively, we need to know them as well as we know our own code. In other words, they are not black boxes...they are just code we import into our app that somebody else wrote (and is kind enough to share). For example, active_scaffold installs 138 files, and who knows how many kLOCs of code, and who knows what funky stuff they’ve done.

So, long story short, I’m not using active_scaffold because I don’t trust that there isn't come code in there that's gonna mess up my app, and it's too much code for me to review and become familiar with. Any ideas on an alternative?

I’ve looked at Streamlined, Hobo Scaffolding Extensions. According to the docs, Streamlined doesn’t do nested records editing which I require, Scaffolding Extensions handles nested record editing, but isn't the best UX, and Hobo has the same problem as Radiant CMS...I have to put my app into theirs, not their app into mine. (However, both Radiant and Hobo seem like nice platforms for the right project, just not the one I'm currently working on.)

What does "idiomatic Ruby" mean?

From a chat with Mark today, in regard to reading a post where I used the word "idiomatic":

(10:10:42) Mark: idiomatic, what a word
(10:11:12) Scott Porad: yes, i don't even know what it means...but people
say it when they mean, "there's a more clever way to write this in ruby".
(10:11:26) Mark: Peculiar to or characteristic of a given language.
(10:12:05) Mark: nice, one of the descriptions of idiomatic, uses idiomatic
to describe it: Characterized by proficient use of idiomatic expressions

Acts As Authenticated and Reset Password

Using Acts as Authenticated, imagine this use case:


  • User forgets password...has reset code sent to their e-mail.
  • User receives e-mail, clicks link to reset password.
  • User arrives on reset password page and thinks, "hmmm...I think I'll go visit the homepage instead".
  • User types http://www.yoursite.com/ in their browser (or clicks a link the header, or whatever).

What happens is that when the user clicks the link in the email it takes them to /account/reset_password which looks up their account by the reset code on the link (which was sent in the e-mail) and it logs them in. So, when they decide, "hmmm...I think I'll go visit the homepage instead," the user is now logged in, but has not reset their password.

To me, that seems to present a problem. So, I added a before_filter to my ApplicationController that verifies that all logged in users must have a nil password reset code otherwise they are redirected to /account/change_password. (The password reset code isn't cleared until after the user has successfully changed their password.)

In the ApplicationController I added:
before_filter :ensure_password_reset
and
module AuthenticatedSystem
def ensure_password_reset
redirect_to :controller => :account, :action => :change_password unless reset_password_has_been_changed
end

def reset_password_has_been_changed
if logged_in?
if current_user.password_reset_code.nil?
return true
else
return false
end
else
true
end
end
end

I'm certain that there is a more idiomatic Ruby way to write reset_password_has_been_changed, but I couldn't think of it.

Fixing a Blank Password Bug in Acts As Authenticated

I came across a bug in Acts As Authenticated when users tried to change their passwords. If they submitted the form with nothing in the new or confirm new password text field, then no error was thrown, but the password wasn't changed either.

The problem was in the :if clause on the validates_presence_of validation, password_required?. It was causing the validation not to occur.

    def password_required?
crypted_password.blank? || !password.blank?
end

This code is saying that a password is required in two situations:

  • when the crypted_password is blank -- that situation happens when creating a new user, OR
  • when the class instance variable 'password' is not blank -- this can only happen when the variable has been accessed, such as when attempting to update a password.

The bug is in the test for !password.blank? because it's assuming that the user will enter something as a new password. To resolve that, I changed the code to this:
    def password_required?
crypted_password.blank? || !password.nil?
end

and now the validation is called (and fails) properly. More about this issue can be found in the last few posts on this page.

How To Make the Debugger Work with RadRails

I've finally got the debugger working with Aptana's RadRails.

I've had the debugger working for awhile, but I was experiencing a problem where the current line, i.e. the current execution line, wasn't being highlighted. Actually, the source file of the current line wasn't being even opened.

After trolling around various messages boards, etc., I learned that the bug has been fixed in the latest beta version of RadRails. Here’s a post on how to do that: http://www.aptana.com/forums/viewtopic.php?t=2939

Personally, I had to manually add the update site in order to get it to work, although you might not have that experience. Of course, it took me awhile to figure out how to do this...here you go:


  • Go to Help > Software Updates > Find and Install

  • Select "Search for new features to install" and click "Next"

  • Click the button on the right for "New Remote Site"

  • Enter:

    • Name: RadRails Beta

    • URL: http://update.aptana.com/update/rails/beta/3.2/site.xml


  • Click 'OK'

  • Check the box next to "RadRails Beta"

  • Click the "Finish" button.


Now the update should download and install.

Where to put/store/save/keep your modules and files containing modules?

To a Rails newbie, sometimes the simplest things are hard to figure out. Here's what you need to know:


When you create a module, you save it to your lib/ directory.

All the modules in the lib/ directory are auto-loaded by Rails. That is, Rails does something like this:
for file in lib/
require file
end
Also, I think there is some magic to this, but I'm not 100% sure. If you have a module called MiscUtils then your file needs to be named misc_utils.rb in order for Rails to auto-load it.

Additionally, Ruby does this super-sweet thing where you can re-open any class or module and add to it at any time. In other words, you can declare a class or module in two locations, and Ruby will merge them together and treat them as one class. My experience has been that when you do this, then you need to explicitly require the files even if they're in the lib directory.

What is that hyphen right before the closing of an ERb block?

Periodically, I see some ERb code in a view like this:


<% form_tag '/posts' do -%>

<%= submit_tag 'Save' %>
<% end -%>

In fact, that code was culled right from the Rails docs at for form_tag

As you can see, in those two ERb tags, there is this syntax at close of the tag:
-%>
Today I was wondering what the hyphen (-) was for before the percentage symbol (%). It turns out that the hyphen means that when the HTML output is generated there is no line return after the close of the ERb tag.

For example, the ERb code above produces this HTML:
<form action="/posts" method="post">    <div><input name="commit" type="submit" value="Save" /></div>
</form>

whereas this ERb code:
<% form_tag '/posts' do %>    
<%= submit_tag 'Save' %>
<% end %>

produces this HTML:
<form action="/posts" method="post">
<div><input name="commit" type="submit" value="Save" /></div>
</form>

How Mixins, Include and Extend Work

I was a little puzzled tonight by this chunk of code that I found in the acts_as_authenticated plug-in module:

    def self.included(base)
base.send :helper_method, :current_user, :logged_in?
end

It comes down to some Ruby magic: when a module is included by a class the self.included method on the module is called. Likewise, when a class extends a module the self.extended method is called.

"base" is the class that includes/extends the module. "send" is calling the method ":helper_method" and passing ":current_user" and ":logged_in?" as parameters.

Special thanks to Juixe for an excellent article that helped me figure this out.