Inherited Databases in Rails 3
- Tutorial
- Recovery mode
Based on real events that have occurred (occurring) with real people.
If you work with legacy databases, you may not always be able to change field names when fields begin to conflict with Ruby on Rails. The simplest example is a field called 'class' in one of your tables. Rails really don't like this. It's like a mother-in-law who doesn't like your new hairstyle, and she pays attention to it whenever possible.

Like the above mother-in-law, your problems are inevitable until the hairstyle is fixed.
Fortunately, Brian Jones solved this problem for us with his gem safe_attributes . Rails automatically creates processors (getters and setters) for each attribute in the ActiveRecord model table. Trying to override Rails with important methods such as "class" is what causes us problems. Safe_attributes excludes the creation of any attributes with dangerous names.
It is enough to do the following:
After adding the gem to the bundle, pass the list of intruder field names to bad_attribute_names, and this will free Rails from trying to generate processor methods for them. Now everything works, but without these processors. Let's try to get / assign our attribute: class:
The setter works (I assume that it was created also because there was no pre-existing 'class' = method), and we can make sure that the attribute value is assigned correctly. But calling the default getter causes ... well, the default behavior.
The fact is that you can always use an attribute in the context of hash (associated array, then hash, approx. Translator). You can pass a key / value to the attribute hash object, and this will work. This means that your controller does not have to be changed when creating and updating.
Methods like new, create, update_attribute, update_attributes, etc. will work fine.
If you only want to assign a value (to avoid immediate saving, for example), do as follows.
In general, you can still set the value of attributes directly, instead of using rail-generated processors. But we are still one step away from the final decision. We want to refer to this attribute as the rest, and this requires us to organize a normal set of access methods (getters and setters). One of the reasons to do this is that standard validations of this attribute will be available to us.
You can add processors, as in this example:
We declare the processor 'class_name', and now we can use it anywhere instead of the original attribute name. We can use it in forms:
Or in the validators:
Or when we create a new object:
If you downloaded the code, then these add-ons are test-driven, meaning that I wrote tests for these methods before writing the methods themselves, to make sure that these methods work properly. I urge you to do the same.
Good luck
Original available

In view of the lack of dramatic talent, the translator was unable to generate a more vivid metaphor that creates a general association, as opposed to a private
# попробуем использовать плохо обозванный атрибут
ruby-1.9.2-p0 > u = User.new :class => '1995'
NoMethodError: undefined method `columns_hash' for nil:NilClass
#пытаемся присвоить другому атрибуту, виновному только в том, что он родился не в том месте.
ruby-1.9.2-p0 > u = User.new :name
NoMethodError: undefined method `has_key?' for nil:NilClass
# пробуем присвоить атрибут последовательно
ruby-1.9.2-p0 > u = User.new
=> #
ruby-1.9.2-p0 > u.class = '1995'
NoMethodError: undefined method `private_method_defined?' for nil:NilClass
Like the above mother-in-law, your problems are inevitable until the hairstyle is fixed.
Fortunately, Brian Jones solved this problem for us with his gem safe_attributes . Rails automatically creates processors (getters and setters) for each attribute in the ActiveRecord model table. Trying to override Rails with important methods such as "class" is what causes us problems. Safe_attributes excludes the creation of any attributes with dangerous names.
It is enough to do the following:
# app/models/user.rb
class User < ActiveRecord::Base
bad_attribute_names :class
end
After adding the gem to the bundle, pass the list of intruder field names to bad_attribute_names, and this will free Rails from trying to generate processor methods for them. Now everything works, but without these processors. Let's try to get / assign our attribute: class:
ruby-1.9.2-p0 > u = User.new
=> #
ruby-1.9.2-p0 > u.class = '1995'
=> "1995"
ruby-1.9.2-p0 > u
=> #
ruby-1.9.2-p0 > u.class
=> User(id: integer, name: string, class: string, created_at: datetime, updated_at: datetime)
The setter works (I assume that it was created also because there was no pre-existing 'class' = method), and we can make sure that the attribute value is assigned correctly. But calling the default getter causes ... well, the default behavior.
The fact is that you can always use an attribute in the context of hash (associated array, then hash, approx. Translator). You can pass a key / value to the attribute hash object, and this will work. This means that your controller does not have to be changed when creating and updating.
Methods like new, create, update_attribute, update_attributes, etc. will work fine.
If you only want to assign a value (to avoid immediate saving, for example), do as follows.
ruby-1.9.2-p0 > u[:class] = '1996'
=> "1996"
ruby-1.9.2-p0 > u
=> #
In general, you can still set the value of attributes directly, instead of using rail-generated processors. But we are still one step away from the final decision. We want to refer to this attribute as the rest, and this requires us to organize a normal set of access methods (getters and setters). One of the reasons to do this is that standard validations of this attribute will be available to us.
You can add processors, as in this example:
# add to app/models/user.rb
def class_name= value
self[:class] = value
end
def class_name
self[:class]
end
We declare the processor 'class_name', and now we can use it anywhere instead of the original attribute name. We can use it in forms:
<%= f.text_field :class_name %>
Or in the validators:
validates_presence_of :class_name
Or when we create a new object:
User.create :class_name => 'class of 1995'
If you downloaded the code, then these add-ons are test-driven, meaning that I wrote tests for these methods before writing the methods themselves, to make sure that these methods work properly. I urge you to do the same.
Good luck
Original available