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

в 9:48, , рубрики: class, ruby on rails, метки: ,

Основано на реальных событиях, произошедших(происходящих) с реальными людьми.
Унаследованные базы данных в 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, имеется в виду то, что я писал тесты для этих методов перед написанием самих методов, чтобы убедиться в том, что эти методы работают подобающим образом. Призываю вас делать тоже самое.

Удачи!

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

Автор: Renius

Поделиться

* - обязательные к заполнению поля