Основано на реальных событиях, произошедших(происходящих) с реальными людьми.
Если вы работаете с унаследованными базами данных, у вас не всегда есть возможность менять имена полей, когда поля начинают конфликтовать с Ruby on Rails. Самый простой пример, это поле с именем 'class' в одной из ваших таблиц. Рельсам это действительно не нравится. Это как теща, которой не нравится твоя новая прическа, и она обращает на это внимание при любой возможности.
В виду отсутствия драматургического таланта, переводчик не смог, породить более яркую метафору, создающую общую ассоциацию, в противоположность частной
# попробуем использовать плохо обозванный атрибут
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
=> #<User id: nil, name: nil, class: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p0 > u.class = '1995'
NoMethodError: undefined method `private_method_defined?' for nil:NilClass
Как и вышеупомянутая теща, ваши проблемы неизбежны, пока прическа не будет исправлена.
К счастью, Брайан Джонс решил эту проблему для нас с его gem safe_attributes. Rails автоматически создает ацессоры ( геттеры и сеттеры ) для каждого атрибута в таблице модели ActiveRecord. Попытка переопределения Рельсами важных методов таких, как «class» это то, что доставляет нам проблемы. Safe_attributes исключает создание любых атрибутов с опасными именами.
Достаточно сделать следующее:
# app/models/user.rb
class User < ActiveRecord::Base
bad_attribute_names :class
end
После добавления gem-а в bundle, передайте в bad_attribute_names список имен полей нарушителей, и это освободить Rails от попытка генерировать методы-ацессоры для них. Теперь все работает, но без этих ацессоров. Давайте попробуем получить/присвоить наш атрибут :class:
ruby-1.9.2-p0 > u = User.new
=> #<User id: nil, name: nil, class: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p0 > u.class = '1995'
=> "1995"
ruby-1.9.2-p0 > u
=> #<User id: nil, name: nil, class: "1995", created_at: nil, updated_at: nil>
ruby-1.9.2-p0 > u.class
=> User(id: integer, name: string, class: string, created_at: datetime, updated_at: datetime)
Сеттер работает (я предполагаю, что он был создан еще и потому, что не было ранее существовавшего метода 'class'=), и мы можем убедиться, что значение атрибута присвоено верно. Но вызов геттера по умолчанию вызывает… ну, поведение по умолчанию.
Дело в том, что можно всегда использовать атрибут в контексте hash (ассоциированный массив, далее хэш, прим. переводчика). Вы можете передать в объект хэш атрибутов ключ/значение, и это будет работать. Это означает, что ваш контроллер при создании и обновлении не придется менять.
Такие методы как new, create, update_attribute, update_attributes и т.д. будут нормально работать.
Если вы хотите только присвоить значение (чтобы избежать немедленного сохранение, для примера), сделайте следующим образом.
ruby-1.9.2-p0 > u[:class] = '1996'
=> "1996"
ruby-1.9.2-p0 > u
=> #<User id: nil, name: nil, class: "1996", created_at: nil, updated_at: nil>
Вообще, вы все еще можете устанавливать значение атрибутов напрямую, вместо использования генерированных рельсами ацессоров. Но мы все еще в одном шаге от окончательного решения. Мы хотим обращаться к этому атрибуту, как к остальным, а это требует от нас организации нормального набора методов доступа (геттеров и сеттеров). Одна из причин сделать это в том, что нам будут доступны стандартные валидации этого атрибута.
Можно добавить ацессоры, как в этом примере:
# add to app/models/user.rb
def class_name= value
self[:class] = value
end
def class_name
self[:class]
end
Мы объявляем ацессор 'class_name', и теперь мы можем использовать его ге угодно вместо оригинального имени атрибута. Мы можем использовать его в формах:
<%= f.text_field :class_name %>
Или в валидаторах:
validates_presence_of :class_name
Или когда создаем новый объект:
User.create :class_name => 'class of 1995'
Если вы скачали код, то эти дополнения test-driven, имеется в виду то, что я писал тесты для этих методов перед написанием самих методов, чтобы убедиться в том, что эти методы работают подобающим образом. Призываю вас делать тоже самое.
Удачи!
Автор: Renius