ФорумПрограммированиеБольше языковRuby → MVC и Ruby on Rails

MVC и Ruby on Rails

  • killich

    Сообщения: 270 Репутация: N Группа: Адекваты

    Spritz 15 августа 2009 г. 17:30, спустя 4 часа 50 минут 54 секунды

    Концепция MVC - Модель Вид Контроллер (Я предпочел бы название - Модель Контроллер Вид), это одна из базовых и самых удачных на данный момент концепций разработки ПО для web (и для большинства других программных продуктов).

    Суть MVC заключается не в расположении файлов внутри проекта, не в их устройстве и не в программной реализации. С идеями MVC меня в 2006 году познакомил мой преподаватель программирования в университете, после чего в течение года я писал свою реализацию MVC на php, не опираясь ни на один программный продукт реализованный на MVC (просто было лень искать), а используя только понятую мною (причем, возможно, не самым правильным образом) идею.

    Я был потрясен, когда многое из того, к чему я пришел самостоятельно я обнаружил в RAILS, что и послужило поводом срочного перехода на RAILS, поскольку изобретать свой велосипед просто надоело. =)

    MVC утверждает (в очень приблизительном виде и моей интерпретации), что средства работы с БД (Модель), должны быть полностью отделены от программной логики (Контроллера). Кроме того, программная логика (Контроллер) получив необходимые данные от БД (посредством Модели) должны передать уже подготовленные данные средству отображения (Виду).

    И так - общая схема такая.

    1. Контроллер, согласно пришедшим от пользователя параметрам вызывает Модель (функции работы с БД).

    2. Получив необходимые данные от Модели, Контроллер выполняет подготовку данных для Вида (выполняет формирование переменных и массивов, их преобразования и т.д.) передает готовые данные (массивы и переменные) в Вид.

    3. Вид - это перемешка HTML кода + вставка переменных и логики отображения (например цикл, пробегающий по массиву и отрисовывающий его элементы в тегах h3). Фактически - Вид - это HTML шаблонизатор, в который поступают уже готовые данные из Контроллера.

    И в чем же отличие от PHP? В чем красота? Ну.. Если вам попадались только PHP проекты устроенные согласно всем канонам MVC - вы очень счастливый человек. Да! MVC можно реализовать на любом языке, просто ROR устроен так, что программы иначе здесь не создаются.

    В ROR практически исключены ситуации, когда в HTML коде присутствует обращение к БД через SQL, тут же производится разбор данных и их проверка, и тут же данные отрисовываются.

    MVC и ROR разработчики заявляют - ПРОГРАММНОЙ КАШЕ - НЕТ! ДАЕШЬ КРАСИВЫЙ И ПРАВЕЛЬНЫЙ MVC КОД!


    Модель (Model)


    Модель - это средство работы с таблицами БД. Набор функций и средств создания записей в таблице БД, получения данных из таблицы, обновление данных и удаления записей из таблиц.

    Модель в понимании RAILS это класс унаследованный от ActiveRecord::Base.
    Что это значит? Хм. Попробуем разобраться.

    Что же нам дает то, что существует некоторый класс унаследованный от ActiveRecord::Base?

    ActiveRecord - это один из подходов работы с БД.

    В Rails ActiveRecord подход реализуется в одноименной библиотеке работы с БД -> ActiveRecord.

    Все в Rails подчинено соглашениям - в этом потрясающем фреймворке трудно найти сотню конфигурационных файлов - большинство настроек существует по-умолчанию, витает в воздухе и за счет своей логичности порою задаешь себе вопрос - а зачем вообще нужны конфигурационные файла в других системах. Я не знаю где это настраивается, но по умолчанию я бы настроил, что бы это работало, так как мне нужно - и ЭТО ДЕЙСТВИТЕЛЬНО РАБОТАЕТ ТАК КАК ТЫ ОЖИДАЕШЬ! без дополнительных настроек и конфигураций.

    Вот и ActiveRecord задает набор таких соглашений. За счет чего нам легко и просто работать с БД. Рассмотрим пример:

    Предположим, что у нас существует миграция:


    class CreateRoles < ActiveRecord::Migration
    def self.up
    create_table 'roles' do |t|
    t.string 'name' # Английское имя роли
    t.string 'title' # Название Роли
    t.text 'description' # Текстовое описание Роли
    t.text 'settings' # Настройки Роли

    t.timestamps
    end
    end

    def self.down
    drop_table 'roles'
    end
    end



    Миграции расположены в каталоге:
    c:\ror_server\rails_apps\MY_SUPER_SITE\db\migrate\

    Мы запустим выполнение миграции:
    c:\ror_server\rails_apps\MY_SUPER_SITE>rake db:migrate

    В результате в БД появится таблица roles в которой есть поля:

    -roles
    name
    title
    description
    settings
    created_at
    updated_at


    Мы создаем файл с описанием класса МОДЕЛИ для работы с данной таблицей:

    class Role < ActiveRecord::Base
    # Здесь может быть много кода
    # Но пока что тут не будет ничего кроме
    # 3х строчек с комментариями
    end


    Все Модели приложения хранятся в каталоге:
    c:\ror_server\rails_apps\MY_SUPER_SITE\app\models\

    Если знания в области программирования достаточны, то легко понять, что от любого класса можно создать экземпляр (для того они и нужны эти самые классы)

    Это значит, что в приложении мы сможем выполнить следующую команду:


    role= Role.new



    Я надеюсь, что определение класса Role, мы еще не забыли?! =)

    Я напомню - в этом классе нет ничего, кроме 3х строчек комментариев.

    Однако класс Role унаследован от ActiveRecord::Base, а это значит, что ActiveRecord уже послал SQL запрос в БД и узнал, какие поля находятся в таблице с именем roles и автоматически создал необходимые переменные в классе Role.

    Почему именно в таблице в именем roles???

    Это очень просто - это негласное СОГЛАШЕНИЕ ActiveRecord и большинства элементов фреймворка - если класс Role, то логичнее всего предположить, что он связан с таблицей roles! Я бы лично сделал именно так! И создатель ActiveRecord тоже так же подумал! Ура!

    Следовательно, я могу попробовать сделать следующее:


    role= Role.new
    role.name= 'administrator'
    role.title= 'Роль администратора'
    role.description= 'В этой записи в таблице roles хранятся настройки админа'
    role.settings= 'админ может все!'



    Замечательно! И что дальше! А вот что! Мы выполняем волшебный метод ActiveRecord с названием SAVE


    role= Role.new
    role.name= 'administrator'
    role.title= 'Роль администратора'
    role.description= 'В этой записи в таблице roles хранятся настройки админа'
    role.settings= 'админ может все!'

    role.save



    В результате выполненных нами действий, мы создали программное отображение новой записи в таблице БД. Мы заполнили соответствующие поля и выполнили метод save. В результате чего в БД появилась новая запись.
    Так мы, не используя SQL запросов можем работать с БД. 99% работы с БД в Rials производится через программное отображение записей таблицы БД.

    При сохранении объекта role (выполнение функции role.save) ActiveRecord самостоятельно заполнил поля created_at и updated_at. Следить за изменением данных полей нам не требуется.


    Модель (Выборка данных из БД) Метод Find


    В приложении нам придется не только создавать записи, но в основном получать их, обновлять или передавать в Вид для отображения. Как же получить необходимые данные из БД? Кночно же, как и все в Rails - все очень просто.

    Вспомним определение класса Roles:


    class Role < ActiveRecord::Base
    # Здесь может быть много кода
    # Но пока что тут не будет ничего кроме
    # 3х строчек с комментариями
    end



    Казалось бы - класс абсолютно пустой, но уже сам факт его существования и то, что он унаследован от ActiveRecord::Base нам вполне достаточно.

    Всякий класс, унаследованный от ActiveRecord автоматически становится счастливым обладателем метода find, а помощью, которого можно выполнять большинство требуемых выборок из таблицы связанной СОГЛАШЕНИЯМИ с данным классом.

    Предположим, что в таблице roles уже есть несколько записей:
    (Что бы не засорять эфир - оставлены только некоторые поля)

    id |name 	                   | title                            | created_at            | updated_at
    1 | administrator | Администратор портала | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
    2 | registrated_user | Зарегистрированный пользователь | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
    3 | guaranted_user_role | Заверенный пользователь | 2009-07-22 16:45:48 | 2009-07-22 16:45:48
    4 | site_administrator_role | Администратор сайта | 2009-07-22 16:45:49 | 2009-07-22 16:45:49


    Так в приложении мы можем выполнить следующие манипуляции:



    # Найти все записи в таблице roles
    roles= Role.find(:all)


    # Найти первую запись в таблице roles
    roles1= Role.find(:first)


    # Найти все записи в таблице roles поле created_at в которых равно '2009-07-22 16:45:49'
    roles2= Role.find_all_by_created_at('2009-07-22 16:45:49')


    # Найти все записи в таблице roles где created_at= '2009-07-22 16:45:49' И поле title= 'Администратор портала'
    roles3= Role.find_all_by_created_at_and_title('2009-07-22 16:45:49', 'Администратор портала')


    # Найти все записи в таблице roles где created_at= '2009-07-22 16:45:49' ИЛИ поле title= 'Администратор портала'
    roles4= Role.find_all_by_created_at_or_title('2009-07-22 16:45:49', 'Администратор портала')



    После того как мы нашли некоторый массив записей или одну запись, мы можем выполнить над каждой записью некоторые действия и сохранить результат в БД.

    Так например выше в переменную roles1 был отправлен результат поиска первой записи в таблице. roles1= Role.find(:first) Теперь можно изменить переменную roles1 и выполнив сохранение методом save - мы изменим запись в таблице БД.



    # Найти первую запись в таблице roles
    # Изменить поле title
    # Сохранить изменения в БД

    roles1= Role.find(:first)
    roles1.title= 'Изменил текст в этом поле'
    roles1.save




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


    Контроллер (Controller)


    Контроллер фактически это файл со списком обработчиков действий для некоторого раздела сайта.
    Так например контроллер Pages будет отвечать в проекте за все производимые над страницами сайта.
    Контроллер Pages будет включать в себя обработчики действий (функции обеспечивающие работу пользователя на сайте).

    Как правило базовый контроллер состоит из набора функций:

    -index - список всех объектов данного класса (как правило с логикой пагинации [перелистывание списка объектов по страницам] )
    -show - показ всех данных для одного объекта
    -new - отработка страницы для создания нового объекта
    -create - алгоритм создания нового объекта
    -edit - отработка страницы для редактирования объекта
    -update - алгоритм обновления объекта
    -destroy- убить объект и перейти на какую нибудь страницу =) гыгыгыг!

    Контроллер - контроллер, controller
    Функция Контроллера - обработчик, action

    После выполнения алгоритма внутри обработчика (action) данные передаются в файл отображения (Вид), который носит точно такое же имя как и обработчик. Так если был выполнен обработчик index, то все данные, которые получены в результате его выполнения поступают в файл отображения index.html.erb
    ERB - это стандартный шаблонизатор Rails - именно тут и генерируется HTML код.

    После выполнения некоторых действий, например destroy, как правило, не происходит отрисовки файла destroy.html.erb (такого файла вообще не существует), а происходит переадресация пользователя к другому действию контроллера - REDIRECT. [redirect_to(admins_users_url) - фактически переадресация на index]

    Пример Контроллера построенного с помощью Скаффолда для администрирования объектов класса User.
    Такой код обеспечивает весь набор действий CRUD для класса User, т.е. полное БАЗОВОЕ управление пользователями на сайте.

    __Смотрим пример и находим места, где действия КОНТРОЛЛЕРА обращаются к БД, посредством класса МОДЕЛИ User__
    Полученные данные сохраняются в переменных обозначенных через символ @ - благодаря чему носят глобальный характер и существуют до момента отрисовки в ВИДЕ.

    class Admins::UsersController < ApplicationController
    # GET /users
    def index
    @users = User.paginate(:all,
    :order=>"created_at ASC", #ASC, DESC
    :page => params[:page],
    :per_page=>6
    )

    respond_to do |format|
    format.html
    end
    end

    # GET /users/1
    def show
    @user = User.find(params[:id])

    respond_to do |format|
    format.html
    end
    end

    # GET /users/new
    def new
    @user = User.new

    respond_to do |format|
    format.html
    end
    end

    # GET /users/1/edit
    def edit
    @user = User.find(params[:id])
    end

    # POST /users
    def create
    @user = User.new(params[:user])

    respond_to do |format|
    if @user.save
    flash[:notice] = 'user успешно создано.'
    format.html { redirect_to(admins_user_path(@user)) }
    else
    format.html { render :action => "new" }
    end
    end
    end

    # PUT /users/1
    def update
    @user = User.find(params[:id])

    respond_to do |format|
    if @user.update_attributes(params[:user])
    flash[:notice] = 'user успешно обновлено.'
    format.html { redirect_to(edit_admins_user_path(@user)) }
    else
    format.html { render :action => "edit" }
    end
    end
    end

    # DELETE /users/1
    def destroy
    @user = User.find(params[:id])
    @user.destroy

    respond_to do |format|
    format.html { redirect_to(admins_users_url) }
    end
    end
    end



    ВИД (View)


    Вид - это файлы отвечающие за отрисовку даннх в виде HTML.
    Для каждого класса существует своя папка со своими VIEW файлами.
    В файлы Вида приходят данные из Контроллера, внутри переменных обозначенных через символ @

    Так например вид может получить от Контроллера переменную @users или @pages_tree

    Стандартным шаблонизатором Rails является ERB - он очень похож на PHP видом записи.
    HTML теги перемешаны с выводом данных из переменных контроллера:


    <div class="site_map">
    <div class="title">
    <img class="symbol" src="/images/basic/site_map.jpg" alt="" />Карта сайта</div>
    <div class="lm55">

    <%= show_map(@sections) %>
    <%= parse_site_map(@sections) %>

    </div>
    </div>


    Здесь:


    <%= show_map(@sections) %>
    <%= parse_site_map(@sections) %>



    Это вывод на экран результата 2х функций, которые в качестве параметра принимают переменные поступившие из Контроллера.
    Рубист с большой буквы Г. Серый кардинал кулинарного блога open-cook.ru
  • phpdude

    Сообщения: 26624 Репутация: N Группа: в ухо

    Spritz 15 августа 2009 г. 12:28, спустя 18 часов 58 минут 2 секунды

    я бы Килич ахуенен, и я говорю это. ТЫ АХУЕНЕН!

    но руби не люблю, поэтому сори даже читать не стал (
    Спустя 7 сек.
    НО добавил в закладки ;)
    Сапожник без сапог
  • killich

    Сообщения: 270 Репутация: N Группа: Адекваты

    Spritz 15 августа 2009 г. 12:34, спустя 6 минут 4 секунды

    Вот начну с сентября плановые уроки писать - и через полгода парабатю твой моск =)
    Рубист с большой буквы Г. Серый кардинал кулинарного блога open-cook.ru
  • rider-sx

    Сообщения: 2706 Репутация: N Группа: Адекваты

    Spritz 15 августа 2009 г. 12:39, спустя 5 минут

    killich, соблазнил на руби…
    а трой на жаву…

Пожалуйста, авторизуйтесь, чтобы написать комментарий!