RDR3T > Первая кровь: Структура приложения, первая модель и контроллер

марта 19, 2011  |  Published in Ruby on Rails, Ruby on Rails 3  |  32 Comments

В прошлой главе мы занимались тем, что создавали себе рабочую среду и создали свое первое приложение на Rails 3. В этой главе RubyDev Rails 3 Tutorial вы еще ближе познакомитесь со структурой приложения Rails, с Git и Bundler, а также создадите свою первую модель и контроллер и узнаете о том, что такое BDD и RSpec.

Прежде чем начать, давайте разберемся с некоторой терминологией: фреймворк Rails построен на основе нескольких принципах, которые очень важно знать любому программисту!

CoC (Convention over Configuration — Соглашение прежде конфигурации) — согласно этому принципу Rails содержит набор соглашений касательно структуры проекта, именования файлов и директорий, а также классов, модулей и так далее. Благодаря этому приложение на Rails избавляется от лишней настройки приложения, чем гораздо ускоряет разработку. Этот принцип вовсе не значит, что приложение на Rails не настраиваемо, Rails как раз таки один из самых гибких фреймворков для веб разработки. Этот принцип означает лишь, то, что мы избегаем лишней конфигурации и приближаемся к стандартизации.

REST (REpresentation State Transfer — представление состояния) — это один из подходов для предоставления данных. REST подразумевает такое понятие, как ресурс. Ресурсом могут быть, например посты, пользователи или комментарии пользователей нашего блога. Доступ к ресурсу и действиям над ним предоставляется по средствам специально регламентированных URL’ов, которые содержат в себе название ресурса и действие, которое мы хотим с этим ресурсом выполнить, например в нашем блоге:

http://localhost:3000/users — будет возвращать полный список зарегистрированных пользователей.
http://localhost:3000/users/1/ — будет отображать информацию о первом пользователе из всех пользователей.
http://localhoost:3000/users/new/ — будет предоставлять нам форму для регистраци нового пользователя.

и так далее.

CRUD (Create Read Update Destroy — Создание, Чтение, Обновление и Удаление) — это не принцип, а аббревиатура от основных действий, которые выполняются нашим приложением. Приложения, которые выполняют все четыре действия называются CRUD приложениями. Блог, разработкой которого мы занимаемся будет самым обыкновенным CRUD приложением:

Create — создание постов, пользователей и комментариев.
Read — получение постов, пользователей и комментариев из базы данных и их представление
Update — редактирование личной информации пользователями, редактирование постов и комментариев.
Delete — удаление нежелательных пользователей, постов и комментариев из базы данных.

В Rails CRUD очень сильно связан с REST.

DRY (Don’t Repeat Yourself — Не повторяйся!) — согласно этому принципу вы должны максимально сократить повторяющиеся фрагменты кода. В Rails 3 DRY используется повсеместоно, это и введение в 3 версии промежуточного контроллера ApplicationController, от которого наследуются все контроллеры, scope в моделях, partials в представлениях и так далее.

MVC (Model View Controller — Модель, Представление и Контроллер) — архитектурный шаблон разработки программного обеспечения, который заключается в разбиении программного кода на три независимых друг от друга части: Модель, Представление и Контроллер, каждая из которых занимается своими специфичными задачами. MVC составляет основную часть нашего приложения и основную рабочую область программиста. В нашем приложении blog MVC, находятся в директории blog/app/ в поддиректориях с соответствущими названиями.

Модель реализует бизнес логику, то есть работу с данными. В Rails модель реализована в виде двух фреймворков: ActiveRecord и ActiveSupport. В нашем учебнике мы основное внимание будем уделять работе с ActiveRecord, который используется для работы с реляционными базами данных. ActiveSupport предназначен для использования на хранилищах данных предоставляющих RESTful API.

Контроллер — часть приложения, которая занимается обработкой пользовательского запроса и передачей пользователю ответа. В Rails контролер берет на себя функции взаимодействия с Rack, маршрутизации, реализации REST архитектуры, и так далее. В Rails контроллеры реализованы с использованием фреймворка ActionPack, который также отвечает и за представление.

Представление — это та часть приложения, которую реально видит пользователь. Само приложение для пользователя представляется какой-то магией, доступ к которой он имеет через веб интерфейс — представление.

Scaffold — метод программирования, который заключается в генерации базового (простейшего CRUD) функционала.

BDD (Behaviour-Driven Development — разработка основанная на поведении) — это подход к разработке программного обеспечения при котором сначала создаются спецификации на определеный код, то есть описывается его поведение, а уже затем пишется сам код по этим спецификациям. BDD позволяет тщательно продумать архитектуру приложения, а разработанные спецификации (спеки) многократно использовать для проверки корректной работы приложения при изменении среды разработки или при добавлении нового функционала.

RSpec — это фреймворк, который используется для написания спеков на специальном DSL.

Итак, мы разобрались с некоторыми очень важными для понимания принципов работы и философии Rails терминами, и теперь, прежде, чем начать, давайте немножко упростим себе работу в будущем и добавим файл .rvmrc в корневую директорию нашего проекта со следующим содержимым:

rvm_gemset_create_on_use_flag=1
rvm use 1.9.2@blog

.rvmrc - это файл конфигурации RVM, который проверяется RVM каждый раз, когда вы посещаете какую-либо директорию. В нашем .rvmrc во второй строке указано, что RVM необходимо переключиться на gemset blog, а в первой строке мы установили опцию, согласно которой gemset blog будет создан автоматически, если не был создан ранее. Теперь нам не нужно все время помнить о переключении на нужную версию Ruby и нужный набор gem’ов, RVM помнит все за нас!

Давайте перед началом разработки произведем некоторые настройки нашего приложения, а именно подключим RSpec для написания спеков. Для этого нам необходимо открыть файл зависимостей нашего приложения и добавить rspec и rspec-rails. Gemfile — файл зависимостей приложения, который используется Bundler’ом — менеджером зависимостей для хранения зависимостей и подключения необходимых Gem’ов в приложение.

Если вы откроете файл Gemfile, то увидите там следующий код:

source 'http://rubygems.org'

gem 'rails', '3.0.5'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

gem 'sqlite3'

# Use unicorn as the web server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
# gem 'ruby-debug'
# gem 'ruby-debug19', :require => 'ruby-debug'

# Bundle the extra gems:
# gem 'bj'
# gem 'nokogiri'
# gem 'sqlite3-ruby', :require => 'sqlite3'
# gem 'aws-s3', :require => 'aws/s3'

# Bundle gems for the local environment. Make sure to
# put test-only gems in this group so their generators
# and rake tasks are available in development mode:
# group :development, :test do
#   gem 'webrat'
# end

Это специальный DSL, который используется в Bundler для описания зависимостей приложения, то есть записи информации о том, какие библиотеки и gem’ы необходимы нашему приложению для работы и где их необходимо брать.

Метод source используется для указания ресурса откуда следует скачивать gem’ы, в нашем случае это официальное хранилище RubyGems.org.

Метод gem используется для определения gem’а. В качестве первого аргумента он принимает название gem’а, а в качестве второго — его версию.

Параметр :git метода gem используется для указания репозитория с которого необходимо скачать gem. Это полезно потому, что в официальных репозиториях хранятся более свежие версии, чем на RubyGems. Если вы используете этот параметр, то вы также можете использовать параметры :branch, :tag и :rel для более точного указания того, какую версию и ветку вам необходимо установить.

Параметр :path метода gem, который не показан в примеры выше, используется для указания локального адреса на gem, это необходимо, например, когда вы хотите использовать свой собственный gem, который находится на вашей рабочей машине.

Параметр :require метода gem используется для создания вложенной зависимости, то есть для установки данного gem’а необходимо установить gem, который указан в :require.

Метод group используется для создания именованных групп зависимостей, которые могут использоваться для селективной работы с зависимостями, то есть по принципу их принадлежности к определенной группе. Вместо использования метода group вы можете использовать параметр :group метода gem, для указания группы персонально для каждого gem’а.

Я рассказываю об этом потому, что сейчас вам следует отредактировать файл Gemfile добавив туда rspec и rspec-rails и удалив все лишнее (то, что закоментировано). Попробуйте сделать это самостоятельно и проверьте с моим вариантом, который представлен ниже:

source 'http://rubygems.org'

gem 'rails', '3.0.5'
gem 'sqlite3'

group :development, :test do
  gem 'webrat'
  gem 'rspec'
  gem 'rspec-rails'
end

Теперь, когда Gemfile отредактирован, необходимо выполнить команду bundle install, для того, чтобы Bungler установил все добавленные в Gemfile зависимости:

$ bundle install
Fetching source index for http://rubygems.org/

Installing diff-lcs (1.1.2)
Installing nokogiri (1.4.4) with native extensions

Installing rspec-core (2.5.1)
Installing rspec-expectations (2.5.0)
Installing rspec-mocks (2.5.0)
Installing rspec (2.5.0)
Installing rspec-rails (2.5.0)

Installing webrat (0.7.3)
Your bundle is complete! It was installed into /home/vladimir/.rvm/gems/ruby-1.9.2-p0@blog

Итак, RSpec и RSpec-Rails установлены, однако нам следует установить RSpec в контекст нашего приложения blog. Для этого необходимо запустить Rails генератор для RSpec rails g rspec:install:

$ rails g rspec:install
create  .rspec
create  spec
create  spec/spec_helper.rb

Теперь мы можем использовать RSpec для написания спецификация для нашего приложения, а также для автоматической генерации спецификаций для базового (генерируемого scaffold’ом) функционала.

Давайте создадим с помощью генератора Scaffold первую модель в нашем приложении User. Модель User будет представлять собой зарегистрированного в нашем блоге пользователя. Для генерации scaffold’а необходимо выполнить следущую команду в консоли:

$ rails g scaffold User name:string lname:string login:string password:string email:string
invoke  active_record
create    db/migrate/20110310133600_create_users.rb
create    app/models/user.rb
invoke    rspec
create      spec/models/user_spec.rb
route  resources :users
invoke  scaffold_controller
create    app/controllers/users_controller.rb
invoke    erb
create      app/views/users
create      app/views/users/index.html.erb
create      app/views/users/edit.html.erb
create      app/views/users/show.html.erb
create      app/views/users/new.html.erb
create      app/views/users/_form.html.erb
invoke    rspec
create      spec/controllers/users_controller_spec.rb
create      spec/views/users/edit.html.erb_spec.rb
create      spec/views/users/index.html.erb_spec.rb
create      spec/views/users/new.html.erb_spec.rb
create      spec/views/users/show.html.erb_spec.rb
invoke      helper
create        spec/helpers/users_helper_spec.rb
create      spec/routing/users_routing_spec.rb
invoke      rspec
create        spec/requests/users_spec.rb
invoke    helper
create      app/helpers/users_helper.rb
invoke      rspec
invoke  stylesheets
identical    public/stylesheets/scaffold.css

Как видите, генератор scaffold за нас создал множество файлов в том числе файлы MVC, файл хэлпера, файл миграции и спецификации для базового функционала, которые содержатся в директории /blog/spec/  и структура которых очень напоминает структуру наших MVC файлов. Такая схожесть присутствует неспроста, все это воплощение того самого принципа Convention over Configuration, о котором я упоминал выше, благодаря которому не нужно специально прописывать местоположение и названия спеков.

Создание спеков мы оставим на следущую часть учебника, а сейчас мы займемся тем, что немного поиграем с MVC файлами нашего приложения. Для начала выполните в консоли следущую Rake задачу:

$ rake db:migrate
(in /home/vladimir/proj/blog)
==  CreateUsers: migrating ====================================================
— create_table(:users)
-> 0.0024s
==  CreateUsers: migrated (0.0026s) ===========================================

Rake задача db:migrate запускает файл миграции, который был сгенерирован генератором scaffold для нашей модели User. Файл миграции — это файл содержащий обычный код на Ruby и состоит всего из двух методов. Ниже приведена миграция для модели User (blog/db/migrate_create_user.rb):

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :name
      t.string :lname
      t.string :login
      t.string :password
      t.string :email

      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

Как видно в файле миграции определяется два метода .up и .down. Метод .up используется для внесения в структуру базы данных каких-либо изменений, а метод .down используется для их отката. Выполнив комманду rake db:migrate мы запустили этот файл миграции благодаря чему в базе данных была создана таблица users со следующими столбцами:

name — имя пользователя, имеет тип string
lname - фамилия пользователя, имеет тип string
login — логин для входа, имеет тип string
password — пароль для входа, имеет тип string

created_at и updated_at — в этих столбцах хранятся данные о дате и времени создания записи и ее обновлении, в отличие от остальных столбцов эти столбци создаются с помощью метода timestamps и при запуске генератора scaffold мы не указывали необходимость их создания.

Также создается столбец id, который хранит уникальный, целочисленный идентификатор записи, так называемый первичный ключь, по которому мы можем обратиться к одной единственной записи. Необходимость создания столбца id не упоминается даже в файле миграции, тем не менее он будет создан автоматически. Это все, что вам на данный момент необходимо знать о миграциях, мы поговорим о них позже в одной из следущих глав RubyDev Rails 3 Tutorial.

Если вы решили отойти от моих рекомендаций и решили использовать вместо SQLite другую базу данных, например MySQL, то перед Rake задачей db:migrate вам следует выполнить задачу rake db:create:

rake db:create
rake db:migrate

Задача db:create выполняет запрос к серверу базы данных на создание новой базы данных для вашего приложения. Мы не используем db:create при работе с SQLite потому, что база создается автоматически, вместе с нашим приложением и хранится в файле blog/db/development.sqlite3, название соответствет окружению в котором база данных используется.

Чтобы запустить приложение Rails, необходимо в консоли набрать следущую команду:

$ rails s
=> Booting WEBrick
=> Rails 3.0.5 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-03-10 16:18:12] INFO  WEBrick 1.3.1
[2011-03-10 16:18:12] INFO  ruby 1.9.2 (2010-08-18) [i686-linux]
[2011-03-10 16:18:12] INFO  WEBrick::HTTPServer#start: pid=3968 port=3000

Теперь сервер WEBrick запущен и прослушивает 3000 порт. Если мы обратимся к этому порту, то будет запущено наше Rails приложение и мы сможем увидить его в браузере. Откройте свой любимый браузер (у меня это FireFox и Chrome) и проследуйте по адресу http://localhost:3000/users/.

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

Если вы вернетесь обратно к окну консоли в котором запустили Rails приложение, то увидите что-то интересное, например:

Started GET «/users» for 127.0.0.1 at 2011-03-11 14:56:49 +0200
Processing by UsersController#index as HTML
User Load (32.7ms)  SELECT «users».* FROM «users»
Rendered users/index.html.erb within layouts/application (23.1ms)
Completed 200 OK in 234ms (Views: 71.6ms | ActiveRecord: 32.7ms)

Это удобное представление того, что называется Уведомлениями (Notifications) в Rails. Эта информация также записывается в лог-файл по адресу blog/log/development.log. Логи очень часто используются программистами для выявления ошибок в работе приложения. Уведомления фиксируют каждое событие происходящее в вашем приложении, начиная от получения запроса и заканчивая рендерингом представлений. Более подробно об Уведомлениях мы поговорим в одной из следущих глав.

Каждая открытая страница: список всех пользователей, создание нового пользователя, редактирование пользователя реализованы в контроллере в виде экшенов (action). Экшен — это самый обыкновеный метод, его отличие от других методов заключается в том, что пользователь приложения работает с ним напрямую через веб интерфейс, то есть события пользователя обрабатываются экшенами. Если вы откроете файл контроллера blog/app/controllers/users_controller.rb, то вы обнаружите в нем достаточно приличного размера код, который был сгенерирован генератором scaffold.

Как выглядит UsersController у меня:

class UsersController < ApplicationController
  # GET /users
  # GET /users.xml
  def index
    @users = User.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @users }
    end
  end

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

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @user }
    end
  end

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

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @user }
    end
  end

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

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

    respond_to do |format|
      if @user.save
        format.html { redirect_to(@user, :notice => 'User was successfully created.') }
        format.xml  { render :xml => @user, :status => :created, :location => @user }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
      end
    end
  end

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

    respond_to do |format|
      if @user.update_attributes(params[:user])
        format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
      end
    end
  end

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

    respond_to do |format|
      format.html { redirect_to(users_url) }
      format.xml  { head :ok }
    end
  end
end

index, show, new, edit, create, update, destroy — это семь стандартных экшенов, кстати, экшены согласно терминологии REST еще называют методами ресурсов.

Метод index занимается представлением списка всех существующих пользователей, записи о которых содержатся в таблице users базы данных нашего приложения. Представление этого экшена вы можете увидеть по адресу http://localhost:3000/users/.

Метод show занимается тем, что отображает информацию об одном определенном пользователе, чей id был передан в адресной строке, например, обращаясь по адресу http://localhost:3000/users/1/, адрес будет обработан таким образом, что будет выполнен экшен show, который покажет пользователя с id = 1.

Метод new занимается тем, что предоставляет пользователю страницу с формой для создания нового пользователя. Этот экшен выполняется при запросе по адресу http://localhost:3000/users/new.

Метод edit занимается предоставлением формы редактирования информации о пользователе, то есть предоставляется та же форма, но ее поля уже заполненны данными об определенном пользователе, чей id передается в URL: http://localhost:3000/users/1/edit/ перейдя по этому адресу вы получите форму заполненную данными о пользователе с id = 1, которые вы можете изменить и сохранить.

Метод create занимается созданием нового пользователя, он не имеет собственного представления, он просто добавляет запись о новом пользователе в базу и перенапрявляет нас к экшену show, который отобразит информацию о только что добавленном пользователе.

Метод update аналогичен методу create, единственное отличие заключается в том, что в место создания новой записи, перезаписывается уже имеющаяся.

Метод destroy используется для удаления определенной записи, не имеет собственного представления и производит после удаления записи перенаправление на экшен index.

В коде контроллера UsersController, который представлен выше, вы можете увидить комментарии к экшенам в которых указывается метод передачи запроса. Всего таких методов четыре: GET, POST, PUT, DELETE. Метод передачи запроса GET может использоваться при передаче запроса к любому из экшенов, у которых имеется представление, метод POST используется для передачи данных из формы,  метод PUT полностью похож на POST с тем лишь отличием, что данные передаваемые этим методом используются только для обновления существующих записей, метод DELETE, как понятно из названия используется для передачи какой-нибудь информации, которую следует удалить, например id пользователя, которого нужно удалить из списка пользователей блога. Сами GET, POST, PUT, DELETE ничего не делают с записями, они просто позволяют Rails понять, к какому экшену идет обращение. GET отличается от POST тем, что параметры передаваемые на сервер видны в адресной строке. Методы DELETE и PUT не поддерживаются браузерами напрямую, пожтому Rails их эмулирует.

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

.respond_to используется для создания представлений (ответа на обращение пользователя) в различных форматах. По умолчанию представление создается в виде html страницы, однако мы можем предоставить пользователю запрашиваемые им данные в других форматах, например: xml, json, text, js, csv, yaml, rss, atom так далее. Давайте рассмотрим работу с методом .respond_to на примере экшена index.

# GET /users
# GET /users.xml
def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
  end
end

Экшен index предоставляет нам два формата представления информации: html и xml. Чтобы получить стандартное html представление нам необходимо обратиться по адресу http://localhost:3000/users, если же вам необходимо обратиться к представлению в любом формате, кроме html, то вам следует явно указать формат в URL, например, давайте получим xml представление списка пользователей:

http://localhost:3000/users.xml
или
http://localhost:3000/users?format=xml

В результате такого запроса мы получим xml код:

<users type="array">
  <user>
    <created-at type="datetime">2011-03-10T14:25:15Z</created-at>
    <email>egotraumatic@gmail.com</email>
    <id type="integer">1</id>
    <lname>Melnik</lname>
    <login>egoholic</login>
    <name>Vladimir</name>
    <password>4*%$@hGY^31</password>
    <updated-at type="datetime">2011-03-10T14:25:15Z</updated-at>
  </user>

  <user>
    <created-at type="datetime">2011-03-11T13:39:08Z</created-at>
    <email>vas.pet@gmail.com</email>
    <id type="integer">2</id>
    <lname>Petrov</lname>
    <login>vasyapetrov</login>
    <name>Vasya</name>
    <password>vaspass</password>
    <updated-at type="datetime">2011-03-11T13:39:08Z</updated-at>
  </user>
</users>

Теперь давайте попробуем получить yaml представление, для этого необхдимо обратиться по адресу: http://localhost:3000/users.yaml

File not found
Firefox can’t find the file at http://localhost:3000/users.yaml.

Страница с yaml представлением не найдена. Это произошло потому, что в экшене index мы не объявили возможность предоставления представления в этом формате. Для того, чтобы добавить возможность получать представление в формате yaml, нам следует привести экшен index к следущему виду:

# GET /users
# GET /users.xml
def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
    format.yaml { render :yaml => @users}
  end
end

Сохраняем изменения и повторяем попытку обращения к http://localhost:3000/users.yaml. Вместо ожидаемого yaml представления мы видим ошибку:

Template is missing

Missing template users/index with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:yaml], :locale=>[:en, :en]} in view paths «/home/vladimir/proj/blog/app/views»

Это связано с тем, что в описанный нами способ мы можем добавить представление не для всех форматов, например с yaml возникает небольшая проблема. Чтобы избавится от нее следует немного поправить экшен index, так, чтобы он имел следущий вид:

# GET /users
# GET /users.xml
def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
    format.yaml { render :text => @users.to_yaml}
  end
end

Теперь, если мы обратимся по адресу http://localhost:3000/users.yaml, то получим представление в формате yaml — файл с названием users.yaml.

Попробуйте для запоминания создать представления для других форматов, например rss, json и js. Если вы выполните это задание, что ваш экшен index должен иметь следущий вид:

def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
    format.yaml { render :text => @users.to_yaml }
    format.json { render :json => @users }
    format.rss  { render :rss => @users }
    format.js   { render :js => "alert('hello');", :content_type => 'text/javascript' }
  end
end

Вы уже догадались, что все, что делает метод .respond_to — это выдает представление для определенного в блоке формата. Заметьте, format.<format> не занимается непосредственно генерацией представления в определенном формате, эта генерация выполняется в передаваемом блоке кода еще одним очень важным и популярным методом .render. Это легко доказуемо, попробуйте переписать экшен index следущим образом:

def index
  @users = User.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @users }
    format.json { render :xml => @users }
  end
end

При обращении в http://localhost:3000/users.json вам будет предложен файл users.json, который будет содержать xml код.

Вы уже немного разобрались с контроллерами, экшенами и такими методами, как respond_to и render, теперь настало время познакомиться с моделями!

Модели нашего приложения находятся в папке blog/app/models. В данный момент мы имеем одну модель user.rb, которая была сгенерирована генератором scaffold.

Код модели User:

class User < ActiveRecord::Base
end

Модель User на данный момент обладает совсем простым кодом, однако за этой простотой кроется могущество унаследованное от ActiveRecord::Base.

Таблица users -> Запись -> Модель User -> Сущность user.

Каждая модель работает с соответствующей ей таблицей в базе данных, для модели User это таблица users. Модель дает возможность обращаться к данным в таблице и выполнять с ними различные операции. В этой статье мы рассмотрим простейшие из них — операции выборки данных, сохранения, обновления, удаления и сортировки записей. Давайте рассмотрим простейшие операции над базой данных. Для этого необходимо запустить консоль Rails:

$ rails c

Получение атрибутов (полей) экземпляра модели:

User
# => User(id: integer, name: string, lname: string, login: string, password: string, email: string, created_at: datetime, updated_at: datetime) 

Создание новой сущности (объекта/записи)

user = User.new #пустая сущность
#=> #<User id: nil, name: nil, lname: nil, login: nil, password: nil, email: nil, created_at: nil, updated_at: nil>

Определяем объекту (сущности) свойства с помощью методов аксессоров:

user.name = 'James'
user.lname = 'Bond'
user.login = 'jbond'
user.password = 'jbpassword'
user.email = 'jbond@england.org'

Альтернативные варианты создания экземпляра модели:

user = User.new do |u|
u.name = 'Frank'
u.lname = 'Black'
u.login= 'frack'
u.password = '123'
u.email = 'ee@ee.ru'
end
#=> #<User id: nil, name: "Frank", lname: "Black", login: "frack", password: "123", email: "ee@ee.ru", created_at: nil, updated_at: nil>

#можно и так:
user = User.new( name:'Frank', lname:'Black', login:'frack', password:'123', email:'ee@ee.ru'}
#=> #<User id: nil, name: "Frank", lname: "Black", login: "frack", password: "123", email: "ee@ee.ru", created_at: nil, updated_at: nil>

Производим сохранение сущности в базу данных

user.save
#=> true

Получаем доступ к атрибутам (свойствам)

user.name # получаем значение свойств
#=> "James"

user.email
#=> "jbond@england.org"

user.email
#=> "bam-bam@mail.ru"

user.created_at
#=> Fri, 11 Mar 2011 15:48:12 UTC +00:00

user.email = 'bam-bam@mail.ru' # устанавливаем/меняем значение свойств
#=> "bam-bam@mail.ru"

Получаем коллекцию всех записей из базы данных:

users = User.all
#=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">,

#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">,

#<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">]

Делаем выборку записей из базы данных по определенному (определенным) id с помощью #find:

user = User.find(1) # выбираем запись с id = 1
=> #<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">

User.find(1,2) # выбираем записи с id = 1 и 2
=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">,

#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">]

Метод #find имеет множество различных параметров, которые будут отменены в Rails 3.1, забигая на перед скажу, что в данном учебнике я также отказываюсь от их использования.

Делаем выборку по определенному свойству записи c помощью #where:

user = User.where(:name => 'Vladimir') # получаем сущность у которой свойство name = "Vladimir"
=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">]

user = User.where(:lname => 'Petrov', name => 'Vasya') # получаем сущность у которой свойство lname = "Petrov" и name = "Vasya"
=> [#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">]

Передаем в метод #where SQL-фрагмент:
[/ruby]
user = User.where(‘name IN («Vasya», «James»)’)
=> [#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">,

#<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">] [/ruby]

Сортировка записей с помощью #order:
Сортировка по id в обратном порядке. По умолчанию записи отсортированы по id.

user = User.where(:name => ["Vasya", "James", "Vladimir"]).order('id DESC')
=> [#<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">,

#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">,

#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">]

Сортировка по значению lname в алфавитном порядке:

user = User.order('lname ASC')
=> [#<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">,

#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">,

#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">]

Метод #first

User.first # получаем первую запись из коллекции
=> #<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">

.first возвращает первую запись из коллекции, но не запись с минимальным id, как то можно было подумать из первого примера:

User.order('lname ASC').first
=> #<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">

Метод #last
Метод #last — полная противоположость метода #first и возвращает последний элемент коллекции сущностей (экемпляров модели):

User.last
=> #<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">

User.order('lname ASC').last
=> #<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">

Выборка с помощью методов #find_by_<something> и #find_by_<something>_and_<something_else>
find_by_* методы — это динамически генерируемые при помощи method_missing методы, в именах которых присутствуют названия параметров по которым происходит выборка, например:

User.find_by_lname("Petrov")
=> #<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">

User.find_by_lname_and_name("Melnik", "Vladimir")
=> #<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">

Вы можете использовать бесконечно большое количество *_and_ частей, например: find_by_name_and_lname_and_email и так далее.

Выборка данных при помощи #find_all_by_<something> и #find_all_by_<something>_and_<something_else> методов.
find_all_by_* методы абсолютно идентичны методам find_by_*, c тем лишь отличием, что find_by_* методы возвращают первую запись соответствующую условию поиска, а методы find_all_by_* возвращают массив всех соответствующих запросу записей:

User.find_all_by_lname("Melnik")
=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">]

User.find_all_by_lname_and_name("Melnik", "Vladimir")
=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">]

Ограничение количества выбираемых записей с помощью метода #limit
Метод #limit используется для ограничения количества записей, которые мы выбираем из базы данных. Это необходимо, например, когда мы не хотим выводить в нашем приложении список всех пользователей, а хотим показать список только последних зарегистированных:

User.order('created_at DESC').limit(1) # получаем последнего зарегичтрированного пользователя
=> [#<User id: 3, name: "James", lname: "Bond", login: "jbond", password: "jbpassword", email: "jbond@england.org", created_at: "2011-03-11 15:48:12", updated_at: "2011-03-11 15:48:12">]

User.limit(2) # получаем ползователей с id = 1 и 2 (записи отсортированы по умолчанию по id)
=> [#<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">,

#<User id: 2, name: "Vasya", lname: "Petrov", login: "vasyapetrov", password: "vaspass", email: "vas.pet@gmail.com", created_at: "2011-03-11 13:39:08", updated_at: "2011-03-11 13:39:08">]

Удаление записей или объектов при помощи метода #destroy:

user = User.find(1)
=> #<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">
user.destroy
=> #<User id: 1, name: "Vladimir", lname: "Melnik", login: "egoholic", password: "4*%$@hGY^31", email: "egotraumatic@gmail.com", created_at: "2011-03-10 14:25:15", updated_at: "2011-03-10 14:25:15">

Существует также метод #destroy_all для удаления целой коллекции записей.

Мы рассмотрели далеко не все методы и не все возможности, которые доступны программисту в моделях, но вы узнаете намного больше в следующих частях учебника RubyDev Rails 3 Tutorial.

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

class UsersController < ApplicationController

  def index
    @users = User.all
  end
 
  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def edit
    @user = User.find(params[:id])
  end

  def create
    @user = User.new(params[:user])
    @user.save and redirect_to(@user, :notice => 'User was successfully created.')
  end

  def update
    @user = User.find(params[:id])
    @user.update_attributes(params[:user]) ?
      redirect_to(@user, :notice => 'User was successfully updated.') : render(:edit)
  end

  def destroy
    @user = User.find(params[:id])
    @user.destroy and redirect_to(users_url)
  end
end

Если присмотреться к экшенам, то вы увидите, что мы используем в них уже извесную нам модель User и известные нам методы: #new, #find и #all.

Методам #new и #find в качестве аргумента передается некий элемент хэша возвращаемого методом params. Этот, возвращаемый params хэш содержит в себе набор пар ключь — значение, которые повсеместно используются в нашем приложении. Например экшену create из params доступен следущий хэш:

{"utf8"=>"✓", "authenticity_token"=>"MGOOd+bIfWbrCCJjiQn9+H1c2VH+yMQNSAN4lNWmcEo=", "user"=>{"name"=>"Yehuda", "lname"=>"Katz", "login"=>"wykatz", "password"=>"123", "email"=>"ykatz@railscoreteam.com"}, "commit"=>"Create User", "action"=>"create", "controller"=>"users"}

Экшен берет из него вложенный хэш params[:user]:

{"name"=>"Yehuda", "lname"=>"Katz", "login"=>"wykatz", "password"=>"123", "email"=>"ykatz@railscoreteam.com"}

И передает этот хэш в качестве аргумента методу User.new чем создает новый экземпляр модели User, после чего сохраняет с помощью метода User.save запись о новом пользователе в базу данных.

Таперь давайте рассмотрим то, как все эти данные представляются пользователю. Для этого вам следует проследовать в директорию app/views/ где хранятся шаблоны представлений нашего блога. В app/views вы можете найти еще две директории: layouts/ и users/. Обратити внимание на директорию /views/users она называется так же как и наш контроллер. Это еще одно проявление принципа Convention over Configuration, Rails по умолчанию ищет одноименную с контроллером директорию, которая содержит шаблонные файлы представлений (template). В папке /views/layouts/ содержится файл application.html.erb — это файл макета представления (layout). Макет представления (layout) является еще одним проявлением принципа DRY. Мы выносим в макет код представления (код разметки и код логики представления), который является общим для всех шаблонов. При желании мы можем объявить для некоторых контроллеров их собственные макеты, но об этом речь пойдет уже в одной из следующих глав RubyDev Rails 3 Tutorial.

Все, что вам необходимо знать на данном этапе — это то, что контроллер использует приписанный ему макет (layout), который в свою очередь включает в себя при помощи выражения yield соответствующий экшену шаблон, но это только в том случае, когда шаблон не указан явно. Ниже представлены файл макета /views/layouts/application.html.erb:

<!DOCTYPE html>
<html>
  <head>
    <title>Blog</title>
    <%= stylesheet_link_tag :all %>
    <%= javascript_include_tag :defaults %>
    <%= csrf_meta_tag %>
  </head>
  <body>

    <%= yield %>

  </body>
</html>

И файл шаблона для экшена new /views/users/new.html.erb

<h1>New user</h1>

<%= render ‘form’ %>

<%= link_to ‘Back’, users_path %>

Файлы представлений написаны на языке ERB (Embeded Ruby), который являестся ни чем иным, как обычным HTML со вставкой в него выражений языка Ruby в специальных <% %> скобках, и используется в Rails по умолчанию в качестве языка шаблонов. Существуют и другие языки шаблонов, которые мы рассмотрим в главе посвященной представлениям, одним изтаких будет широко применяемый HAML.

Итог
Давайте проверим какие изменения произошли в нашем приложении, для этого необходимо выполнить в консоли некоторые команды Git’а.

Команда git status сообщит нам текущее положение рабочей области:

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#    modified:   .gitignore
#    modified:   Gemfile
#    modified:   config/routes.rb
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#    .rspec
#    .rvmrc
#    Gemfile.lock
#    app/controllers/users_controller.rb
#    app/helpers/users_helper.rb
#    app/models/
#    app/views/users/
#    db/migrate/
#    db/schema.rb
#    public/stylesheets/scaffold.css
#    spec/
#    test/fixtures/
#    test/functional/
#    test/unit/
no changes added to commit (use "git add" and/or "git commit -a")

Как видно из результатов выполнения команды git status, у нас появились новые файлы, слежение за которыми еще не производится Git’ом. Чтобы Git начал следить за их состоянием, нам следует выполнить:

$ git add .
vladimir@egoholic:~/proj/blog$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#    modified:   .gitignore
#    new file:   .rspec
#    new file:   .rvmrc
#    modified:   Gemfile
...
#    new file:   test/unit/user_test.rb

Мы добавили новые файлы в список отслеживаемых Git’ом файлов и нам осталось сделать коммит (снимок состояния рабочей области), для этого выполняем уже известную нам команду:

$ git commit -m ‘add resource Users’
[master f29d0da] add resource Users
31 files changed, 765 insertions(+), 31 deletions(-)
create mode 100644 .rspec
create mode 100644 .rvmrc
rewrite Gemfile (91%)
create mode 100644 Gemfile.lock

create mode 100644 test/unit/user_test.rb

Снимок состояния рабочей области выполнен и команда git commit сообщила нам о том, что было изменено 31 файл, добавлено 765 строк кода и 31 строка кода удалена. После создания коммита, необходимо выполнить команду git push для того, чтобы придать удаленному репозиторию актуальное состояние:

$ git push origin
Counting objects: 70, done.
Compressing objects: 100% (45/45), done.
Writing objects: 100% (57/57), 9.54 KiB, done.
Total 57 (delta 5), reused 0 (delta 0)
To git@github.com:egoholic/blog.git
f9dd227..f29d0da  master -> master

Tags: , , , , , , ,

Responses

  1. enRicke says:

    марта 19, 2011 at 11:10 (#)

    Стоит сказать автору спасибо за такую большую статью. Жду с нетерпением поста об RSpec.

  2. alp says:

    марта 21, 2011 at 08:40 (#)

    Стоящая статья,как в прочем и весь блог

  3. stranniknavsegda says:

    марта 21, 2011 at 20:33 (#)

    Просто нет слов — одни эмоции! Ваши статьи по рельсам интересно читать, но вот лично у меня как любителя все попробовать своими руками возник вопрос по srepond_to и генерации rss: format.rss { render :rss => @users } явно не прокатыват, вылетает такое чудо: Missing template users/index with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:rss], :locale=>[:en, :en]}

    конечно я нашел способ генерации валидного rss (), но все же… ))

  4. admin says:

    марта 22, 2011 at 01:18 (#)

    stranniknavsegda, спасибо за информацию. Действительно я несколько упустил материал об RSS и ATOM, думаю добавлю это при обновлении статьи до следущей версии и будет еще целая отдельная статья про то, как организовать подписку на блог. Спасибо за внимание к жизни блога и помощь в усовершенствовании текстов учебника и исправлении ошибок и недочетов.

  5. ip82 says:

    марта 23, 2011 at 16:44 (#)

    Статья супер, жалко только что серии так медленно выходят :-) С нетерпением жду продолжение про RSpec, никак не могу въехать в разработку через тестирование…

  6. admin says:

    марта 23, 2011 at 23:34 (#)

    ip82, спасибо за отзыв, статьи бы выходили хоть каждые два дня, но у меня есть работа, учеба, да и люди, с которыми я договорился о вычитке и редактировании несколько подводят, кроме того, со многим, что пишется в статьях, с сам либо слабо знаком, либо совсем не знаком и приходится погружаться и подробно разбираться. Следущая статья, должна будет выйти на следущей неделе, там будет 50% статьи про RSpec и 50 про создание нового ресурса и связи (ассоциации) между ресурсами. Статья будет очень объемная и достаточно подробная, однако в справочнике будут так же присутствовать 2 отдельные статьи по RSpec и ассоциациям между моделями.

  7. Fedor says:

    марта 26, 2011 at 20:00 (#)

    Спасибо за столь интересный и качественный блог и статьи!
    Информации по рору на русском не так много как хотелось бы.

  8. systemIV says:

    апреля 1, 2011 at 12:34 (#)

    Прошу помощи:
    При bundle install сначало загружает гемы, а потом:

    systemiv@systemiv-900AX:~/rails/blog$ bundle install
    Fetching source index for
    ^C
    Quitting…
    systemiv@systemiv-900AX:~/rails/blog$ bundle install
    Fetching source index for
    Using rake (0.8.7)
    Using abstract (1.0.0)
    Using activesupport (3.0.5)
    Using builder (2.1.2)
    Using i18n (0.5.0)
    Using activemodel (3.0.5)
    Using erubis (2.6.6)
    Using rack (1.2.2)
    Using rack-mount (0.6.14)
    Using rack-test (0.5.7)
    Using tzinfo (0.3.25)
    Using actionpack (3.0.5)
    Using mime-types (1.16)
    Using polyglot (0.3.1)
    Using treetop (1.4.9)
    Using mail (2.2.15)
    Using actionmailer (3.0.5)
    Using arel (2.0.9)
    Using activerecord (3.0.5)
    Using activeresource (3.0.5)
    Using bundler (1.0.10)
    Installing diff-lcs (1.1.2)
    Installing nokogiri (1.4.4) with native extensions /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:533:in `rescue in block in build_extensions’: ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)

    /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/bin/ruby extconf.rb
    checking for libxml/parser.h… no
    ——
    libxml2 is missing. please visit for help with installing dependencies.
    ——
    *** extconf.rb failed ***
    Could not create Makefile due to some reason, probably lack of
    necessary libraries and/or headers. Check the mkmf.log file for more
    details. You may need configuration options.

    Provided configuration options:
    —with-opt-dir
    —without-opt-dir
    —with-opt-include
    —without-opt-include=${opt-dir}/include
    —with-opt-lib
    —without-opt-lib=${opt-dir}/lib
    —with-make-prog
    —without-make-prog
    —srcdir=.
    —curdir
    —ruby=/home/systemiv/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
    —with-zlib-dir
    —without-zlib-dir
    —with-zlib-include
    —without-zlib-include=${zlib-dir}/include
    —with-zlib-lib
    —without-zlib-lib=${zlib-dir}/lib
    —with-iconv-dir
    —without-iconv-dir
    —with-iconv-include
    —without-iconv-include=${iconv-dir}/include
    —with-iconv-lib
    —without-iconv-lib=${iconv-dir}/lib
    —with-xml2-dir
    —without-xml2-dir
    —with-xml2-include
    —without-xml2-include=${xml2-dir}/include
    —with-xml2-lib
    —without-xml2-lib=${xml2-dir}/lib
    —with-xslt-dir
    —without-xslt-dir
    —with-xslt-include
    —without-xslt-include=${xslt-dir}/include
    —with-xslt-lib
    —without-xslt-lib=${xslt-dir}/lib

    Gem files will remain installed in /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4 for inspection.
    Results logged to /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/ext/nokogiri/gem_make.out
    from /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:511:in `block in build_extensions’
    from /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:486:in `each’
    from /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:486:in `build_extensions’
    from /home/systemiv/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:159:in `install’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/source.rb:96:in `install’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/installer.rb:55:in `block in run’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/spec_set.rb:12:in `block in each’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/spec_set.rb:12:in `each’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/spec_set.rb:12:in `each’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/installer.rb:44:in `run’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/installer.rb:8:in `install’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/cli.rb:226:in `install’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/vendor/thor/task.rb:22:in `run’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/vendor/thor/invocation.rb:118:in `invoke_task’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/vendor/thor.rb:246:in `dispatch’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/lib/bundler/vendor/thor/base.rb:389:in `start’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.10/bin/bundle:13:in `’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/bin/bundle:19:in `load’
    from /home/systemiv/.rvm/gems/ruby-1.9.2-p180/bin/bundle:19:in `’
    systemiv@systemiv-900AX:~/rails/blog$ rails g rspec:install
    Could not find gem ‘rspec-rails (>= 0)’ in any of the gem sources listed in your Gemfile.

  9. admin says:

    апреля 1, 2011 at 14:28 (#)

    systemIV, у вас просто отсутствует libxml2, по указаной ссылке можно найти решение:
    sudo apt-get install libxslt-dev libxml2-dev
    gem install nokogiri
    устанавливая nokogiri не забудьте переключиться на необходимый gemset.

  10. systemIV says:

    апреля 1, 2011 at 15:06 (#)

    Ура!!! Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
    )))
    А я не понял из статьи, зачем создавать gemset? Кто то говорит с ним лучше, что то говорит что он не нужен

  11. admin says:

    апреля 1, 2011 at 16:46 (#)

    gemset — набор gem’ов. По идее для каждого проекта следует использовать одноименный gemset, однако это не обязательно, вы можете устанавливать все gem’ы в каталог по умолчанию, просто используя gemset вы можете использовать разные версии gem’ов для разных проектов, например, 2, 3, … разных версии Rails и так далее.

  12. systemIV says:

    апреля 2, 2011 at 14:33 (#)

    А когда планируешь следующий урок?)

  13. admin says:

    апреля 2, 2011 at 20:28 (#)

    может, на этих выходных успею сделать, но скорее всего на следущей неделе.

  14. devel says:

    апреля 30, 2011 at 23:46 (#)

    Большое-большое СПАСИБО за статью! 8)
    Очень помогает.

  15. Bighamster says:

    мая 1, 2011 at 16:44 (#)

    А как так получается, что модель мы создаваи «User», а табличка создалась «users»? Это тоже часть соглашения указывать сущность в единственном числе, а рельсы сами сделают множественное? А если мне нужна будет модель «Milk» — рельсы мне Milks создадут?

  16. admin says:

    мая 2, 2011 at 12:42 (#)

    Bighamster, да, это такое соглашение в Rails, экземпляр модели — одна единичная запись в базе даных, поэтому модельименуется в единственном числе, то есть User. При выполнении команды rake db:create или rake db:migrate (в случае работы с SQLite), создается соответствующая модели таблица, которая носит имя во множественном числе, так как содержит коллекцию записей соответствующих экземплярам модели, то есть users, поскольку содержит несколько User. Все это реализуется при помощи . По той же причине контроллеры также носят множественные имена User -> UsersController, то есть «контроллер, который обслуживает несколько пользователей». Вообще контроллер можно называть как угодно, особенно когда он не взаимодействует с моделями, однако я бы рекомендовал придерживаться соглашения об именовании.

  17. says:

    июня 12, 2011 at 05:23 (#)

    Спасибо за статью
    Один минус, много ситаксических ошибок и в одном месте нет подсветки кода, искать по тексту «[/ruby]«.

  18. admin says:

    июня 12, 2011 at 10:57 (#)

    Anton, нашел время и постарался поправить все ошибки. Спасибо за замечание.

  19. dima buko says:

    октября 22, 2011 at 01:37 (#)

    Спасибо за посты! Респект!

  20. Dan says:

    декабря 3, 2011 at 22:59 (#)

    Можешь написать пример приложения Rails + Cassandra?

    Есть несколько моментов, которые можно обсудить, продукт относительно молодой.

  21. admin says:

    декабря 3, 2011 at 23:27 (#)

    Dan, что конкретно интересует? Я уже много статей наобещал, но в свой стек заказ добавил. Было бы хорошо, если бы ты поделился информацией о том, что в твоем приложении должна делать cassandra. На сколько я знаю ее используют для хранения кэша, она вроде как даже быстрее memcached’а а еще для записи всяких кратковременно хранящихся данных, например в играх можно юзать и т.д. Игру я писать не буду, а вот как ее использовать в качестве хранилища для кеша могу.

  22. Dan says:

    декабря 4, 2011 at 11:32 (#)

    На сколько мне известно ее можно применить для хранения данных hi-load приложений. Яркий пример — facebook. Интересует именно это — хранение, чтение и запись данных.

  23. Dan says:

    декабря 5, 2011 at 23:28 (#)

    У меня проблема с установкой гема кассандры, мб сталкивался?

    sudo gem install cassandra —source

    Building native extensions. This could take a while…
    ERROR: Error installing cassandra:
    ERROR: Failed to build gem native extension.

    /usr/bin/ruby1.9.1 extconf.rb
    checking for strlcpy() in string.h… no
    creating Makefile

    make
    gcc -I. -I/usr/include/ruby-1.9.1/i686-linux -I/usr/include/ruby-1.9.1/ruby/backward -I/usr/include/ruby-1.9.1 -I. -D_FILE_OFFSET_BITS=64 -fPIC -g -O2 -Wall -Werror -o struct.o -c struct.c
    struct.c:28:1: ошибка: static-декларация «strlcpy» после неstatic-декларации
    /usr/include/ruby-1.9.1/ruby/missing.h:157:20: замечание: здесь была предыдущая декларация «strlcpy»
    make: *** [struct.o] Ошибка 1

    Gem files will remain installed in /usr/lib/ruby/gems/1.9.1/gems/thrift-0.7.0 for inspection.
    Results logged to /usr/lib/ruby/gems/1.9.1/gems/thrift-0.7.0/ext/gem_make.out

  24. admin says:

    декабря 8, 2011 at 11:44 (#)

    Dan, c Cassandra еще не работал, скоро попробую и отпишу что и как получилось или нет. В подобный ситуациях, как правило, стоит ставить cassandra или любую другую программу отдельно, а затем уже устанавливать gem-драйвер.

  25. Dan says:

    декабря 8, 2011 at 14:11 (#)

    Установил кассандуру по образу и подобию как здесь:

    Далее ставлю гем, но выскакивает ошибка thrift

    Ок, протестируй, мб у тебя получится.

  26. vladiboc says:

    декабря 17, 2011 at 10:57 (#)

    Пытаюсь получить xml представление списка пользователей:

    XML Parsing Error: no element found
    Location:
    Line Number 1, Column 2:
    -^

    Что не так, как исправить?

  27. admin says:

    декабря 17, 2011 at 11:33 (#)

    vladiboc, нужно код видеть и в какой версии Rails работаете?

  28. vladiboc says:

    декабря 17, 2011 at 18:59 (#)

    Туть:

    [sysadmin@localhost blog]$ cat app/controllers/users_controller.rb
    class UsersController < ApplicationController
    # GET /users
    # GET /users.json
    def index
    @users = User.all

    respond_to do |format|
    format.html # index.html.erb
    format.json { render json: @users }
    end
    end
    #…
    end
    [sysadmin@localhost blog]$ rails —version
    Rails 3.1.3

  29. admin says:

    декабря 17, 2011 at 22:24 (#)

    Достаточно было привести код одного экшена. Дело в том, что у вас есть format.json и format.html, но format.xml у вас нету.

  30. vladiboc says:

    декабря 18, 2011 at 19:37 (#)

    о, спасибо, заменил в коде *.json на *.xml
    и все открылось по адресу

    и извиняюсь за кодо-флуд. Если хотите можете его обрезать или убрать.
    Или если дадите такую возможность я сам его обрежу.

  31. admin says:

    декабря 19, 2011 at 00:13 (#)

    Обрезал лишнее. Если вам угодно разрабатывать какой-то сервис, то используйте по возможности JSON — он более удобен для парсинга, быстрее парсится и его передача расходует меньше трафика.

  32. me says:

    апреля 13, 2012 at 07:16 (#)

    Вообще говоря, чтобы возникало меньше вопросов с компиляцией гемов, сразу после установки rvm сделует выполнить команду
    [ruby]rvm requirements[ruby]
    ответ будет содержать минимальный список пакетов, необходимых гемам. Также для curl необходимо установить libcurl4-openssl-dev пакет.

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.