Ruby On Rails - best practices
Прошел где-то месяц, после того как я начал изучать Ruby On Rails. Так как Livestreet потихоньку движется в сторону framework'a (чему я сильно рад), хочу поделиться с разработчиками livestreet чего же такого там есть, чего сильно упрощает жизнь при разработке.
Вообще, ruby on rails позиционируется как фреймворк, который максимально упрощает разработку и в то же время структурирует систему.
В то же самое время, livestreet сильно подходит для высоконагруженных проектов (больше чем rails).
Переводится как согласование над конфигурацией. Основная идея — сэкономить время программиста и задать некие стандарты разработки.
Например, у нас есть модель user. Рельсы будут использовать табличку users для нее автоматически (множественное число опредлеяется в движке). Если название таблички отличается, то мы можем написать table_name => :users_table, тем самым задав свое название. То же самое с роутами, шаблонами и еще много чем.
Что это нам дает? Структурируемость кода, экономию времени.
Призван чтобы так же облегчить жизнь программиста и структурировать код.
Сама реализация достаточно обширна. Опишу некоторые плюсы, которые успел подчерпнуть.
Каждая модель по-умолчанию относится к одной табличке (user -> users). Все связи этой модели с другими прописывается в ней же.
Например код
в классе модели user «привязывает» модель comment к модели user.
И у модели user появляется метод comments, который возвращает список комментариев. (используется так: user.comments).
Далее мы можем привязать с другой стороны: В классе модели comments пишем
Теперь у нас есть метод comment.user, который возвращает пользователя. Тут действует принцип COC (о котором я писал выше). По-умолчанию в табличке comments берется поле user_id, но его можно перезадать. Автоматом прописываются связи.
Вообще, к моделям добавляется много дефолтных методов (типа delete, find_by_id итд), среди которых очень часто много скрыто под COC
Существуют спец. скрипты, которые генерируют код автоматически. Например, можно сгенерить контроллер с моделью (создаются автоматом файлы с кодом)
Так же есть такое понятие как scaffolding (строительные леса). Это генератор (скрипт), который генерирует модель, контроллер с базовыми eventами(как в LS) и представлениями с формами (CRUD):
* список элементов
* просмотр одного элемента
* редактирование
* удаление
Потом конечно же всё 100 раз заменяется, но для быстрого внедрения сущности самое оно.
При разработке если нам нужно добавить новые поля/табличку, или удалить чего-нибудь, с помощью генератора, мы создаем т.н. миграцию. Это файл, в котором пишутся изменения «туда» и «сюда»
Напр:
После этого, мы запускаем скрипт, который генерит нам sql-запрос под конкретную базу (тут понятно, что он может сгенерить платформонезависимый скрипт).
Кстати, rails не привязан к mysql. Помогают отвязаться миграции и activerecord, где всё скрыто. Так локально можно разрабатывать на sqlLite, а продакш на oracle.
Хотя это накладывает некоторые ограничения. Например, нет возможности добавлять связи к табличкам.
Думал, что напишу больше, однако времени не хватило. Так что будет вторая часть:)
Параллельно хотел бы разместить небольшую объяву:
Ищу программиста под livestreet, который поможет мне с одним проектом. Проект — сайт про детей, бюджет у проекта не большой — 8 т.р.
Требуется модификация ЛС и модуля «галерея». Это может быть человек, которому просто интересно программировать под ЛС, пусть он даже и не обладает некоторыми серьезными знаниями.
Я могу его подучить и расписать что и как надо программировать:)
По всем вопросам жду в личку.
Вообще, ruby on rails позиционируется как фреймворк, который максимально упрощает разработку и в то же время структурирует систему.
В то же самое время, livestreet сильно подходит для высоконагруженных проектов (больше чем rails).
Convention over configuration
Переводится как согласование над конфигурацией. Основная идея — сэкономить время программиста и задать некие стандарты разработки.
Например, у нас есть модель user. Рельсы будут использовать табличку users для нее автоматически (множественное число опредлеяется в движке). Если название таблички отличается, то мы можем написать table_name => :users_table, тем самым задав свое название. То же самое с роутами, шаблонами и еще много чем.
Что это нам дает? Структурируемость кода, экономию времени.
Active Record
Призван чтобы так же облегчить жизнь программиста и структурировать код.
Сама реализация достаточно обширна. Опишу некоторые плюсы, которые успел подчерпнуть.
Каждая модель по-умолчанию относится к одной табличке (user -> users). Все связи этой модели с другими прописывается в ней же.
Например код
has_many :comments
в классе модели user «привязывает» модель comment к модели user.
И у модели user появляется метод comments, который возвращает список комментариев. (используется так: user.comments).
Далее мы можем привязать с другой стороны: В классе модели comments пишем
belongs_to :user
Теперь у нас есть метод comment.user, который возвращает пользователя. Тут действует принцип COC (о котором я писал выше). По-умолчанию в табличке comments берется поле user_id, но его можно перезадать. Автоматом прописываются связи.
Вообще, к моделям добавляется много дефолтных методов (типа delete, find_by_id итд), среди которых очень часто много скрыто под COC
Генераторы
Существуют спец. скрипты, которые генерируют код автоматически. Например, можно сгенерить контроллер с моделью (создаются автоматом файлы с кодом)
Так же есть такое понятие как scaffolding (строительные леса). Это генератор (скрипт), который генерирует модель, контроллер с базовыми eventами(как в LS) и представлениями с формами (CRUD):
* список элементов
* просмотр одного элемента
* редактирование
* удаление
Потом конечно же всё 100 раз заменяется, но для быстрого внедрения сущности самое оно.
Миграции БД
При разработке если нам нужно добавить новые поля/табличку, или удалить чего-нибудь, с помощью генератора, мы создаем т.н. миграцию. Это файл, в котором пишутся изменения «туда» и «сюда»
Напр:
def self.up
add_column :collages, :status, :string
end
def self.down
remove_column :collages, :status
end
После этого, мы запускаем скрипт, который генерит нам sql-запрос под конкретную базу (тут понятно, что он может сгенерить платформонезависимый скрипт).
Кстати, rails не привязан к mysql. Помогают отвязаться миграции и activerecord, где всё скрыто. Так локально можно разрабатывать на sqlLite, а продакш на oracle.
Хотя это накладывает некоторые ограничения. Например, нет возможности добавлять связи к табличкам.
Думал, что напишу больше, однако времени не хватило. Так что будет вторая часть:)
Параллельно хотел бы разместить небольшую объяву:
Ищу программиста под livestreet, который поможет мне с одним проектом. Проект — сайт про детей, бюджет у проекта не большой — 8 т.р.
Требуется модификация ЛС и модуля «галерея». Это может быть человек, которому просто интересно программировать под ЛС, пусть он даже и не обладает некоторыми серьезными знаниями.
Я могу его подучить и расписать что и как надо программировать:)
По всем вопросам жду в личку.
49 комментариев
Теперь выскажу свое мнение по предложениям:
1. Convention over configuration — вопрос косвенно поднимался в комментариях к топику в о нововведениях. Сейчас система слишком динамично меняется, чтобы говорить о каких-то глобальных Convention, а локально это уже внедряется — например, routing rewrite.
2. Active Record — это только один из паттернов связки реляционнки и логики приложения. В LS используется data mapper, дело стиля, какие выгоды по вашему получит приложения от перехода на AR?
3. Генераторы. Мне тоже раньше эта функциональность импонировала, но долгий опыт работы с фреймворками меня научил — НАСЛЕДОВАНИЕ КЛАССОВ = ЛУЧШИЙ КОДОГЕНЕРАТОР. Главное правильно смонтировать каркас, и не это касается не только CRUD.
Да, стандартный CRUD можно сделать на ROR за минуту-две. Но у меня под ZF есть каркас приложения, на основе которого я реализую CRUD минут за 5. При том, что этот вариант мне нужно будет гораздо меньше «допиливать», и он более функционален.
2. data mapper, насколько я знаю, это вынесение запросов в отдельный класс, который позволяет абстрагироваться от базы данных всё приложение.
В rails Active Record же существенно более сложный механизм (хотя, можно взять только основную идею). По поводу выгод это нужно либо писать отдельный топик, либо почитать мануалы. Я наверное напишу сам…
3. Наследование классов это другое, нежели генерация. Генерировать можно много чего:)
Не стану спорить на тему последнего абзаца, потому что не знаю чего там. Да и не надо, главное подумать нужно ли это ЛС и, если да, то как лучше реализовывать.
Нет. Суть дата мепинга объясню на конкретном примере сущности «User». У вас есть две стороны барикады:
1) приложение, для которого User это «что-то» («нечто»), имеющее определенные характеристики (имя, логин, пароль, ...), определенные права, совершающее определенные действия, и меняющееся при совершении этих действий;
2) база данных, для которой пользователь — это только строка в таблице и несколько смежных ячеек. И все.
Паттерн Data Mapper подразумевает, что для наведения порядка вы — а) имеете класса UserEntity, инкапсулирующий в себе характеристики и действия объекта приложения; б) UserMapper — который может превратить объект UserEntity в строку данных в реляционной базе данных, и наоборот, читая строку(и) базы вернуть вам нормальный UserEntity.
То, что SQL выносятся в отдельный файл, это следствие, а не причина.
Да и вообще, тема топика не в Data Mappere, а в том как сделать ЛС лучше)
В этом-то мой вопрос: как AR сделает LS лучше? :)
Проблема работы уровня Models в LS в том, что нет общей платформы — каждый mapper пишется так или иначе «с нуля». Равно как и каждый новый Entity. По хорошему здесь нужно писать хороший DB-layer со своим объектом row (строка таблицы) и rowset (набор строк) — а в дальнейшем оперировать наследованными от него объектами.
А там дальше начинается та область, где граница между Data Mapping & Active Records становиться очень тонка :)
Я просто показал как бывает и не пропагандирую тот или иной подход. Спорить на эту тему глупо, нужно просто подумать как в данном случае лучше.
В этом есть серьезные неудобства, для того чтобы это понять достаточно написать пару модулей… Но это не смертельно, посмотрите на WordPress — живет процветает с 10 тысяч стронних плагинов, а работает до сих пор на $db->query();
Мне кажется всё-таки следствие, потому что ВП один из первых блоговых движков. Тем более с системой плагинов.
Другое дело, если твой движок используют потому что под него УДОБНО разрабатывать сайты.
Кстати, вот примеры скринкастов фич rails: railscasts.com)
Сказал сам себе тоже самое 5 миинут назад :) На вкус и цвет все фломастеры разные…
Вы уверены?
Генерирование — это создание кода исходя из а) ранее созданных форматов, б) переданных параметров кастомизации.
В наследовании мы получаем тоже самое — мы переносим ранее созданный формат (экземпляр) логики, кастомизируя его.
Вот если бы кодогенератор мог создавать логику кода, которая изначально не заложена в него… тогда это был бы уже говорили об искусственном интеллекте, пишущем программы по нашей просьбе. Но человечеству до этого еще далеко.
А чем плохо/старо/долго писать все последовательно расширяя классы? Разве это не удобнее/нагляднее чем генерация?
При генерации может создаться много ненужного кода, который можно просто удалить.
Да, могу. На примере SQL. Пишется класс, к примеру, SQL, объект которого может «собирать» по кускам ваш запрос,
Далее делаете в SQL функцию init(), которая загружает стандартные (базовые) составляющие. Потом наследуете этот класс, перегружаете init(), добавляя новые составляющие (только сначала вызвав parent::init()).
Вот вам и наследование SQL. Tpl — все тоже самое.
Лично я постоянно пользуюсь наследование вида и функционала Web Form, реализованное через Zend_Form.
Конечно же не идет речь о хелперах, когда мы создаем форму в шаблоне, вызывая метод, чтобы потом можно было легко менять структуру, изменяя методы.
Про Active Record сколько не читал, до моего глубинного сознания не доходит пока гениальность и актуальность этого подхода. Возможно, опыт традиционного программирования давлеет. Единственный, но весьма несущественный нюанс — я тоже таблицы, как правило, во множественном числе называю. :)
А вот генераторы — тема интересная. В этом направлении стоит подумать. Может, удастся избежать использования eval()?
2. Для этого достаточно написать сайтик с 10 сущностями и потом вспомнить сколько времени понадобилось. А после этого отдать его своему другу и спросить, насколько всё понятно.
3. Ага:)
Подчеркну — это лишь предположение. Но было б классно, если б такое можно было реализовать.
Я имел в виду генератор — скрипт, который генерит код. А откуда будет грузиться код это уже дело не генератора…
Можно всё это хранить в одной бд, но модель address будет расширять модель user и будет:
Хотя хранится всё в табличке users.
А в каких случаях может быть по-другому?
Например, есть метод find_by_sql («select * from users»)…
Так же можно написать так:
user.find_all (:select => «select users.*, count (blogs.*)», :joins => :blogs, :group => «user_id»}
На счет друзей тут уже сложнее вечером написать на «языке» rails.
В любом случае всегда доступен find_by_sql
Более того, необходимые счетчики одной строчкой добавляются к табличкам (напр. кол-во комментариев пользователей).
вопрос снят
И мы используем user.address
Т.е. класс UserModel работает с таблицей users, BlogModel — с таблицей blogs. Если на уровне данных есть связь между пользователями и блогами, то работаем не с этой связью, а со связью UserModel <-> BlogModel.
Чаще всего используется в совокупности с Data Row Gateway. Очень неудобно, когда сущность «размазана» в базе по таблицам или поддается динамической кастомизации.
Посоветуйте с чего начать изучать его? И сколько примерно необходимо для этого времени?