Унаследованные базы данных в Rails 3

от автора

Основано на реальных событиях, произошедших(происходящих) с реальными людьми.

Если вы работаете с унаследованными базами данных, у вас не всегда есть возможность менять имена полей, когда поля начинают конфликтовать с 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, имеется в виду то, что я писал тесты для этих методов перед написанием самих методов, чтобы убедиться в том, что эти методы работают подобающим образом. Призываю вас делать тоже самое.

Удачи!

Оригинал доступен

ссылка на оригинал статьи http://habrahabr.ru/post/155215/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *