<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1748784436382377483</id><updated>2011-11-27T16:31:44.600-08:00</updated><title type='text'>Vladimir Bystrov's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://vbystrov.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1748784436382377483/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://vbystrov.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Vladimir Bystrov</name><uri>http://www.blogger.com/profile/17842768975170914842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/__yXKdrBIzEA/SJbn-6uMGeI/AAAAAAAAAAM/1g8ios5OFfs/S220/avatar_vova_new.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1748784436382377483.post-3187816019353518396</id><published>2009-04-02T08:38:00.001-07:00</published><updated>2009-04-07T05:21:35.920-07:00</updated><title type='text'>Single Table Inheritance in Ruby on Rails</title><content type='html'>Недавно, при попытке портирования одного приложения с Java на RoR столкнулся с проблемой "отображения иерархии классов на реляционную модель". Известное дело в Java или .NET я бы использовал Hibernate/NHibernate и Single Table Inheritance или STI (в данном случае это наиболее подходящий из 3 представленных способов). На мое счастье, оказывается ActiveRecord в RoR, также поддерживает STI. Итак, приступим.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Предметная область&lt;/h3&gt;&lt;br /&gt;В качестве предметной области я взял модель безопасности приложения (т.е. ее часть). У нас есть базовая сущность Principal(name, description), и три конкретные сущности наследника: User(login, passwordHash, lastLoginDate, expirationDate, accountDisabled), Role, Group. Для данного примера я решил не усложнять модель связями между User, Role и Group.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Реализация&lt;/h3&gt;&lt;br /&gt;Для реализации STI нам потребуется в таблицу principals добавить зарезервированное поле type и все поля из классов: Principal, User, Role и Group.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Скрипт миграции&lt;/h4&gt;&lt;br /&gt;Привожу скрипт миграции для таблицы principals.&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;class CreatePrincipals &lt; ActiveRecord::Migration&lt;br /&gt;  def self.up&lt;br /&gt;    create_table :principals do |t|&lt;br /&gt;      t.column :name, :string, :limit =&gt; 64&lt;br /&gt;      t.column :description, :string&lt;br /&gt;      t.column :account_disabled, :boolean&lt;br /&gt;      t.column :last_login_date, :date&lt;br /&gt;      t.column :expiration_date, :date&lt;br /&gt;      t.column :type, :string, :limit =&gt; 48&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def self.down&lt;br /&gt;    drop_table :principals&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Модель&lt;/h4&gt;&lt;br /&gt;Сущность Principal&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;class Principal &lt; ActiveRecord::Base&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Сущность User&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;class User &lt; Principal&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Сущность Role&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;class Role &lt; Principal&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Сущность Group&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;class Group &lt; Principal&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Как видим в объявлениях классов модели нет ничего особенного. Обо всем остальном позаботится ActiveRecord.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Примеры работы с моделью&lt;/h3&gt;&lt;br /&gt;Тепрь можно создавать наши объекты, сохранять в базу данных и выполнять запросы. В качестве примеров приведу тесты модели (они простые, так что прошу не бить ногами). Примеры только для Role и Group, с User-ом аналогично.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Фикстуры для principals&lt;/h4&gt;&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;users_role:&lt;br /&gt;  id: 100&lt;br /&gt;  name: users&lt;br /&gt;  description: system users&lt;br /&gt;  type: Role&lt;br /&gt;&lt;br /&gt;testers_group:&lt;br /&gt;  id: 101&lt;br /&gt;  name: testers&lt;br /&gt;  description: testers group&lt;br /&gt;  type: Group&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Тест для сущности Role&lt;/h4&gt;&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;require 'test_helper'&lt;br /&gt;&lt;br /&gt;class RoleTest &lt; ActiveSupport::TestCase&lt;br /&gt;  fixtures :principals&lt;br /&gt;&lt;br /&gt;  test "role_has_type_eq_role_after_save" do&lt;br /&gt;    role = Role.new(:name=&gt;"admins", &lt;br /&gt;                    :description=&gt;"system administrators")&lt;br /&gt;    role.save&lt;br /&gt;&lt;br /&gt;    assert role.id&lt;br /&gt;    assert_equal "Role", role.type&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  test "users_role_exists_in_db" do&lt;br /&gt;    role = Role.find_by_id(100);&lt;br /&gt;&lt;br /&gt;    assert_not_nil role&lt;br /&gt;    assert_equal 100, role.id&lt;br /&gt;    assert_equal "users", role.name&lt;br /&gt;    assert_equal "Role", role.type&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Тест для сущности Group&lt;/h4&gt;&lt;br /&gt;&lt;pre style="border: 1px dotted gray; padding: 3px;"&gt;&lt;br /&gt;require 'test_helper'&lt;br /&gt;&lt;br /&gt;class GroupTest &lt; ActiveSupport::TestCase&lt;br /&gt;  fixtures :principals&lt;br /&gt;&lt;br /&gt;  test "group_has_type_eq_group_after_save" do&lt;br /&gt;    group = Group.new(:name=&gt;"developers", &lt;br /&gt;                      :description=&gt;"developers group")&lt;br /&gt;    group.save&lt;br /&gt;&lt;br /&gt;    assert group.id&lt;br /&gt;    assert_equal "Group", group.type&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  test "users_group_exists_in_db" do&lt;br /&gt;    group = Group.find_by_id(101);&lt;br /&gt;&lt;br /&gt;    assert_not_nil group&lt;br /&gt;    assert_equal 101, group.id&lt;br /&gt;    assert_equal "testers", group.name&lt;br /&gt;    assert_equal "Group", group.type&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Как видим все достаточно прозрачно.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Ложка дегтя&lt;/h3&gt;&lt;br /&gt;Как уже можно было догадаться есть и обратная сторона медали. Контроль соответствия набора значащих полей конкретной сущности лежит целиком на совести программиста. Для среды ActiveRecord значение имеет лишь колонка type. Так, что я вполне могу создать сущность Role с данными User-а. А это не очень хорошо отразится на отношении ко мне&lt;br /&gt;моих коллег. Поэтому в Rails очень важно покрытие кода функциональными тестами.&lt;br /&gt;&lt;br /&gt;Жду отзывов, исправлений и предложений.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1748784436382377483-3187816019353518396?l=vbystrov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vbystrov.blogspot.com/feeds/3187816019353518396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1748784436382377483&amp;postID=3187816019353518396' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1748784436382377483/posts/default/3187816019353518396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1748784436382377483/posts/default/3187816019353518396'/><link rel='alternate' type='text/html' href='http://vbystrov.blogspot.com/2009/04/single-table-inheritance-in-ruby-on.html' title='Single Table Inheritance in Ruby on Rails'/><author><name>Vladimir Bystrov</name><uri>http://www.blogger.com/profile/17842768975170914842</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/__yXKdrBIzEA/SJbn-6uMGeI/AAAAAAAAAAM/1g8ios5OFfs/S220/avatar_vova_new.JPG'/></author><thr:total>0</thr:total></entry></feed>
