Buildin’ the Blog: Part 5 – Refactoring: Part 2
In my last Buildin’ the Blog article, I wrote about how I cleaned up my comments controller by moving all of the processing to the model. In this follow-up article, I’m going to write about how to access properties that aren’t part of your model object (aka table).
Take for example, a simple comments database table. It might look like this:
| id | full_name | email_address | url | body |
As you can see this is pretty straightforward, a single comment is comprised of a full_name, email_address, and the body. These attributes can easily be checked and processed in our comment model. Here comes the tricky part.
Take for instance my comment form below:

Name, email address, URL, and the body of the comment can all be mapped to database table fields. Unfortunately, Remember Me and Subscribe Via Email cannot and should not.
A beginner in Rails might write something like the following in their controller code:
if params[:subscribe]
#create a subscription record here
end
This code works but it leaves a lot of logic in the controller. The controller should be fairly lightweight, “I accept input from the user pass it to the model and then I pass it back to the browser/user.”
In order to get non-database bound fields to the model we use Ruby’s attr_accessor declaration (we could use attr_writer too to only write attributes and not read them).
For example:
class Comment < ActiveRecord::Base
attr_accessor :subscribe
end
This allows me to create a form like the following:
<% form_for @comment do |f| %>
<%= f.text_field :full_name %>
...snip...
<%= f.check_box :subscribe %>
<%= f.submit %>
Once this is done, I can check what the value of the checkbox was set to in the model in a callback (let’s say either a before_save or an after_save):
def before_save
if self.subscribe == 1
#create a subscription record here
end
end
It’s that simple. Now all of my logic can be done in the model.
With that out of the way, here is something to watch out for. Let’s say that you have a comments table that looks like this:
| id | full_name | email_address | url | body | is_admin |
Obviously, the is_admin attribute should be set by the application and not by the user but unless you protect your model, the user can set any attribute he wants. Let’s say that you have a simple comments form with full_name, email_address, url, and body. The is_admin attribute is not displayed as a form field.
A malicious user could come by and change the form by adding the following or by submitting to the comment form using cURL. Here is an example:
<input type="checkbox" name="comment[is_admin] checked="checked" />
By crafting this form and sending it, the user automatically becomes an admin!
One way to protect your application from this type of malicious activity is by protecting fields that should never be set by the user behind the attr_protected declaration. For example:
class Comment < ActiveRecord::Base
attr_protected :is_admin
end
This will stop malicious users from trying to set attributes that should never be set by the user.