highlighted with http://tinker.kotaweaver.com/blog/?p=152
environment:
ruby > 1.9.3
RubyGems
установка
$ gem install rails
$ rails --version
ещё раз о структуре:
Создание приложения Blog
$ rails new blog
структура (самые важные части) проекта:
| app/ | Контроллеры, модели, вьюхи, хелперы, рассыльщики и ресурсы вашего приложения. |
| script/ | Скрипты для старта/деплоя/ещё чего нибудь (В rails 4 переименован в bin) |
| config/ | Конфигурации маршрутов, БД и т.д. |
| db/ | Содержит текущую схему вашей базы данных, а также миграции базы данных. |
| Gemfile, Gemfile.lock | Эти файлы позволяют указать, какие зависимости от гемов нужны для вашего приложения на Rails. Эти файлы используются гемом Bundler. |
| lib/ | Внешние модули для вашего приложения. |
| log/ | Файлы логов приложения. |
| public/ | Единственная папка, которая доступна извне как есть. Содержит статичные файлы и скомпилированные ресурсы. |
| Rakefile | Содержит набор команд, которые могут быть запущены в командной строке. Вместо изменения Rakefile, вы можете добавить свои собственные задачи, добавив файлы в lib/tasks. |
| test/ | Юнит-тесты, фикстуры и прочий аппарат тестирования. |
| tmp/ | Временные файлы (такие как файлы кэша, pid и сессии) |
| vendor/ | Место для кода внешних разработчиков. В типичном приложении на Rails, включает внешние гемы. |
Запуск локального веб-сервера
$ bin/rails server
Это запустит WEBrick, веб-сервер, распространяющийся с Ruby по умолчанию, посмотреть можно по http://localhost:3000
создание контроллера и экшена
$ bin/rails generate controller welcome index
Rails создаст несколько файлов и маршрут.
create app/controllers/welcome_controller.rb
route get 'welcome/index'
invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
create test/helpers/welcome_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/welcome.js.coffee
invoke scss
create app/assets/stylesheets/welcome.css.scss
Active Record это фреймворк ORM
Active Record предоставляет несколько механизмов, наиболее важными из которых является способности для:
- Представления моделей и их данных.
- Представления связей между этими моделями.
- Представления иерархий наследования с помощью связанных моделей.
- Валидации моделей до того, как они будут сохранены в базу данных.
- Выполнения операций с базой данных в объектно-ориентированном стиле.
Соглашения по именованию:
- Таблица базы данных - прописывается во множественной форме, разделение слов через знак подчеркивания (например, book_clubs).
- Класс модели - прописывается в единственной форме с первой прописной буквой в каждом слове (например, BookClub).
Соглашения по схемам:
- Внешние ключи - singularized_table_name_id (например, item_id, order_id)
- Первичные ключи - id как первичный ключ таблицы.
Создание моделей Active Record:
class Product < ActiveRecord::Base
endсоздание миграции:
$ bin/rails generate migration AddPartNumberToProducts part_number:string:index price:decimal
Это создат следующую миграцию:
class AddPartNumberToProducts < ActiveRecord::Migration
def change
add_column :products, :part_number, :string
add_column :products, :price, :decimal
add_index :products, :part_number
end
endточно так же можно и удалять поля
$ bin/rails generate migration RemovePartNumberFromProducts part_number:string
или создавать таблицы
$ bin/rails generate migration CreateProducts name:string part_number:string
можно сразу создавать и модели и миграции к ним:
$ bin/rails generate model Product name:string description:text #создаст и модель и миграцию
список доступных в миграции комманд (некоторые из этих команд доступны только в Rails 4):
create_table
create_join_table
drop_table
drop_join_table
change_table (удаление полей, переименование, создание новых, индексы)
rename_table
add_column
change_column
change_column_null
change_column_default
rename_column
add_index
rename_index
remove_index
add_foreign_key
remove_foreign_key
add_reference
remove_reference
add_timestamps
remove_timestamps
как rails определяет что делать при накате/откате миграций:
- change почти всегда сам знает как накатить и откатить (а то что не знает - в Rails 4 можно описать в reversible.up/reversible.down)
- можно написать up/down методы
запуск миграций:
$ bin/rake db:migrate [VERSION=20080906120000] # на последнюю [определённую] миграцию
$ bin/rake db:rollback [STEP=3] # откатиться назад одну [несколько] последних миграций,
# можно сделать предыдущей командой
генерация базы:
$ rake db:setup
Валидации используются, чтобы быть уверенными, что только правильные данные сохраняются в вашу базу данных
class Person < ActiveRecord::Base
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
p = Person.create(name: nil).valid? # => false
p.errors.messages # => {name:["can't be blank"]} #эти ошибки потом можно отображать во вьюхе
p.errors[:name].any? # => trueСледующие методы вызывают валидацию, и сохраняют объект в базу данных только если он валиден:
- create
- create!
- save
- save!
- update
- update!
Версии с восклицательным знаком вызывают исключение, если запись недействительна. Невосклицательные версии save и update просто возвращают false, create возвращает объект.
основные валидационные хэлперы:
| validates_associated | валидирует связанные объекты |
| confirmation | введите подтверждение (для паролей и т.д) |
| inclusion/exclusion | проверяют что значение входит/не входит в список |
| format | проверяет на соответствие регэкспу |
| numericality | ожидает только числовые значения |
| length | проверяет на длину |
| precence/absence(последнее только в Rails 4) | проверяют наличие/отсутствие значения |
| uniquiness | проверяет на уникальность значения в БД |
| validetes_with | валидирует с помощью кастомного класса-валидатора (да, можно написать свой класс-валидатор) |
! Можно переопределить условия, при которых будет выполняться валидация с помощью :on, :if/:unless
class User < ActiveRecord::Base
after_create do |user|
puts "You have a user with name #{user.name} and age #{user.age}!"
end
endCписок всех доступных колбэков Active Record:
- before_validation
- after_validation
- before_save
- around_save
- after_save
- before_create
- around_create
- after_create
- before_update
- around_update
- after_update
- before_destroy
- around_destroy
- after_destroy
- after_commit/after_rollback
!можно писать классы колбэков, чтобы потом юзать в разных моделях
!колбэки бывают условными (:if/:unless)
определяем отношения:
class Customer < ActiveRecord::Base
has_many :orders, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :customer
endи потом можно очень удобно этим пользоваться:
customer.orders # вернёт список заказов определённого кастомера
order = customer.create_order(order_name = 'blah blah') # создаст новый заказ для данного кастомератипы связей:
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
в данном примере модель изображения принадлежит или модели работника, или модели продукта
куча методов для полноценного общения с БД через ОРМ:
- find
- take
- first
- last
- find_by
- where
- order
- select
- limit
- group
- joins
- includes
и т.д.
customer = Customer.find(10)
customer.orders # получаем заказы из базы данных
.....
customer.orders.empty? # используем кэшированную копию заказов
customer.orders(true).empty? # отказываемся от кэшированной копии заказов
# и снова обращаемся к базе данных2 запроса вместо 11:
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
endclass Article < ActiveRecord::Base
default_scope -> { where(deleted_at: nil) }
scope :published, -> { where(published: true) }
scope :published_and_commented, -> { published.where("comments_count > 0") }
end
Article.all # => articles
Article.published # => [published articles where deleted_at is nil]Convention over Configuration в действии:
контроллер:
class BooksController < ApplicationController
endроутинг:
resources :booksв таком случае Rails автоматически отрендерит app/views/books/index.html.erb при переходе на адрес /books
широкий функционал:
можно явно переопределить вьюху, которую надо отрендерить с помощью render 'view_name' можно сделать редирект с помощью redirect_to
существует куча методов для генерации ответа от сервера в любом виде (json, xml, js, raw, HEAD с пустым телом и т.д.), с определением кода ответа, location, content-type
отрендеренные вьюхи вставляются в макеты :
/app/views/layouts/books.html.erb :
<html>
<head>
<%= yield :head %>
</head>
<body>
<%= yield %>
</body>
</html>app/views/books/index.html.erb :
<% content_for :head do %>
<title>A simple page</title>
<% end %>
<p>Hello, Rails!</p>результат :
<html>
<head>
<title>A simple page</title>
</head>
<body>
<p>Hello, Rails!</p>
</body>
</html>также во вьюхи можно вставлять партиалы, чтобы ещё лучше всё структурировать:
<%= render "shared/ad_banner" %>
<h1>Products</h1>
<p>Here are a few of our fine products:</p>
...
<%= render "shared/footer" %>в партиалы можно передаватьлокальные переменные
также очень удобно использовать партиалы для рендеринга коллекций:
index.html.erb
<h1>Products</h1>
<%= render partial: "product", collection: @products %>_product.html.erb
<p>Product Name: <%= product.name %></p>хэлперы ресурсных тэгов:
- auto_discovery_link_tag - встраивает RSS или ATOM
- javascript_include_tag - встраивает js из app/assets/javascripts
- stylesheet_link_tag - встривает css из app/assets/stylesheets
- image_tag - встраивает картинки из public/img
- video_tag - встраивает видео из public/videos
- audio_tag - встраивает видео из public/audios
хэлперы форм
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= check_box_tag(:pet_dog) %>
<%= radio_button_tag(:age, "child") %>
<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_field(:user, :meeting_time) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>
<%= submit_tag("Search") %>
<% end %>привязка форм к объектам ActiveRecord
легка и непринуждённа с использованием form_for
articles_controller.rb:
def new
@article = Article.new
endarticles/new.html.erb:
<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body, size: "60x12" %>
<%= f.file_field :picture %>
<%= f.submit "Create" %>
<% end %>формы и связанные объекты
если у вас есть связанные объекты
(например пользователь имеет несколько адресов) - можно создавать вложенные формы:
<%= form_for @person do |person_form| %>
<%= person_form.text_field :name %>
<% @person.addresses.each do |address| %>
<%= person_form.fields_for address, index: address.id do |address_form|%>
<%= address_form.text_field :city %>
<% end %>
<% end %>
<% end %>защита от CSRF
для форм, созданных с помощью хэлперов, добавляется автоматически.
для созданных вручную форм есть form_authenticity_token
<input type="hidden"
value="67250ab105eb5ad10851c00a5621854a23af5489"
name="authenticity_token"/>class ClientsController < ApplicationController
def new # пустой экшен просто отрендерит new.html.erb
end
endполучает, не делая разницы между GET и POST
class ClientsController < ApplicationController
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
render "new"
end
end
endRails также отлично конвертит json -> params
params также содержит controller_name, action_name, и другие параметры из роутинга, например:
get '/clients/:status' => 'clients#index', foo: 'bar' # /clients/active will hit clients#index with params = {status:'active', foo:'bar'}Есть сессия для каждого пользователя, в которой можно хранить небольшие порции данных, которые будут сохранены между запросами. Сессия доступна только в контроллере и во вьюхе.
- ActionDispatch::Session::CookieStore - Хранит все на клиенте.
- ActionDispatch::Session::CacheStore - Хранит данные в кэше Rails.
- ActionDispatch::Session::ActiveRecordStore - Хранит данные в базе данных с использованием Active Record. (требует гем activerecord-session_store).
- ActionDispatch::Session::MemCacheStore - Хранит данные в кластере memcached (эта реализация - наследие старых версий, вместо нее рассмотрите использование CacheStore).
доступ к сессии:
session[:some_key] = some_valueFlash Cпециальная часть сессии, которая очищается с каждым запросом.
доступ:
flash[:notice] = "You have successfully logged out."либо как часть редиректа:
redirect_to root_url, notice: "You have successfully logged out."
redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }отображение flash во вьюхе:
<% if flash[:just_signed_up] %>
<p class="welcome">Welcome to our site!</p>
<% end %>можно продлять жизнь flash с помощью flash.keep / flash.keep(:some_key)
cookies[:commenter_name] = 'Boris'есть возможность создавать подписанные и зашифрованные куки
request:
| host | Имя хоста, используемого для этого запроса. |
| domain(n=2) | Первые n сегментов имени хоста, начиная справа (домен верхнего уровня). |
| format | Тип содержимого, запрошенного с клиента. |
| method | Метод HTTP, использованного для запроса. |
| get?, post?, patch?, put?, delete?, head? | true, если метод HTTP - это GET/POST/PATCH/PUT/DELETE/HEAD. |
| headers | Хэш, содержащий заголовки, связанные с запросом. |
| port | Номер порта (число), использованного для запроса. |
| protocol | Строка, содержащая использованный протокол плюс "://", например "http://". |
| query_string | Часть URL со строкой запроса, т.е. все после "?". |
| remote_ip | Адрес IP клиента. |
| url | Полный URL, использованный для запроса. |
| path_parameters | параметры, распозннные роутингом |
| query_parameters | параметры, пришедшие в строке запроса |
| request_parameters | параметры, посланные в теле запроса |
response:
| body | Cтрока данных, которая будет возвращена клиенту. Часто это HTML. |
| status | Код статуса HTTP для отклика, например 200 для успешного отклика или 404 для ненайденного файла. |
| location | URL, по которому клиент будет перенаправлен, если указан. |
| content_type | Тип содержимого отклика. |
| charset | Кодировка, используемая для отклика. По умолчанию это "utf-8". |
| headers | Заголовки, используемые для отклика. |
Фильтры - это методы, которые запускаются до, после или "вокруг" экшна контроллера. Фильтры наследуются.
- before_action
- around_action (позволяет например обернуть экшен в транзакцию, в теле фильтра для вызова экшена используется yield)
- after_action
типичный пример:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # сообщает о переходе на страницу авторизации
end
end
endможно задавать для каких именно экшенов будет работать фильтр с помощью :only:
around_action :wrap_in_transaction, only: :showможно отменять использование фильтров в унаследованных контроллерах (для определённых экшенов или вцелом):
class LoginsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
endфильтры можно описывать как в виде функций, так и в виде классов
встроенные механизмы:
basic auth
class AdminsController < ApplicationController
http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
enddigest auth
class AdminsController < ApplicationController
USERS = { "lifo" => "world" }
before_action :authenticate
private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end2 метода: send_file и send_data. send_file посылает уже готовый файл с диска, send_data - сгенерированный
def download_pdf
client = Client.find(params[:id])
send_data generate_pdf(client),
filename: "#{client.name}.pdf",
type: "application/pdf"по дэфолту, 404 и 500 ошибки приводят к тому, что отдаются public/404.html и public/500.html соответственно но это можно переопределить в routes.rb : (Rails 4)
match '/404', via: :all, to: 'errors#not_found'
match '/422', via: :all, to: 'errors#unprocessable_entity'
match '/500', via: :all, to: 'errors#server_error'ошибки также можно перехватывать при помощи resсue_from:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
endclass DinnerController
force_ssl only: :cheeseburger
# или
force_ssl except: :cheeseburger
endописывается в config/routes.rb:
пример роута:
get '/patients/:id', to: 'patients#show', as: 'patient'просмотр всех роутов:
команда rake routes выдаёт примерно такую таблицу:
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
генерация правильных урлов из кода:
get '/patients/:id', to: 'patients#show', as: 'patient'далее можно сделать вот так:
PatientsController.rb
def some_action
@patient = Patient.find(17)
endapp/views/patients/some_action.html.erb
<%= link_to 'Patient Record', patient_path(@patient) %> # преобразуется в /patients/17resources :photosданная строка создаёт следующие маршруты:
| Метод HTTP | Путь | Контроллер#Экшн | Использование |
|---|---|---|---|
| GET | /photos | photos#index | отображает список всех фото |
| GET | /photos/new | photos#new | возвращает форму HTML для создания нового фото |
| POST | /photos | photos#create | создает новое фото |
| GET | /photos/:id | photos#show | отображает определенное фото |
| GET | /photos/:id/edit | photos#edit | возвращает форму HTML для редактирования фото |
| PATCH/PUT | /photos/:id | photos#update | обновляет определенное фото |
| DELETE | /photos/:id | photos#destroy | удаляет определенное фото |
Создание ресурсного маршрута также сделает доступными множество хелперов в контроллере . В случае с resources :photos:
- photos_path возвращает /photos
- new_photo_path возвращает /photos/new
- edit_photo_path(:id) возвращает /photos/:id/edit (например, edit_photo_path(10) возвращает /photos/10/edit)
- photo_path(:id) возвращает /photos/:id (например, photo_path(10) возвращает /photos/10)
иногда требуется определить ресурс, доступный только в 1 экземпляре, такой роутинг пишется так:
resource :geocoderданный код создаст 6 ресурсных маршрутов и 3 хелпера (по аналогии с resouces)
Пространства имён
можно поместить некоторые контроллеры в отдельную папку, например /app/controllers/admin
в роутинге после этого можно сгруппировать их в пространство имён:
namespace :admin do
resources :articles, :comments
endэто создаст соответствующие урлы, включающие нэймспэйс (GET /admin/articles например)
также можно задать кастомный базовый урл для ресурса:
resources :posts, path: '/admin/articles'или переопределить контроллер:
resources :photos, :controller => "images"или название именованных хелперов
resources :photos, :as => "images"или название некоторых урлов, ведуших к экшенам (название самих экшенов не меняется)
resources :photos, :path_names => { :new => 'make', :edit => 'change' }или вообще явно задать, какие маршруты ресурса нужно создавать, а какие - нет:
resources :photos, :only => [:index, :show]
resources :photos, :except => :destroyВложенные ресурсы
предположим, имеются две таких модели:
class Magazine < ActiveRecord::Base
has_many :ads
end
class Ad < ActiveRecord::Base
belongs_to :magazine
endтогда можно сделать вложенный роутинг:
resources :magazines do
resources :ads
endчто создаст урлы вот такого типа:
GET /magazines/:magazine_id/ads ads#index
можно тем не менее делать часть экшенов вложенного ресурса вложенными, а часть - невложенными :
resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
# в данном примере экшены коллекции - вложенные, а экшены отдельных элементов , требующие id - нетКонцерты маршрутов (Rails 4)
Позволяют объявлять обычные маршруты, которые затем могут быть повторно использованы внутри других ресурсов и маршрутов.
Чтобы определить концерн:
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
endи потом используются концерны вот так:
resources :messages, concerns: :commentable
resources :articles, concerns: [:commentable, :image_attachable]добавление маршрутов
добавление маршрутов к элементам:
resources :photos do
get 'preview', on: :member
endдобавление маршрутов к коллекции:
resources :photos do
get 'search', on: :collection
endтипичный урл
get ':controller(/:action(/:id))'вообще, параметров можно передавать сколько угодно:
get ':controller/:action/:id/with_user/:user_id'последяя строчка обработает урл /photos/show/1/with_user/2 и выдаст params:{ controller: 'photos', action: 'show', id: '1', user_id: '2' }
можно задавать дэфолтные значения:
get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }именование маршрутов
get 'exit', to: 'sessions#destroy', as: :logout # создаст хелперы logout_path и logout_urlперенаправление маршрутов
get '/stories', to: redirect('/articles')Active Support – это компонент Ruby on Rails, отвечающий за предоставление расширений для языка Ruby, утилит и множества других вещей. Он предлагает более ценные функции на уровне языка.
например, у хэшей появляется метод to_xml:
require 'active_support/core_ext'
{"foo" => 1, "bar" => 2}.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </hash>Гем I18n , поставляемый с RoR, представляет простой и расширяемый фреймворк для перевода вашего приложения на отдельный другой язык, иной чем английский, или для предоставления поддержки многоязычности в вашем приложении.
в самом простом рассмотрении работает он так: сначала нужно добавить переводы фраз:
# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!
# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flashзатем добавить код для определения локали из запроса, например вот такой (самый простой метод, хоть и не самый красивый):
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
endзатем можно использовать гем так:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = t(:hello_flash)
end
end
# app/views/home/index.html.erb
<h1><%=t :hello_world %></h1>
<p><%= flash[:notice] %></p>Action Mailer позволяет отправлять электронные письма из вашего приложения посредством создание рассыльщиков, наследуемых от ActionMailer::Base, и находящихся в app/mailers. Эти рассыльщики имеют связанные вьюхи, которые находятся в app/views.
Работает очень просто:
# app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default :from => "notifications@example.com"
def welcome_email(user)
@user = user
@url = "http://example.com/login"
mail(:to => user.email, :subject => "Welcome to My Awesome Site")
end
end<!--app/views/user_mailer/welcome_email.html.erb-->
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
</head>
<body>
<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
You have successfully signed up to example.com,
your username is: <%= @user.login %>.<br/>
</p>
<p>
To login to the site, just follow this link: <%= @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>(также можно добавить app/views/user_mailer/welcome_email.text.erb для поддержки текстовых версий)
ну и дальше письма отправляются очень просто:
UserMailer.welcome_email(@user).deliver- debug @post # вернёт <pre> + @post.to_yaml
- @post.to_yaml
- inspect # печатает строкой
logger
Rails.logger = Logger.new(STDOUT)уровни лога:
:debug, :info, :warn, :error, :fatal, :unknownзадание уровня:
config.log_level = :warn # В любом инициализаторе среды, или
ActiveRecord::Base.logger.level = 0 # в любое времяиспользование:
logger.info "Processing the request..."gem debugger
$ gem install debugger
$ rails server --debugger
Available commands:
backtrace delete enable help next quit show trace
break disable eval info p reload source undisplay
catch display exit irb pp restart step up
condition down finish list ps save thread var
continue edit frame method putl set tmate where
gem byebug (для Rails 4)
rails new создаёт новый проект rails
$ rails new commandsapp
rails server запускает сервер
$ rails server -e production -p 4000
rails generate генерирует что угодно
$ rails generate controller Greetings hello
$ rails generate model HighScore game:string score:integer
$ rails generate scaffold HighScore game:string score:integer
rails console запускает рельсовую консоль
$ rails console staging # запустит консоль в среде staging
rails dbconsole коннектит к базе данных исходя из настроек rails приложения
rails runner запускает без интерактивности
$ rails runner -e staging "Model.long_running_method"
rails destroy противоположность rails generate
rake
Rake означает Ruby Make, отдельная утилита Ruby, заменяющая утилиту Unix "make", и использующая файлы "Rakefile" и .rake для построения списка задач
$ rake --tasks
rake about # List versions of all Rails frameworks and the environment
rake assets:clean[keep] # Remove old compiled assets
rake assets:clobber # Remove compiled assets
rake assets:environment # Load asset compile environment
rake assets:precompile # Compile all the assets named in config.assets.precompile
rake cache_digests:dependencies # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake cache_digests:nested_dependencies # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config)
rake db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config)
rake db:fixtures:load # Load fixtures into the current environment's database
rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status # Display status of migrations
rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:cache:clear # Clear a db/schema_cache.dump file
rake db:schema:cache:dump # Create a db/schema_cache.dump file
rake db:schema:dump # Create a db/schema.rb file that is portable against any DB supported by AR
rake db:schema:load # Load a schema.rb file into the database
rake db:seed # Load the seed data from db/seeds.rb
rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)
rake db:structure:dump # Dump the database structure to db/structure.sql
rake db:version # Retrieves the current schema version number
rake doc:app # Generate docs for the app -- also available doc:rails, doc:guides (options: TEMPLATE=/rdoc-template.rb, TITLE="Custom Title")
rake log:clear # Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
rake middleware # Prints out your Rack middleware stack
rake notes # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
rake notes:custom # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
rake rails:template # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake rails:update # Update configs and some other initially generated files (or use just update:configs or update:bin)
rake routes # Print out all defined routes in match order, with names
rake secret # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)
rake stats # Report code statistics (KLOCs, etc) from the application
rake test # Runs test:units, test:functionals, test:generators, test:integration together
rake test:all # Run tests quickly by merging all types and not resetting db
rake test:all:db # Run tests quickly, but also reset db
rake time:zones:all # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6
rake tmp:clear # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)
rake tmp:create # Creates tmp directories for sessions, cache, sockets, and pids
свои задачи:
desc "I am short, but comprehensive description for my cool task" task task_name: [:prerequisite_task, :another_task_we_depend_on] do
end
вызов задач:
rake task_name
по умолчанию включено только для production
включить:
config.action_controller.perform_caching = trueдоступны 3 механизма:
- кэширование страницы
- кэширование экшена
- кэширование фрагмента
кэширование страницы (в rails 4 перемещено из классов ядра в gem 'actionpack-page_caching')
class ProductsController < ActionController
caches_page :index
def index
@products = Products.all
end
endкэширование экшена (в rails 4 перемещено из классов ядра в gem 'actionpack-action_caching')
class ProductsController < ActionController
before_filter :authenticate
caches_action :index
def index
@products = Product.all
end
def create
expire_action :action => :index
end
endкэширование фрагмента
Если хотите показать все заказы, размещенные на веб сайте, в реальном времени и не хотите кэшировать эту часть страницы, но хотите кэшировать часть страницы, отображающей все доступные продукты, можете использовать следующий кусок кода:
<% Order.find_recent.each do |o| %>
<%= o.buyer.name %> bought <%= o.product.name %>
<% end %>
<% cache do %>
All available products:
<% Product.all.each do |p| %>
<%= link_to p.name, product_url(p) %>
<% end %>
<% end %>если хотите кэшировать несколько фрагментов на экшн, следует предоставить action_suffix в вызове cache:
<% cache(:action => 'recent', :action_suffix => 'all_products') do %>
All available products:инвалидация:
expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products')глобальные фрагменты (не привязаны к экшену):
<% cache('all_available_products') do %>
All available products:
<% end %>Уборщики (sweepers)
представляют собой обсерверы, автоматически инвалидирующие кэш при изменении состояния объектов (в rails 4 перемещено из классов ядра в gem 'rails-observers')
class ProductSweeper < ActionController::Caching::Sweeper
observe Product # This sweeper is going to keep an eye on the Product model
# Если наш уборщик обнаружит, что Product был создан, вызываем это
def after_create(product)
expire_cache_for(product)
end
# Если наш уборщик обнаружит, что Product был обновлен, вызываем это
def after_update(product)
expire_cache_for(product)
end
# Если наш уборщик обнаружит, что Product был удален, вызываем это
def after_destroy(product)
expire_cache_for(product)
end
private
def expire_cache_for(product)
# Прекращает страницу list теперь, когда мы добавили новый продукт
expire_page(:controller => 'products', :action => 'index')
# Прекращает фрагмент
expire_fragment('all_available_products')
end
endхранилища:
- ActiveSupport::Cache::MemoryStore
- ActiveSupport::Cache::FileStore
- ActiveSupport::Cache::MemCacheStore
- ActiveSupport::Cache::NullStore
conditional GET
class ProductsController < ApplicationController
# Это автоматически отошлет :not_modified, если запрос свежий,
# и отрендерит дефолтный шаблон (product.*), если он устарел.
def show
@product = Product.find(params[:id])
fresh_when :last_modified => @product.published_at.utc, :etag => @product
end
endAsset Pipeline (файлопровод) представляет фреймворк для соединения и минимизации JavaScript и CSS. Также добавляет возможность писать эти ресурсы на других языках и препроцессорах, таких как CoffeeScript, Sass и ERB.
в rails 4 был извлечён из ядра в гем sprockets-rails
Rails 4 автоматически добавляет гемы sass-rails, coffee-rails и uglifier в Gemfile
основные фишки:
Cоединение ресурсов. Sprockets соединяет все JavaScript файлы в один главный файл .js и все CSS файлы в один главный файл .css. Можно настроить эту стратегию, сгруппировав файлы любым способом. В production, Rails вставляет метку MD5 в каждое имя файла, таким образом файл кэшируется браузером. Кэш можно сделать недействительным, изменив эту метку, что происходит автоматически каждый раз, когда изменяется содержимое файла.
Минимизация или сжатие ресурсов. Для CSS выполняется путем удаления пробелов и комментов. Для JS могут быть применены более сложные процессы.
Позволяет писать эти ресурсы на языке высшего уровня с дальнейшей прекомпиляцией до фактического ресурса. Поддерживаемые языки по умолчанию: Sass для CSS, CoffeeScript для JS и ERB для обоих.
включить:
#config/application.rb
require "sprockets/railtie"установить методы компрессии ресурсов:
config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifyиспользование:
-все файлы хранятся в app/assets | lib/assets | vendor/assets
-rake assets:precompile компилит их и помещает в папку, указанную в config.assets.prefix
(по умолчанию это public/assets, но можно лить куда угодно)
-public/assets отдаётся вэб-сервером как статика, не обращаясь к аппликейшн-серверу
ссылаться на lib/assets/javascripts/subdir/moovinator.js можно вот так:
//= require subdir/moovinator
Asset Pipeline автоматически подставляет нужные имена файлов в следующие хэлперы:
stylesheet_link_tag, javascript_include_tag, image_tag
кроме этого, файлопровод вычисляет ERB, что позволяет писать вот такие строки в CSS:
.class { background-image: url(<%= asset_path 'image.png' %>) }
или в JS/Coffee :
$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });
для SASS используется гем sass-rails, предоставляющий хэлперы -url и -path, например image-url("rails.png") становится url(/assets/rails.png)
пример продакшена: данный код
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>создаст что-то наподобие этого:
<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" />Файлы манифеста и директивы
Sprockets использует файлы манифеста для определения, какие ресурсы включать и отдавать. Эти файлы манифеста содержат директивы - инструкции, говорящие Sprockets, какие файлы требуются для создания отдельного файла CSS или JavaScript. С помощью этих директив Sprockets загружает указанные файлы, при необходимости их обрабатывает, соединяет в отдельный файл и затем сжимает их (если Rails.application.config.assets.compress равно true). При отдаче одного файла, а не нескольких, время загрузки страницы значительно уменьшается, поскольку браузер делает меньше запросов. Компрессия также уменьшает размер файла, что позволяет браузеру быстрее его скачать.
пример manifest.json:
{
"files": {
"application-723d1be6cc741a3aabb1cec24276d681.js": {
"logical_path": "application.js",
"mtime": "2013-07-26T22:55:03-07:00",
"size": 302506,
"digest": "723d1be6cc741a3aabb1cec24276d681"
},
"application-12b3c7dd74d2e9df37e7cbb1efa76a6d.css": {
"logical_path": "application.css",
"mtime": "2013-07-26T22:54:54-07:00",
"size": 1560,
"digest": "12b3c7dd74d2e9df37e7cbb1efa76a6d"
},
"favicon-a9c641bf2b81f0476e876f7c5e375969.ico": {
"logical_path": "favicon.ico",
"mtime": "2013-07-26T23:00:10-07:00",
"size": 1406,
"digest": "a9c641bf2b81f0476e876f7c5e375969"
},
"my_image-231a680f23887d9dd70710ea5efd3c62.png": {
"logical_path": "my_image.png",
"mtime": "2013-07-26T23:00:27-07:00",
"size": 6646,
"digest": "231a680f23887d9dd70710ea5efd3c62"
}
},
"assets": {
"application.js": "application-723d1be6cc741a3aabb1cec24276d681.js",
"application.css": "application-1c5752789588ac18d7e1a50b1f0fd4c2.css",
"favicon.ico": "favicona9c641bf2b81f0476e876f7c5e375969.ico",
"my_image.png": "my_image-231a680f23887d9dd70710ea5efd3c62.png"
}
}чтобы ощутить преимущества диджест меток файлов, необходимо настроить вэб-сервер на вечное (или хотябы очень долгое) кэширование. например, для nginx :
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
gzip_static on; # to serve pre-gzipped version
add_header ETag "";
break;
}Engine можно рассматривать как миниатюрное приложение, предоставляющее функционал содержащим их приложениям. Приложение Rails фактически всего лишь "прокачанный" engine с классом Rails::Application, унаследовавшим большую часть своего поведения от Rails::Engine.
