A Plataformatec usa
pull requests e code review
All the f*cking time.
Slide 24
Slide 24 text
E é comum aprender
coisas novas no processo
Estamos sempre aprendendo, certo?
Slide 25
Slide 25 text
No content
Slide 26
Slide 26 text
No content
Slide 27
Slide 27 text
No content
Slide 28
Slide 28 text
No content
Slide 29
Slide 29 text
No content
Slide 30
Slide 30 text
ಠ_ಠ
Eu tive que criar este talk depois disso.
Slide 31
Slide 31 text
O que vamos ver:
Active Record
Active Model
Action Mailer
!
Action View
Action Controller
Bonus Stuff
Slide 32
Slide 32 text
Active Record
Slide 33
Slide 33 text
migrations: revert
class DropAuthorsActive < ActiveRecord::Migration
def up
remove_column :authors, :active
end
!
def down
add_column :authors, :active, :boolean, default: true
end
end
Slide 34
Slide 34 text
migrations: revert
class DropAuthorsActive < ActiveRecord::Migration
def change
remove_column :authors, :active
end
end
# => ActiveRecord::IrreversibleMigration
Slide 35
Slide 35 text
migrations: revert
class DropAuthorsActive < ActiveRecord::Migration
def change
revert do
add_column :authors, :active, :boolean, default: true
end
end
end
Slide 36
Slide 36 text
migrations: change column null
class ChangeNameToNotNullOnAuthors < ActiveRecord::Migration
def up
change_column :authors, :name, :string, null: false
end
!
def down
change_column :authors, :name, :string, null: true
end
end
Slide 37
Slide 37 text
migrations: change column null
class ChangeNameToNotNullOnAuthors < ActiveRecord::Migration
def up
change_column_null :authors, :name, false
end
!
def down
change_column_null :authors, :name, true
end
end
Slide 38
Slide 38 text
migrations: change column null
class ChangeNameToNotNullOnAuthors < ActiveRecord::Migration
def change
change_column_null :authors, :name, false
end
end
Slide 39
Slide 39 text
migrations: change column default
class ChangeStatusDefaultOnArticles < ActiveRecord::Migration
def up
change_column :articles, :status, :string, default: 'draft'
end
!
def down
change_column :articles, :status, :string, default: 'published'
end
end
Slide 40
Slide 40 text
migrations: change column default
class ChangeStatusDefaultOnArticles < ActiveRecord::Migration
def up
change_column_default :articles, :status, 'draft'
end
!
def down
change_column_default :articles, :status, 'published'
end
end
Slide 41
Slide 41 text
migrations: change column default
class ChangeStatusDefaultOnArticles < ActiveRecord::Migration
def change
change_column_default :articles, :status, 'draft'
end
end
# => ActiveRecord::IrreversibleMigration
Slide 42
Slide 42 text
Relation#merge
class Author < ActiveRecord::Base
has_many :articles
!
scope :with_draft_articles, -> {
uniq.joins(:articles).
where(articles: { status: 'draft' })
}
end
Slide 43
Slide 43 text
Relation#merge
>> Author.with_draft_articles.to_sql
!
SELECT DISTINCT "authors".*
FROM "authors"
INNER JOIN "articles" ON "articles"."author_id" = "authors"."id"
WHERE "articles"."status" = 'draft'
Slide 44
Slide 44 text
Relation#merge
class Author < ActiveRecord::Base
has_many :articles
!
scope :with_draft_articles, -> {
uniq.joins(:articles).merge(Article.draft)
}
end
Slide 45
Slide 45 text
Relation#merge
>> Author.with_draft_articles.to_sql
!
SELECT DISTINCT "authors".*
FROM "authors"
INNER JOIN "articles" ON "articles"."author_id" = "authors"."id"
WHERE "articles"."status" = 'draft'
where.not
class Article < ActiveRecord::Base
scope :not_draft, -> { where 'status != ?', 'draft' }
end
!
>> Article.not_draft.to_sql
!
SELECT “articles".* FROM "articles"
WHERE (status != 'draft')
Slide 48
Slide 48 text
where.not
class Article < ActiveRecord::Base
scope :not_draft, -> { where.not status: 'draft' }
end
!
>> Article.not_draft.to_sql
!
SELECT “articles".* FROM "articles"
WHERE ("articles"."status" != 'draft')
Slide 49
Slide 49 text
order :asc/:desc
class Article < ActiveRecord::Base
scope :recent, -> { order 'created_at DESC' }
end
!
>> Article.recent
!
SELECT "articles".* FROM "articles"
ORDER BY created_at DESC
Slide 50
Slide 50 text
order :asc/:desc
class Article < ActiveRecord::Base
scope :recent, -> { order created_at: :desc }
end
!
>> Article.recent
!
SELECT "articles".* FROM "articles"
ORDER BY "articles"."created_at" DESC
Slide 51
Slide 51 text
estratégias de eager load
>> Article.includes(:author)
!
SELECT "articles".* FROM "articles"
!
SELECT "authors".* FROM "authors"
WHERE "authors"."id" IN (980190962, 298486374)
Slide 52
Slide 52 text
estratégias de eager load
>> Article.includes(:author).order('authors.name')
!
SELECT "articles"."id" AS t0_r0 ………… "authors"."updated_at" AS t1_r4
FROM "articles"
LEFT OUTER JOIN "authors" ON "authors"."id" = “articles"."author_id"
ORDER BY authors.name
Slide 53
Slide 53 text
estratégias de eager load
>> Article.eager_load(:author)
!
SELECT "articles"."id" AS t0_r0 ………… "authors"."updated_at" AS t1_r4
FROM "articles"
LEFT OUTER JOIN "authors" ON "authors"."id" = “articles”."author_id"
Slide 54
Slide 54 text
estratégias de eager load
>> Article.preload(:author)
!
SELECT "articles".* FROM “articles"
!
SELECT "authors".* FROM “authors"
WHERE "authors"."id" IN (980190962, 298486374)
Slide 55
Slide 55 text
estratégias de eager load
>> Article.preload(:author).
joins(:author).order('authors.name')
!
SELECT "articles".* FROM "articles"
INNER JOIN "authors" ON "authors"."id" = "articles"."author_id"
ORDER BY authors.name
!
SELECT "authors".* FROM "authors"
WHERE "authors"."id" IN (980190962, 298486374)
Slide 56
Slide 56 text
group counting
>> Article.group(:status).count
!
SELECT COUNT(*) AS count_all, status AS status
FROM "articles"
GROUP BY status
!
=> {"draft"=>7, “published"=>2}
Slide 57
Slide 57 text
group counting: múltiplos atributos
>> Article.group(:status, :category).count
!
SELECT COUNT(*) AS count_all, status AS status, category AS category
FROM "articles"
GROUP BY status, category
!
=> {["draft", nil]=>5, ["draft", "Talks"]=>2,
["published", "News"]=>1, ["published", "Releases"]=>1}
exists?/any? vs present?
<% if @author.articles.exists? %>
<%= render @author.articles %>
<% else %>
No article found.
<% end %>
Slide 64
Slide 64 text
exists?/any? vs present?
>> author = Author.first
!
>> author.articles.exists?
!
SELECT 1 AS one FROM "articles"
WHERE "articles"."author_id" = ? LIMIT 1 [["author_id", 1]]
!
=> true
Slide 65
Slide 65 text
exists?/any? vs present?
>> author.articles.each {}
!
SELECT "articles".* FROM "articles"
WHERE "articles"."author_id" = ? [["author_id", 1]]
Slide 66
Slide 66 text
exists?/any? vs present?
>> author = Author.first
!
>> author.articles.present?
!
SELECT "articles".* FROM "articles"
WHERE "articles"."author_id" = ? [["author_id", 1]]
!
=> true
Slide 67
Slide 67 text
exists?/any? vs present?
>> author.articles.each {}
!
# => já carregado, não dispara outra query
Slide 68
Slide 68 text
association count vs size
>> author = Author.first
!
>> author.articles.count
SELECT COUNT(*) FROM "articles"
WHERE "articles"."author_id" = ? [["author_id", 1]]
!
>> author.articles.size
SELECT COUNT(*) FROM "articles"
WHERE "articles"."author_id" = ? [["author_id", 1]]
Slide 69
Slide 69 text
association count vs size
>> author = Author.includes(:articles).first
!
>> author.articles.count
SELECT COUNT(*) FROM "articles"
WHERE "articles"."author_id" = ? [["author_id", 1]]
!
>> author.articles.size
!
# => já carregado, não dispara outra query
Slide 70
Slide 70 text
benchmark
class Article < ActiveRecord::Base
def self.expensive_operation
sleep 2
'did something very expensive'
end
end
benchmark
>> ActiveRecord::Base.benchmark 'SUPER EXPENSIVE OPERATION' do
?> Article.expensive_operation
>> end
SUPER EXPENSIVE OPERATION (2000.9ms)
=> "did something very expensive"
Slide 73
Slide 73 text
benchmark
class Article < ActiveRecord::Base
def self.expensive_operation
benchmark 'SUPER EXPENSIVE OPERATION' do
sleep 2
'did something very expensive'
end
end
end
Slide 74
Slide 74 text
Active Model
Slide 75
Slide 75 text
ActiveModel::Model
class EmailContact
attr_accessor :name, :email, :message
!
def deliver
if name.present? && email.present? && message.present?
puts 'deliver email'
end
end
end
ActiveModel::Model
class EmailContact
extend ActiveModel::Naming
extend ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::Conversion
!
attr_accessor :name, :email, :message
validates :name, :email, :message, presence: true
!
def deliver
if valid?
puts 'deliver email'
end
end
!
def persisted?
false
end
end
Slide 79
Slide 79 text
ActiveModel::Model
class EmailContact
include ActiveModel::Model
!
attr_accessor :name, :email, :message
validates :name, :email, :message, presence: true
!
def deliver
if valid?
puts 'deliver email'
end
end
end
ActiveModel::Model
>> contact = EmailContact.new
=> #
!
>> contact.valid?
=> false
!
>> contact.errors.full_messages.to_sentence
=> "Name can't be blank, Email can't be blank, and Message
can't be blank"
Slide 82
Slide 82 text
Action Mailer
Slide 83
Slide 83 text
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.notifier.new_article.subject
#
def new_article
@greeting = "Hi"
!
mail to: "[email protected]"
end
end
Slide 84
Slide 84 text
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
def new_article
@greeting = "Hi"
!
mail to: "[email protected]"
end
end
Slide 85
Slide 85 text
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
def new_article
@greeting = "Hi"
!
mail to: "[email protected]", subject: I18n.t('subjects.new_article')
end
end
Slide 86
Slide 86 text
Mailer com i18n subject
en:
subjects:
new_article: 'New article just published.'
Slide 87
Slide 87 text
Mailer com i18n subject
en:
notifier:
new_article:
subject: 'New article just published.'
Slide 88
Slide 88 text
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
def new_article
@greeting = "Hi"
!
mail to: "[email protected]"
end
end
Slide 89
Slide 89 text
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
def new_article
@greeting = "Hi"
!
mail to: "[email protected]",
subject: I18n.t('subjects.new_article', title: 'Clarity is king')
end
end
Slide 90
Slide 90 text
Mailer com i18n subject
en:
subjects:
new_article: 'New article: %{title}.'
Mailer com i18n subject
class Notifier < ActionMailer::Base
default from: "[email protected]"
!
def new_article
@greeting = "Hi"
!
mail to: "[email protected]",
subject: default_i18n_subject(title: 'Clarity is king')
end
end
Slide 93
Slide 93 text
Action View
Slide 94
Slide 94 text
content_tag_for
<%= content_tag_for :article, @articles do |article| %>
<%= article.title %>
<%= simple_format article.body %>
<% end %>
Slide 95
Slide 95 text
content_tag_for
Rails 4.2.0 is going to be more Adequate
Rails 4.2.0 is going to be made more Adequate by
Tenderlove and Gorby Puff
Rails 4.1.0 Released
Rails 4.1.0 was just released before RailsConf
Slide 96
Slide 96 text
content_tag_for
<%= content_tag_for :section, @author do |author| %>
<%= author.name %>
…
<% end %>
Slide 97
Slide 97 text
content_tag_for
Author 2
…
Slide 98
Slide 98 text
render com coleções vazias
<% if author.articles.exists? %>
<%= render author.articles %>
<% else %>
redirects com parâmetros
get '/articles/:id' => redirect('/articles/%{id}/edit')
Slide 108
Slide 108 text
tratamento de exceções
$ ls public/*.html
!
404.html 422.html 500.html
Slide 109
Slide 109 text
tratamento de exceções
# config/application.rb
!
# Use the app's own router to display error pages.
config.exceptions_app = self.routes
Slide 110
Slide 110 text
tratamento de exceções
# config/routes.rb
!
get "/404", to: "errors#not_found"
get "/422", to: "errors#unprocessable_entity"
get "/500", to: "errors#server_error"
Slide 111
Slide 111 text
tratamento de exceções
class ErrorsController < ApplicationController
layout "error"
!
def not_found
render status: :not_found
end
!
…
end
Slide 112
Slide 112 text
tratamento de exceções
app/views/
errors/
not_found.html.erb
server_error.html.erb
unprocessable_entity.html.erb
layouts/
error.html.erb
Slide 113
Slide 113 text
Console
Slide 114
Slide 114 text
app
>> app.author_path(Author.first)
=> “/authors/298486374"
!
>> app.get _
Started GET "/authors/298486374" for 127.0.0.1 at 2014-04-22 19:31:04 -0500
Processing by AuthorsController#show as HTML
…
Rendered authors/show.html.erb within layouts/application (1.1ms)
Completed 200 OK in 9ms (Views: 7.7ms | ActiveRecord: 0.2ms)
=> 200
!
>> app.class
=> ActionDispatch::Integration::Session
console helpers da aplicação
# lib/console_helpers.rb
module ConsoleHelpers
def a(id)
Article.find(id)
end
end
Slide 117
Slide 117 text
console helpers da aplicação
# config/application.rb
module RubyConf2014
class Application < Rails::Application
console do
require 'console_helpers'
Rails::ConsoleMethods.send :include, ConsoleHelpers
end
end
end
Slide 118
Slide 118 text
console helpers da aplicação
>> a(113629430)
Article Load (0.1ms) SELECT "articles".* FROM "articles" WHERE
"articles"."id" = ? LIMIT 1 [["id", 113629430]]
!
=> #
rails update
$ rake rails:update
identical config/boot.rb
exist config
conflict config/routes.rb
./railsconf2014-404/config/routes.rb? (enter "h" for help) [Ynaqdh] h
Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
d - diff, show the differences between the old and the new
h - help, show this help
./railsconf2014-404/config/routes.rb? (enter "h" for help) [Ynaqdh] a
force config/routes.rb
…
exist config/initializers
identical config/initializers/backtrace_silencers.rb
create config/initializers/cookies_serializer.rb
identical config/initializers/filter_parameter_logging.rb
…
Slide 124
Slide 124 text
rails update
$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
!
modified: Gemfile
modified: Gemfile.lock
modified: config/environment.rb
modified: config/environments/development.rb
modified: config/environments/production.rb
modified: config/environments/test.rb
modified: config/initializers/mime_types.rb
modified: config/initializers/session_store.rb
modified: config/routes.rb
!
Untracked files:
(use "git add ..." to include in what will be committed)
!
config/initializers/cookies_serializer.rb
config/secrets.yml
Slide 125
Slide 125 text
Cada parte do Rails tem
seus truques escondidos
Slide 126
Slide 126 text
Eles são às vezes
difíceis de achar
Slide 127
Slide 127 text
Mas eles podem te deixar
muito mais produtivo
Slide 128
Slide 128 text
Então toda vez que você
começar algo novo
Slide 129
Slide 129 text
Antes mesmo de considerar
usar o pattern X, Y ou Z
Slide 130
Slide 130 text
Pergunte-se: Como o Rails
pode me ajudar?
Slide 131
Slide 131 text
Pesquise
API
Guidelines
Google
Stack Overflow
…
Slide 132
Slide 132 text
Pergunte
Stack Overflow
Rails Talk mailing list
IRC
…