Rails вкратце Глава 1. Rails Что делает вас счастливыми? Программиста делает счастливым возможность быстро и ясно изложить свою идею так, что ее смогут легко понять и развить другие. Сюрпризы в поведении кода, повторяющийся код, рутинные операции по заполнению кофигурационных файлов, разрешение вопросов о структуре каталогов, чтобы держать все под рукой, и поиск необходимых библиотек создают препятствия в работе. Rails использует принцип DRY (don’t repeat youself — не повторяй себя) — единая предсказуемая и продуманная конфигурация, файловая структура и встроенные библиотеки используются во всех проектах по умолчанию, сводя к минимуму дополнительную работу по настройке. Главная идея Rails — преимущество соглашения о структуре веб-приложения над ручной настройкой. Создавая приложения с Rails, вы сосредоточены на собственной задаче, а не на изобретении очередного велосипеда. Архитектура После создания нескольких веб приложений, вы воспринимаете некоторые паттерны как лучшие с точки зрения построения и разработки. Обычно над проектом работают три типичных участника команды: представитель бизнеса, дизайнер и менеджер, который объединяет их усилия для достижения желаемого результата. Такая команда работает гораздо эффективнее, если каждый из них имеет полный контроль только над своей работой, а друг с другом они общаются посредством общепринятых методов. Эта идея — разделять бизнес логику, интерфейс, и соединение этого всего вместе — формально известна как шаблон MVC (Model View Controller). Фреймворк Rails построен на этой идее разделения ролей. Модель Модель содержит бизнес логику — можно ли что-то отправлять куда-то: поздравить кого-то с днём рождения или запустить космический корабль. Все это действует как модель вещей в реальной жизни. Эти решения часто основываются на прошедших событиях, так что практически всегда используется база данных. Rails берет на себя работу с базой данных при помощи ActiveRecord, а вам остается заполнить лишь те части, которые относятся к вашему делу. Также генерируются тесты посредством пакета UnitTest. Представление Представление определяет, как информация будет показана: например в браузере, или в агрегаторе RSS, или где-либо ещё. Это сфера дизайнера, его задача — правильно представить данные, полученные из модели. Rails помогает работать с представлением с помощью ActionView, которое объединяет: встроенный в Руби язык шаблонов (ERB), основанный на Builder язык шаблонов (употребляющийся для создания XML, как например ленты RSS, а так же объектов JSON), вспомогательные методы для генерации форм, отображения дат, ссылок и текста, совместное использование шаблонами блоков верстки, интеграцию JavaScript и Ajax, а так же механизмы быстрого построения эскизов сайта. Контроллер Контроллер всё объединяет. Его задача — обработать поступивший запрос, получить данные от компонентов бизнес-логики и вернуть обработанный с помощью Представления результат. ActiveController — часть Rails которая предоставляет для этого: фильтры предварительной и завершающей обработки ответа (полезно, например, для аутентификации), маршрутизацию (по соглашению — REST, но легко адаптируемую для любой необходимой вам структуры сайта), автоматические тесты производительности и журналирование, кэширование (с тремя уровнями детализации), а так же управление сессиями. Замечание ActionView и ActionController входят в состав ActionPack Почта Почта не является частью шаблона MVC (хотя можно представить его как часть представления), однако вы, скорее всего, рано или поздно захотите отправлять письма. Rails упрощает этот процесс с помощью библиотеки ActionMailer, которая делает отправку почты очень простой. Десерт Rails значительно расширяет язык Ruby, позволяя писать такие вещи, как: 5.days.from_now Этот слой дополнительного «синтаксического сахара» для Ruby находится в библиотеке ActiveSupport. Расширяемость Rails легко расширять и модифицировать. Для этой цели существуют тысячи доступных плагинов И если вас не устраивает установка одного из этого множества, мы расскажем как написать плагин. Пример Rails-приложения Давайте сделаем простой сайт интернет-газеты, позволяющий создавать, редактировать и читать статьи. В командной строке (терминале), создайте Rails-проект, выполнив команду: rails newspaper Перейдите в папку с приложением: cd newspaper Давайте посмотрим что было сгенерировано [вставить таблицу из директории со структурой и описанием] Запустим приложение (оно содержит собственный сервер, который работает по умолчанию на порту 3000): script/server Готово, можно посмотреть в браузере по адресу http://localhost:3000 Примечание Вам необходим текстовый редактор для следующих частей. Если вы работаете в Windows, воспользуйтесь Блокнотом, а не Word. Если все идет верно то вы увидите веб-страницу с некоторыми советами как начать работу с вашим приложением. Давайте последуем примеру и исполним script/generate для создания моделей и контроллеров Практическое правило, используемое для определения, какую создать модель — взглянуть на существительные, которыми мы воспользуемся, описывая приложение. В нашем случае, приложение предназначено для написания, редактирования и показа статей, поэтому мы создадим модель Article (статья): $ ./script/generate model Article exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/article.rb create test/unit/article_test.rb create test/fixtures/articles.yml create db/migrate create db/migrate/20090913163814_create_articles.rb Нам также необходим контроллер для определения когда читатель захочет прочесть статьи: $ ./script/generate controller articles exists app/controllers/ exists app/helpers/ create app/views/articles exists test/functional/ create test/unit/helpers/ create app/controllers/articles_controller.rb create test/functional/articles_controller_test.rb create app/helpers/articles_helper.rb create test/unit/helpers/articles_helper_test.rb Предупреждение Имя контроллера по договорённости должно быть множественным числом имени модели. (Договорённость важнее настройки — один и основных принципов Rails) Убедитесь что вы написали articles а не article. Если придерживаться договорённости, Rails сможет автоматически прописать маршрутизацию, о чем мы расскажем позже. Если ошиблись, запустите: script/destroy controller article Теперь, когда у нас есть Контроллер и Модель, давайте опять взглянем на наше приложение. Запустите его с помощью команды script/server, если вы останавливали его, и наберите в браузере http://localhost:3000 еще раз. Выглядит как будто ничего не изменилось. Это потому что мы не выполнили второй шаг — «установить маршрут по умолчанию и переименовать файл». Здесь говорится о файле public/index.html. Все статичные файлы, такие как JavaScript, CSS и картинки, живут в папке public/ Попробуйте отредактировать файл public/500.html, заменив текст на следующий: «Извиняемся, что-то произошло неправильно. Но я действительно люблю ПЕЧЕНЬЕ!». Сохраните файл и посмотрите страницу http://localhost:3000/500.html. Если сообщение не изменилось — перезапустите сервер и попробуйте снова (если что-то идёт не так — для начала всегда попробуйте перезапустить сервер). Теперь, зная как работает папка public/, мы можем догадаться, что страница, которую мы видели в браузере по запросу http://localhost:3000/ находится в файле public/index.html, поскольку веб-сервера всегда показывают файл index.html, если находят его в корневой директории (как в нашем случае). Переименуйте public/index.html в public/setup_instructions.html и посетите http://localhost:3000/ еще раз. Должна обнаружится ошибка маршрута — так как мы не последовали инструкциям по установке маршрута по умолчанию в config/routes.rb. Примечание Вы можете снова увидеть инструкции по настройке, открыв в браузере http://localhost:3000/setup_instructions.html или посмотрев файл public /setup_instructions.html Взгляните на config/routes.rb и замените # map.root :controller => "welcome" чтобы он указывал на ArticlesController, написав map.root :controller => "articles" Обновите http://localhost:3000/ и вы увидите ошибку «Unknown action» (Неизвестное действие), так как ни одно действие не отвечает на запрос к index. Примечание Убедитесь, что вы видите отладочные сообщения в терминале где вы запустили rails-сервер или в конце файла log/development.log. Эти сообщения — одно из лучших средств отладки. Это легко исправить. Откройте apps/controllers/articles_controller.rb. Прямо сейчас он довольно уныл: class ArticlesController < ApplicationController end «Действие index», об отсутствии которого говорили Rails обозначает отсутствие публично объявленного метода index в ArticlesController. Исправим это, добавив его: class ArticlesController < ApplicationController def index end end Обновим http://localhost:3000/ и увидим, что Rails теперь не может найти шаблон представления. Шаблоном называют файл, используемый для представления данных, полученных контроллером из модели. Идея в том, что большинство текста повторяется на каждой странице, кроме некоторых частей (например, даты показа). Нам необходим шаблон, подходящий к действию articles/index, которое мы недавно создали, поэтому создадим файл index.html.erb в папке app/views/articles/ со следующим содержимым:

Привет! Здесь будут статьи.

Обновите страницу в браузере, и вы должны увидеть тот же текст. Разработаем примерное представление, как страница будет выглядеть, добавив туда «рыбу» — временный текст, заполняющий макет страницы:

Время Рельс

Деструктивный гений — актуальная национальная задача

Как отмечает Жан Пиаже, дуализм многопланово представляет собой латентный импульс, ломая рамки привычных представлений.

Самоактуализация монотонно аннигилирует первоначальный флегматик, именно об этом комплексе движущих сил писал З.Фрейд в теории сублимации. Одиночество понимает закон внешнего мира, и это неудивительно, если речь о персонифицированном характере первичной социализации. Гедонизм, например, продолжает дедуктивный метод, именно об этом комплексе движущих сил писал З.Фрейд в теории сублимации. Художественная богема амбивалентно вызывает социометрический закон, таким образом, сходные законы контрастирующего развития характерны и для процессов в психике.

Эмпирический синтаксис искусства: предпосылки и развитие

Пародия, в первом приближении, дискредитирует катарсис, именно об этом комплексе движущих сил писал З.Фрейд в теории сублимации.

Действительно, душа возможна. Комплекс априорной бисексуальности выбирает страх, учитывая опасность, которую представляли собой писания Дюринга для не окрепшего еще немецкого рабочего движения. Отсюда естественно следует, что действие имитирует дуализм, отрицая очевидное. Гомеостаз образует потребительский филогенез, и это неудивительно, если речь о персонифицированном характере первичной социализации. Компульсивность, например, изящно продолжает интеллект, открывая новые горизонты.

Почему свободна ирония?

Интроекция продолжает субъект, tertium nоn datur. Закон исключённого третьего транспонирует гений, так Г.Корф формулирует собственную антитезу.

Эмпирическая история искусств, согласно традиционным представлениям, осмысленно заполняет невротический комплекс агрессивности, однако само по себе состояние игры всегда амбивалентно. Суждение, конечно, монотонно иллюстрирует гомеостаз, таким образом, сходные законы контрастирующего развития характерны и для процессов в психике. Добавлю, что притча готично порождает и обеспечивает символизм, однако само по себе состояние игры всегда амбивалентно.

Обновите страницу в браузере чтобы увидеть новое содержимое. Выглядит неплохо. Нам следует позвать верстальщика, чтобы он оформил это с помощью CSS действительно хорошо, но пока нам этого достаточно, чтобы понять, какие данные нам нужны от модели: она должна хранить заголовок и содержимое статьи. Напишем тест, проверяющий, что модель на это способна. Откроем test/unit/article_test.rb require 'test_helper' class ArticleTest < ActiveSupport::TestCase # Replace this with your real tests. test "the truth" do assert true end end Мы должны быть уверены, что можем создавать статьи с заголовками и содержимым, поэтому заменим «рыбу» на следующий тест: test "save a title and body" do a = Article.new a.title = "Нормальный заголовок" a.body = "Короткое, но всё же годное содержимое." assert a.save end Этот тест инициирует экземпляр класса Article, устанавливает значения заголовка и содержимого, а потом утверждает, что экземпляр может быть сохранён (метод a.save возвращает истину, если модель сохранена и ложь в противном случае). Примечание Есть более короткий способ создания статьи, с использованием метода ActiveRecord::Base#create, но о нём расскажем позже Сохраним файл и запустим тестирование командой: rake test При попытке запуска тестов мы получим ошибку, говорящую о ждущей обработке миграции, которую мы можем совершить, выполнив rake db:migrate. Последуем совету, выполнив rake db:migrate, а затем снова rake test. Миграция пройдёт успешно, а тест завершится ошибкой, говорящей о необъявленном методе «title=» для #
, который был вызван в 6-ой строке файла test/unit/article_test.rb Выглядит, будто нам надо поработать над базой данных. Давайте взглянем на миграцию, которую мы недавно запустили: 20091029031214_create_articles.rb Примечание Ваш файл не будет называться 20091029031214_create_articles.rb, поскольку первая часть имени файла обозначает момент времени, в который миграция была создана. Не смотря на это, содержимое файла будет таким же. Вот файл миграции данных: class CreateArticles < ActiveRecord::Migration def self.up create_table :articles do |t| t.timestamps end end def self.down drop_table :articles end end Миграции последовательно вносят изменения в базу данных, делая легкой параллельную работу над проектом и простой откат неправильных изменений. Миграция CreateArticles создает таблицу articles и автоматически добавляет поля с метками времени created_at (создано) и updated_at (обновлено) при своем запуске (up) и удаляет таблицу articles при откате (down). Запуск теста сообщит нам, что не удалось найти метод title=, так как в модели такой метод явно не определен и потому что нет такового, определенного неявно при наличии таблицы articles с полем title. Так как Rails связывает каждую таблицу базы данных с Моделью, если их названия совпадают, Rails связывает модель Tree связывается с таблицей trees и создает методы для доступа и присваивания значений полям таблицы. Примечание Вы также можете сами настроить сопоставление таблицы и Модели, для этого прочтите главу ActiveRecord Создадим миграцию, добавляющую в таблицу articles строковое поле title и текстовое поле body. Типы полей более подробно рассматриваются в главе ActiveRecord. Отредактируем файл db/migrate/20090913163814_create_articles.rb class CreateArticles < ActiveRecord::Migration def self.up create_table :articles do |t| t.string :title t.text :body t.timestamps end end def self.down drop_table :articles end end Замечание Не волнуйтесь, если имя файла вашей миграции начинается с другой временной метки. В главе ActiveRecord более подробно рассматриваются различия доступных полей миграции. Запустим: rake db:migrate - это нужно для обновления схемы. Ок! Теперь, когда у нас есть место для наших данных, ошибка с которой мы столкнулись во время выполнения теста должна быть исправлена. Запустим снова rake test. Тест пройден! Наша модель Article работает так как нам надо, давайте начнем ее использовать в представлении index. Изменим файл app/views/articles/index.html.erb

The Rails Times

<% @articles.each do |article| %>

<%= article.title %>

<%= article.body %>
<% end %> Код заключенный в <% и %> воспринимается как Руби код (Откуда и взялось erb в названии файла расшифровка Embedded RuBy) итак первая строка перебирает статьи содержащиеся в @articles и вставляет их в переменную блока article, и повторяет код находящийся в блоке для каждой статьи. Код между <%= и %> также воспринимается как руби-код, но результат отображается на заполняемой странице. Вот так работают следующие кусочки кода: article.title превращается в строку и появляется в заполняемой странице. Примечание Обычная ошибка спутать эти два способа, так что отслеживайте их при дебаге. Загрузите http://localhost:3000/ снова, чтобы увидеть наши изменения. Ой-ой! В Articles#index есть некая ошибка, связанная с NoMethodError: в районе третьей строки есть код, который интерпретируется в nil.each. Сравнивая сообщение об ошибке с нашим кодом представления (особенно в строке 3), кажется, что @articles интерпретируется в nil, который не знает, что делать, когда мы вызываем у него each. В этом есть смысл — мы пока ничего не присвоили переменной @articles. Давайте сделаем это в контроллере ArticlesController, который соответствует предcтавлению. Изменим метод index в ArticlesController (файл app/controllers/articles_controller.rb): def index @articles = Article.find(:all) end Этот фрагмент кода присваивает экземпляру класса @articles все найденные в базе записи статей Article. Переменные экземпляра и переменные класса доступны для представления, а локальные - нет. Замечание Убедитесь, что вы употребляете правильное число существительного article в названиях модели и контроллера - модель единственного числа: Article, а контроллер, работающий с этой моделью - множественного ArticlesController. Обновите http://localhost:3000/ - ошибка исчезла. Мы видим симпатичную пустую страницу, ведь записей в базе статей еще нет. Давайте сделаем несколько вручную, для того чтобы мы могли увидеть что-то. В терминале запустите консоль Rails, выполнив: script/console Вы должны получить приглашение вида: >> Это интерактивный командный интерпретатор вашего приложения - вы можете заниматься отладкой работающего приложения и сразу видеть все изменения. >> a = Article.new => #
>> a.title = "Cake is nice." => "Cake is nice." >> a.body = "My favourite is chocolate." => "My favourite is chocolate." >> a.save => true Последняя строчка показывает, что статья была успешно сохранена, поэтому обновим страницу в браузере http://localhost:3000/ и увидим нашу первую статью во всей красе. Сделайте другую статью в консоли, повторяя те же шаги, или попробуйте более короткую версию: Article.create(:title => "Texas Ranger", :body => "Remember when Chuck Norris jokes were funny?") Этот код работает также как и тот который мы ввели тремя строками ранее, если Вам интересно, как это работает, см. раздел ActiveRecord. Ок! Мы получили работающую Модель, Вид и Контроллер. Мы вроде закончили? Почти. Давайте упростим создание статьи, созданием Вида new Создайте файл new.html.erb в папке app/views/articles Добавьте этот код в начало Вида index (index.html.erb): <%= link_to 'Create a new article', :controller => 'articles', :action => 'new' %> Метод link_to создаст для нас правильно сформированную ссылку и избавит от ручного редактирования, если мы обновим структуру url приложения (Rails-маршрутизация) Обновите http://localhost:3000 и кликните на ссылке new. Опять ошибка Unknown action error (неизвестное действие), но в этот раз вы знаете ее причину и можете самостоятельно пофиксить этот баг :) Создайте Вид для нового действия: app/views/articles/new.html.erb. Этот вид содержит форму ввода для создания новой статьи, ее код выглядит так:

Making a new article

<% form_for :article do |f| %>

Title:
<%= f.text_field :title %>

Body:
<%= f.text_area :body %>

<%= f.submit "Create a new article" %> <% end %> Этот код создает форму для действия new по умолчанию. В блоке два метода, вызываемых для создания формы: text_field и text_area, которые генерируют html-код полей ввода (input) для каждого атрибута Модели (т.е. title и body). Замечание Этот метод Вида из ActionView::Helpers::FormHelper, также известный в Rails как "хелпер". Более подробно о хелперах в главе ActionView. Обновите http://localhost:3000/articles/new и введите любые данные в форму. Попробуйте кликнуть кнопку "Create a new article". Обратите внимание,что когда вы это делаете, файлы регистрации, появляющиеся в терминале, выполняющем script/server , сообщают, что ArticlesController#new обрабатывается даже при том, что мы не определили действие для этого. Это работает так потому что Rails если не найдет соответствующего Действия, создает его на лету. Это Действие просто обрабатывает Вид с таким же именем. Однако, хотя это не взрыв, это не делает то, что нам нужно, что для новой статьи, которая будет создана с значениями формы. Измените form_for метод в Виде следующим образом: <% form_for :article, :url => { :action => "create" } do |f| %> Более подробно о работе form_for читайте в главе ActionView. Обновите, нажмите кнопку submit формы и вы увидите знакомую ошибку ActionController::UnknownAction error. Вы знаете, как это исправить. Создайте действие "create" в ArticlesController: def create @article = Article.new(params[:article]) if @article.save redirect_to :action => 'index' else render :new end end Обновите страницу, введите что-нибудь в поля и нажмите "подтвердить". Видите, как это работает (новая статья внизу страницы)? Небольшая проблема - у нас нет проверки данных в Article model, то есть данные никак не проверяются перед сохранением в базе данных. Попробуйте создать новую статью, но не вводите никаких данных в форму. Видите, она все равно сохранилась. Нужно проверять, что поле body (тело статьи) не пустое. И тоже самое делать для поля title. Добавьте проверку в файл article.rb: class Article < ActiveRecord::Base validates_presence_of :title validates_presence_of :body end Попробуйте теперь создать пустую статью. Судя по логам, Действие create отработало, но нас просто переслало на articles/new. Похоже, валидация работает, но пока она делает это молча - никакого сообщения о попытке сохранить пустую статью мы не получили. Добавим сообщение об ошибке. Отредактируйте new.html.erb:

Making a new article

<%= error_messages_for :article %> <% form_for :article, :url => { :action => "create" } do |f| %> [] Обновите /new и попробуйте создать пустую статью - теперь вы получите сообщение о том, что собираетесь сохранить пустую статью. В главе ActionView подробно объяснены о работе сообщений об ошибках, а в главе ActiveModel рассказывается, как создать и настроить эти сообщения. Мы могли создавать статьи, теперь мы должны получить право редактировать и удалять их. Создайте Вид для действия "edit" (редактирование): отредактируйте app/views/articles/edit.html.erb:

Редактирование статьи

<%= error_messages_for :article %> <% form_for :article, :url => { :action => "update", :id => @article.id } do |f| %>

Заголовок:
<%= f.text_field :title %>

Текст:
<%= f.text_area :body %>

<%= f.submit "Сохранить статью" %> <% end %> Это представление очень похоже на /new, но action формы указывает на update подобно действию create. Оно также обращается к статье, которую мы редактируем через переменную @article, которая будет подготовлена в действии edit в ArticlesController (здесь у нас немного магии: в представлении мы используем :article, а в контроллере — @article. Если хотите узнать об этом больше прямо сейчас, см. как работает form_for в главе ActionView). В самый низ класса ArticlesController (в articles_controller.rb) нужно добавить два действия: [] def edit @article = Article.find(params[:id]) end def update @article = Article.find(params[:id]) if @article.update_attributes(params[:article]) redirect_to :action => 'index' else render :edit end end [] Замените заголовки каждой статьи в представлении /index на ссылки, которые указывают на страницу редактирования: [] <% @articles.each do |article| %>

<%= link_to article.title, :action => 'edit', :id => article.id %>

<%= article.body %>
<% end %> [] Обновите страницу /index, и увидите, что каждый заголовок — ссылка на действие edit. Кликнув по одному из них, Вы должны увидеть статью. А изменив ее, после перенаправления назад на /index Вы должны увидеть, что изменения отразились в списке статей. Вроде все отлично! Но выглядит немного мрачновато. Нам нужно немного CSS. Также можем добавить поддержку комментариев и придумать какую-нибудь аутентификацию для редактирования статей. Еще было бы неплохо уменьшить количество copy-paste кода в действиях new и edit, чтобы при изменении формы пришлось менять его только в одном месте. И в завершении, было бы замечательно, если бы статьи считались ресурсами, которые соответствуют требованиям REST. Глава еще не дописана, в окончательном варианте будут убраны некоторые избыточные моменты для краткости, что-то будет добавлено для лучшего сочетания с другими главами и более ясного понимания некоторых вещей (наример, params[]). ------------------------------------------------------------------------------- http://translated.by/you/rails-in-a-nutshell/into-ru/trans/ © Copyright © 2009 Cody Fauser, James MacAulay, Edward Ocampo-Gooding, and John Guenin Original (English): Rails in a Nutshell (http://rails-nutshell.labs.oreilly.com/index.html) Translation: © alexbaumgertner, Александр Семёнов, Valeanna, asplogika, Андрей Савченко, zula, sitnik, saturn721. License: Open Feedback Publishing System (OFPS) translated.by crowd