SafeAttributes

By default Rails/ActiveRecord 3 creates attribute accessors for all database table columns in each model. Columns with specific names cause errors because they result in ActiveRecord redefining a key method within either Ruby or ActiveRecord in an incompatible way. A classic example is any table with a column named ‘class’, though there are other possible examples. Put simply, this gem makes it easier to support a legacy database schema with ActiveRecord.

Using this gem enhances ActiveRecord to change the default behavior for the creation of attribute accessors. Instance methods in ActiveRecord::Base, except for ‘id’, are combined into a list. This list is checked before the creation of two types of attribute accessors: attribute() and attribute=().

You can add to this list by calling bad_attribute_names with a list of method names you do not want generated. Rails generates additional methods that this module does not prevent the creation of:

  • attribute_before_type_cast

  • attribute_changed?

  • attribute_was

  • attribute_will_change!

  • attribute?

These largely should not run afoul of Ruby or ActiveRecord in most cases.

Accessing Attributes

To access an attribute in ActiveRecord without its normal getter or setter you can use a couple of different approaches.

  • model_instance # works as both getter and setter like a hash

  • model_instance.read_attribute(‘attribute’)

  • model_instance.write_attribute(‘attribute’, value)

You can read more about reserved words and magic field names on Rails’ wiki pages.

Validations

By including safe_attributes, an instance method read_attribute_for_validation is defined in a way that will work for all attributes instead of the default implementation that relies upon the default accessors. In other words, ‘validates_presence_of :class’ will work as of version 1.0.3.

Installing

gem install safe_attributes

If you do not have the newest version of ActiveRecord, rubygems will attempt to install it for you. This can result in an error like below.

ERROR:  Error installing safe_attributes:
  activemodel requires activesupport (= 3.0.3, runtime)

You can use this gem with activerecord and activesupport >= 3.0.

If you already have an appropriate version of activerecord and activesupport installed then use –ignore-dependencies to avoid this error. If you have the latest version already then the gem should install without issue using the recommended approach above.

Using

Ruby

This gem has been tested with:

  • Ruby Enterprise Edition 1.8.7 2012-02-08

  • Ruby 1.9.3p374

  • JRuby 1.7.2

Rails

Add safe_attributes to your Gemfile.

gem 'safe_attributes'

SafeAttributes is included into ActiveRecord::Base automatically. While nothing else should be necessary, you can still add to the list of bad attributes if you find it necessary. This list is a list of method names not to generate.

class MyModel < ActiveRecord::Base
  bad_attribute_names :my_attr
  validates_presence_of :my_attr
end

If you do not want SafeAttributes to be automatically included into ActiveRecord::Base, then instead add this to your Gemfile.

gem 'safe_attributes', :require => 'safe_attributes/base'

You will then need to include the SafeAttributes::Base module into any model you wish to have this functionality.

Outside of Rails

require 'safe_attributes/base'
class MyModel < ActiveRecord::Base
  include SafeAttributes::Base
end

Caveats

It is virtually impossible to have a column named ‘attribute’ in your schema when using ActiveRecord. After spending some time trying to make it work I’ve come to the conclusion the only way to support this will be to change the tactic I’m using entirely, and likely to get the developers of ActiveRecord to agree to a patch.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010,2011,2012,2013 C. Brian Jones. See LICENSE for details.

Thanks

  • Jaime Bellmyer - kconrails.com

  • James Brennan

  • Billy Watson

  • Jean Boussier