понедельник, 28 июля 2008 г.

Простая выборка записей через метод Find. Кратко о Conditions.

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


































# name last name age create_date updated_at
1 John Doe 31 29.07.2008 29.07.2008
2 Sergey Saenko 23 29.07.2008 29.07.2008
3 Ivan Petrenko 25 29.07.2008 29.07.2008


Как же работает с SQL используя ActiveRecord?

Для того чтобы делать выборки из базы, для каждой модели существует метод Find. Посмотрим на простом примере как же он работает:

User.find(1) - результатом такого запроса будет первая запись нашей таблицы, которая соответствует пользователю John Doe, результатом выполнения запроса будет обьект класа User.
Таким образом, если метод find() принимает в качестве параметра число, то он будет искать запись, ІD которой будет соответствовать параметру.
User.find(:all) - вернет коллекцию єкземпляров класса User. Согласно переданому параметру - выберет все записи из таблицы.
User.find(:first), и по аналогии User.find(:last) - эти два запроса соответственно вернут первого и последнего зарегистрированного пользователя в системе.


При генерации любой модели, независимо от полей таблицы заданных в миграции - в автоматическом режиме создаются 2 дополнительных поля:
create_date и updated_at которые имеют тип дата/время. Оба поле заполняется автоматически при создании записи(дата создания и дата обновления сначала совпадают), но второе поле автоматически изменяется при любой модификации записи.


Так к чему же я это все писал - при любой выборке коллекции записей, данные будут отсортированы как-раз по полю create_date (если порядок сортировки не задан в явном виде). Таким образом я точно знаю что User.find(:first) вернет первый созданный обьект, а User.find(:last) соответствует последнему созданному экземпляру User.


Немного отвлекусь от темы. Рельсы сделаны очень интиресным образом, сейчас немного объясню свое понимание почему же фреймворк назвали именно Рельсы. Все что можно было задать по-умолчанию - разработчики фрейворка уже описали. Другими словами частичное поведение всех методов и классов при их генерации уже заданы, и менять программисту нужно только то что ему надо. Таким образом у нас как-бы есть уже "сырые заготовки", которые осталось только немного подправить и настроить. Создается впечатление, что все делается как-бы по накатанному пути - "как по рельсам". На самом деле все не так, Руби очень гибкий язык программирования, который позволяет реализовать в принципе любой алгоритм. Рельсы писались на Руби, то понятно что это очень быстрый в разработке и довольно гибкий - учитывая язык на котром он написан - инструмент разработки. Но повторяю - это только мое субъективное мнение.


Для того чтобы задать параметры выборки используется метод :conditions, например:
User.find(:all, :conditions=>"age=23") - выберет второго пользователя.
Чтобы передавать в :conditions параметры, можно использовать следующие способы:
name = "Sergey"

1. User.find(:all,:conditions=>["name=:name",{:name=>name}])

2. User.find(:all,:conditions=>["name= ?",name])

3. User.find(:all,:conditions=>"name=#{name}")

Рельса в любом из 3 случаев предварительно формирует для БД запрос, а потом его отсылает. Тоесть неверно говорить что что параметры на сервере в 1 и 2 передаются отдельно, а ядро запроса кешируется. В любом случае на сервер идет готовый запрос, проверял лично. По скорости работы выигрывает на сотые доли секунды 3 вариант, но его целиесобразно использовать только если мы не вставляем в параметры пользовательские данные. Для того чтобы предотвратить сюл-иньекции используйте вариант 1 или 2.

И как завершение я расскажу разницу вариантов 1 и 2. В первом случае, мы передаем именованые параметры, тоесть, в случае если надо чтобы 1 параметр встречался в запросе боьше одного раза, во 2 варианте придется через запятую 2 раза его дублировать:


User.find(:all,:conditions=>["start_date= ? and end_date = ?",current,current])


а в 1 варианте можно сделать так:


User.find(:all,:conditions=>["start_date= :current_date and end_date = :current_date",{:current_date=>current}])


таким образом удается избавится от избыточности параметров


Сonditions- очень похожи на С-функцию sprintf, так что сложности с пониманием быть не должно. А само тело условия формируется аналогично постороения условия запроса через WHERE.

Ruby on Rails. Настройка database.yml

Эта статья является дополнением к моей предыдущей публикации "Как связана модель и таблица в БД?". Для того чтобы успешно работать с БД, ActiveRecord должен получить ее описание. Дальше он сам подберет нужный для работы драйвер. Программист не должен заботится о том с какой базой даных он работает, единственное что он должен сделать - правильно настроить конфигурационный файл подключения к базе и создать саму базу в соответствующем редакторе (например SQL Manager for MySQL).

Откроем папку с проектом и найдем там файл \config\database.yml. Первоначально он имеет вид (я опускаю комментарии. Комментарием считается строка которая начинается с символа "#"):


development:
adapter: mysql
encoding: utf8
database: blogger
username: root
password: 123123
host: localhost

test:
adapter: mysql
encoding: utf8
database: TestApplication_test
username: root
password:
host: localhost

production:
adapter: mysql
encoding: utf8
database: TestApplication_production
username: root
password:
host: localhost

Здесь описаны подключения к трем БД:
  • тестовой(на которой прогоняют тесты)
  • БД разработчика(которая подключается к локально установленному проекту разработчика)
  • БД production(которая включается в релиз готового проекта)
Рассмотрим детально подключение к произвольной БД, так как параметры везде одинаковые:
  1. adapter: mysql - указывается база данных с которой предстоит работать
  2. encoding: utf8 - выбирается кодировка хранимых данных (следите чтобы она совпадала с кодировкой самого проекта)
  3. database: TestApplication_production - имя уже созданной базы данных
  4. username: root - логин админа БД
  5. password: - пароль админа БД (если он не пустой)
  6. host: localhost - хост БД, localhost указывет на то что работать предстоит на локальной машине
Теперь вам предстоит создать тестовую(test) и девелоперскую(
development) БД и прописать соответствующие параметры в database.yml. Базу данных для продакшина(production) можно закомментировать, так как она понадобится только после завершения проекта или выхода первого релиза.

К выбору имени БД тоже есть некоторые соглашения. Если вы планируете назвать вашу БД к примеру "my_database", то к имени тестовой БД принято добавлять суфикс "_test", а к девелоперской - "_development". В результате получим my_database_test и my_database_development соответственно.




вторник, 22 июля 2008 г.

Как связана модель и таблица в БД?

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

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

Нам не нужно создавать вручную ни таблицу, ни класс - рельса это сделает за нас. Достаточно для этого в консоли запустить генератор:
>ruby script\generate model User
в консоли генерируется список созданных файлов и папок:
> exists app/models/ - папка где хранятся все модели
> exists test/unit/ - папка для юнит-тестов
> exists test/fixtures/ - папка для фикстур
> create app/models/user.rb - файл модели
> create test/unit/user_test.rb - файл юнит-теста модели
> create test/fixtures/users.yml - файл где хрянтся фикстуры для модели
> create db/migrate - папка где хранятся миграции
> create db/migrate/001_create_users.rb - миграция для создания таблицы users

генератор создает нужные файли с шаблонным текстом для модели, функцональный тест, фикстуры. Нас пока будет интересовать только файл /app/model/user.rb - в котором и хранится наша модель. Он по-умолчанию содержит только обьявления класса(модели),
который наследуется от ActiveRecord - фреймворка для работы с БД

Генератор также создает в БД таблицу с именем users.

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

Для доступа к полю таблицы - используеться одноименный метод модели, например в таблице user есть поле name, то мы можем к нему обратиться как User.name

У вас возникает естественный вопрос: "Как модель знает какие поля есть у таблицы?"
Для того чтобы понять как ставиться в соответствие метод модели и поле базы - необходимо разобраться с миграциями, так как разгадка находиться в файле db/migrate/001_create_users.rb который создал наш генератор.

Подробно про о миграциях поговорим в следующей статье.


Немного терминологии:

Юнит-тест - это класс(тесты подробно рассмотрим позже) который привязывается к конкретной модели (имеет доступ ко всем ее методам), генерируется отдельно для каждой модели, манипулирует ее методами (может быть еще даже несуществующими), имитируя ее поведение заданое в спецификации. Основым инструментом для тестирования является утверждение(assert).

Утверждения могут быть разных типов, но общим для всех являеться сравнение уже известного параметра (согласно спецификации проекта) и параметра который возвращает модель в результате выполнения какого-либо метода.

Пример:утверждаем что количество юзеров в БД равняется 1:

def test_should_be_one_user_in_database
assert 1, User.count
end


Принято известный параметр указывать в утверждении слева. Результатом будет логичесое отрицание(тогда говорят про "падение теста", а имя метода укажет нам на вид ошибки - "в базе должен быть только один пользователь") или логическая "истина".

Таким образом: еще не реализовав модель, мы можем задать ее поведение (которое хочет заказчик). В тех местах где тест будет "падать" мы будем знать какая конкретно, и где, возникает ошибка. Если над проектом работает команда - то написание тестов просто необходимо, они тестируют все важные узлы системы, и программист сразу сможет увидеть что его код что-то нарушил в системе - и сразу все исправит.


Фикстуры: это файлы которые содержат записи для конкретных таблиц (в формате yml) . Реально, для разработки нужно 2 базы данных - для разработки и тестовая.

В БД разработки храняться реальные данные разрабатываемого проекта, которые программист тестирует у себя в браузере и вводит через формы.

В тестовой БД
хранятся данные на которых проганяются тесты, и эти данные как-раз и подгружаются каждый раз в БД из фикстур.

Существует две БД для того, чтобы тестовые данные были у всех разработчиков одинаковые и независили от того какие данные в проекта у каждого разработчика

Ruby on Rails. Модель в контексте MVC архитектуры построения веб-приложений

Детальное описание применения MVC архитектуры в Рельсах я решил все-таки начать с модели. На то есть ряд причин:
  • литература, предложенная мной для быстрого старта в Релсах, уже позволяет читателю немного представить в голове основной алгоритм построения приложения
  • Контроллер использует для работы модель, а значит описание начнем именно с нее:
О чем здесь пойдет речь:
  1. Как связана модель и таблица в БД
  2. Краткий старт в ActiveRecord
  3. Запросы к БД, задание параметров, работа с БД через интерфейс модели
  4. Связывание моделей
  5. Методы модели, методы экземпляров модели
  6. Особенности работы метода with_scope, или как мержить параметры запроса?
Приведенный выше список является своего рода названиями разделов, каждый из элементов которого будет разбит и описан в отдельных подпунктах


четверг, 17 июля 2008 г.

Кратко о MVC

Подробно MVC можно посмотреть в интернете, информации полно. Моя задача не описать все еше раз своими словами, а показать как это все работает в Релсах.

Для новичков я вкратце опишу что же это такое (немного детальнее посмотрите тут). Главная задача шаблона проектирования - отделить код от представления данных, тоесть как в поговорке "мухи отдельно, котлеты отдельно".

MVC (Модель-Представление-Контроллер) шаблон проектирования cостоит из 3 основных компонентов:
  • Модель - объектная прослойка между хранилищем информации(в частном случае база данных), которая предоставляет интерфейс для работы с сохраненной информацией (поиск, выборки, апдейты, удаления), а также методы котрые занимаются всевозможной обработкой для конкретной модели. Для Ruby on Rails в основе любой модели лежит супер мощный фреймворк - ActiveRecord. Который предоставляет все возможныйе базовые методы для работы с данными.
    Каждая таблица в БД имеет свою модель, где описаны связи между разными моделями (связи осуществляются на уровне моделей, а не таблиц!), а также методы работы с данными для конкретной модели(например User)
  • Контроллер - это ваш посредник между броузером(представлением данных) и моделью(БД), который занимается перехватом всех событий которые возникают в процессе работы и дальнейшей их обработкой. Используя роутер(файл содержащий информацию о том как обрабатывать события и куда направлять данные) контроллер "знает" какому экшну какого контроллера передать реакцию на событие (например сабмит формы) . Контроллер можно представить себе в виде продавца-консультанта, который выслушав ваши пожелания проведет вас в нужный отдел магазина.
  • Представления данных (View) - это последний компонент МVС корый занимается отображением данных. В рельсах используется для этого RHTML - помесь HTML и Ruby - RubyHTML. Где возможно использования руби вместе с обычным штмл. Основная задача представления - отобразить данные которые сформировал контроллер в результате на запрос юзера. Здесь позволено только фильтровать данные пришедшее с контроллера, обработка запрещается(вынесена в контроллер)
Таким образом работу типового рельсового приложения можна описать используя такую схему:
Запрос пользователя(в броузере) -> Обработка события и вызов нужного экшна в контроллере(с передачей пользовательских параметров запроса) -> Обработка в контроллере используя для работы с БД нужные Модели -> формирование ответа и передача в Представление -> отбражение запроса в нужном формате и выдача юзеру через броузер


Логические выражения в Руби

На своем горьком опыте я убедился, что спецификацию языка программирования надо читать очень и очень внимательно, сегодня столкнулся с проблемой, которая забрала у меня целый час времени.
Если вы есть новичек в Руби, то запомните одну истину - для Руби and i &&, or i || имеют разные приоритеты, ВСЕГДА юзайте только && i ||.
Рассмотри маленький примерчик котрый легко воспроизвести в ИРБ:
>> a = 2
>> puts (a==2) and false
=> true !!!!

>> puts (a==2)&& false
=> false

вторник, 8 июля 2008 г.

Ruby on Rails and MVC

Я начинаю цикл статей, которые максимально доступно помогут разобраться в архитектуре ModelViewController (MVC) применимо к Ruby on Rails. Я не утверждаю что эти статьи есть эталон, но постараюсь покрайней мере дать вам мое виденье данного вопроса.