Eu já participei em
vários projetos com
contextos diferentes
Slide 5
Slide 5 text
Eu já vi código
Slide 6
Slide 6 text
Eu já vi código complexo
Slide 7
Slide 7 text
Eu já vi código duplicado
Slide 8
Slide 8 text
Eu já vi código do Rails
reimplementado em apps
Slide 9
Slide 9 text
Com que frequência você vê eles?
Todo o tempo. Eles estão em toda parte.
Slide 10
Slide 10 text
Você se acostuma com o
código após algum tempo
Slide 11
Slide 11 text
E isso é um
grande problema
As pessoas têm medo de lidar com esse tipo de código.
Slide 12
Slide 12 text
Rails tricks / features
Slide 13
Slide 13 text
Que podem te ajudar a
refatorar código existente
ou melhorar sua vida como desenvolvedor um pouquinho
Slide 14
Slide 14 text
No content
Slide 15
Slide 15 text
No content
Slide 16
Slide 16 text
Consultoria em
Desenvolvimento Web
Com foco principal em Ruby e Rails
São Paulo - Brazil
Slide 17
Slide 17 text
❤️
Slide 18
Slide 18 text
plataformatec/devise
Slide 19
Slide 19 text
plataformatec/simple_form
Slide 20
Slide 20 text
elixir-lang/elixir
Slide 21
Slide 21 text
Rails Core Team
! Carlos Antonio
Slide 22
Slide 22 text
Rails Core Team
José Valim Carlos Antonio Rafael França
Slide 23
Slide 23 text
Nós usamos pull requests !
e code review
All the f*cking time.
Slide 24
Slide 24 text
Nós fizemos code review
de diferentes projetos
Sempre que o tempo nos permite.
Slide 25
Slide 25 text
E é comum aprender
coisas novas no processo
Estamos sempre aprendendo, certo?
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
No content
Slide 31
Slide 31 text
ಠ_ಠ
Eu tive que criar este talk depois disso.
Slide 32
Slide 32 text
Rails tricks / features
Não usadas por muitos
Não muito conhecidas e / ou documentadas
Slide 33
Slide 33 text
O que vamos ver:
Active Record
Active Model
Action Mailer
Action View
Action Controller
Bonus Stuff
Slide 34
Slide 34 text
Active Record
Slide 35
Slide 35 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 36
Slide 36 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 37
Slide 37 text
migrations: change column null
class ChangeNameToNotNullOnAuthors < ActiveRecord::Migration
def change
change_column :authors, :name, :string, null: false
end
end
# => ActiveRecord::IrreversibleMigration
!
class ChangeNameToNotNullOnAuthors < ActiveRecord::Migration
def change
change_column_null :authors, :name, false
end
end
Slide 38
Slide 38 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 39
Slide 39 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 40
Slide 40 text
migrations: change column default
class ChangeStatusDefaultOnArticles < ActiveRecord::Migration
def change
change_column :articles, :status, :string, default: 'draft'
end
end
# => ActiveRecord::IrreversibleMigration
!
class ChangeStatusDefaultOnArticles < ActiveRecord::Migration
def change
change_column_default :articles, :status, 'draft'
end
end
# => ActiveRecord::IrreversibleMigration
Slide 41
Slide 41 text
Relation#merge
class Author < ActiveRecord::Base
has_many :articles
!
scope :with_draft_articles, -> {
uniq.joins(:articles).
where(articles: { status: 'draft' })
}
end
Slide 42
Slide 42 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 43
Slide 43 text
Relation#merge
class Author < ActiveRecord::Base
has_many :articles
!
scope :with_draft_articles, -> {
uniq.joins(:articles).merge(Article.draft)
}
end
Slide 44
Slide 44 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 45
Slide 45 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 46
Slide 46 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}
Slide 47
Slide 47 text
first!
relation = Article.where(title: 'UNKNOWN')
!
relation.first or raise(ActiveRecord::RecordNotFound, 'ZOMG not found')
# => ZOMG Article found (ActiveRecord::RecordNotFound)
!
relation.first!
# => ActiveRecord::RecordNotFound
Slide 48
Slide 48 text
last!
relation = Article.where(title: 'UNKNOWN')
!
relation.last or raise(ActiveRecord::RecordNotFound, 'ZOMG not found')
# => ZOMG Article found (ActiveRecord::RecordNotFound)
!
relation.last!
# => ActiveRecord::RecordNotFound
Slide 49
Slide 49 text
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 50
Slide 50 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 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
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 57
Slide 57 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
benchmark
>> ActiveRecord::Base.benchmark 'SUPER EXPENSIVE OPERATION' do
?> Article.expensive_operation
>> end
SUPER EXPENSIVE OPERATION (2000.9ms)
=> "did something very expensive"
Slide 71
Slide 71 text
benchmark
class Article < ActiveRecord::Base
def self.expensive_operation
benchmark 'SUPER EXPENSIVE OPERATION' do
sleep 2
'did something very expensive'
end
end
end
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]", subject: I18n.t('subjects.new_article')
end
end
Slide 85
Slide 85 text
Mailer com i18n subject
en:
subjects:
new_article: 'New article just published.'
Slide 86
Slide 86 text
Mailer com i18n subject
en:
notifier:
new_article:
subject: 'New article just published.'
Slide 87
Slide 87 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 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]",
subject: I18n.t('subjects.new_article', title: 'Clarity is king')
end
end
Slide 89
Slide 89 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 92
Slide 92 text
Action View
Slide 93
Slide 93 text
content_tag_for
<%= content_tag_for :article, @articles do |article| %>
<%= article.title %>
<%= simple_format article.body %>
<% end %>
Slide 94
Slide 94 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 95
Slide 95 text
content_tag_for
<%= content_tag_for :section, @author do |author| %>
<%= author.name %>
…
<% end %>
Slide 96
Slide 96 text
content_tag_for
Author 2
…
Slide 97
Slide 97 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 109
Slide 109 text
tratamento de exceções
$ ls public/*.html
!
404.html 422.html 500.html
Slide 110
Slide 110 text
tratamento de exceções
# config/application.rb
!
# Use the app's own router to display error pages.
config.exceptions_app = self.routes
Slide 111
Slide 111 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 112
Slide 112 text
tratamento de exceções
class ErrorsController < ApplicationController
layout "error"
!
def not_found
render status: :not_found
end
!
def server_error
render status: :server_error
end
!
def unprocessable_entity
render status: :unprocessable_entity
end
end
Slide 113
Slide 113 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 114
Slide 114 text
Bonus Time!
Slide 115
Slide 115 text
Console
Slide 116
Slide 116 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 119
Slide 119 text
console helpers da aplicação
# config/application.rb
module RailsConf2014
class Application < Rails::Application
console do
require 'console_helpers'
Rails::ConsoleMethods.send :include, ConsoleHelpers
end
end
end
Slide 120
Slide 120 text
console helpers da aplicação
>> a(113629430)
Article Load (0.1ms) SELECT "articles".* FROM "articles"
WHERE "articles"."id" = ? LIMIT 1 [["id", 113629430]]
!
=> #
console sandbox
$ rails console --sandbox
…
Any modifications you make will be rolled back on exit
!
!
!
>> exit
rollback transaction
Slide 123
Slide 123 text
Annotations
Slide 124
Slide 124 text
notas
class Author < ActiveRecord::Base
# TODO: create inactive scope
scope :active, -> { where active: true }
end
!
class Article < ActiveRecord::Base
# FIXME: Fix this
scope :published, -> { where status: 'published' }
end
!
class AuthorsController < ApplicationController
# OPTIMIZE: eager load stuff
def show
@author = Author.find(params[:id])
end
end
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 131
Slide 131 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 132
Slide 132 text
Cada parte do Rails tem
seus truques escondidos
Slide 133
Slide 133 text
Eles são às vezes
difíceis de achar
Slide 134
Slide 134 text
Mas eles podem te deixar
muito mais produtivo
Slide 135
Slide 135 text
Então toda vez que você
começar algo novo
Slide 136
Slide 136 text
Antes mesmo de considerar
usar o pattern X, Y ou Z
Slide 137
Slide 137 text
Pergunte-se: Como o
Rails pode me ajudar?
Slide 138
Slide 138 text
Pesquise
API
Guidelines
Google
Stack Overflow
…
Slide 139
Slide 139 text
Pergunte
Stack Overflow
Rails Talk mailing list
IRC
…