I’ve participated in
several projects with
different contexts
Slide 5
Slide 5 text
I’ve seen code
Slide 6
Slide 6 text
I’ve seen complex code
Slide 7
Slide 7 text
I’ve seen duplicated code
Slide 8
Slide 8 text
I’ve seen Rails code
reimplemented in apps
Slide 9
Slide 9 text
How often do you see them?
All the time. They're everywhere.
Slide 10
Slide 10 text
You get used to it after
some time
Slide 11
Slide 11 text
And that’s a big problem
People are afraid to deal with code like that.
Slide 12
Slide 12 text
Rails tricks / features
Slide 13
Slide 13 text
That might help you
refactor existing code
or improve your developer life a bit
Slide 14
Slide 14 text
No content
Slide 15
Slide 15 text
No content
Slide 16
Slide 16 text
Web Development
Consultancy
Mainly focused in Ruby and Rails
Based in São Paulo - Brazil
Slide 17
Slide 17 text
No content
Slide 18
Slide 18 text
❤️
Slide 19
Slide 19 text
plataformatec/devise
Slide 20
Slide 20 text
plataformatec/simple_form
Slide 21
Slide 21 text
elixir-lang/elixir
Slide 22
Slide 22 text
Rails Core Team
! Carlos Antonio
Slide 23
Slide 23 text
Rails Core Team
José Valim Carlos Antonio Rafael França
Slide 24
Slide 24 text
We use pull requests
and code review
All the f*cking time.
Slide 25
Slide 25 text
We do cross project
code review
Sometimes, when the time allows us.
Slide 26
Slide 26 text
And it’s usual to learn
new things in the process
We’re always learning, right?
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
No content
Slide 32
Slide 32 text
ಠ_ಠ
I had to do this talk after this look of disapproval
Slide 33
Slide 33 text
Rails tricks / features
Not used by many
Not very well known and / or documented
Slide 34
Slide 34 text
What we will see:
Active Record
Active Model
Action Mailer
Action View
Action Controller
Bonus Stuff
Slide 35
Slide 35 text
Active Record
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 :authors, :name, :string, null: false
end
end
# => ActiveRecord::IrreversibleMigration
!
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 :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 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'
Slide 46
Slide 46 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 47
Slide 47 text
group counting: multiple attrs
>> 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 48
Slide 48 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 49
Slide 49 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 50
Slide 50 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 51
Slide 51 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 52
Slide 52 text
eager load strategies
>> Article.includes(:author)
!
SELECT "articles".* FROM "articles"
!
SELECT "authors".* FROM "authors"
WHERE "authors"."id" IN (980190962, 298486374)
Slide 53
Slide 53 text
eager load strategies
>> 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 54
Slide 54 text
eager load strategies
>> 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 55
Slide 55 text
eager load strategies
>> Article.preload(:author)
!
SELECT "articles".* FROM “articles"
!
SELECT "authors".* FROM “authors"
WHERE "authors"."id" IN (980190962, 298486374)
Slide 56
Slide 56 text
eager load strategies
>> 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 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 created_at DESC
Slide 58
Slide 58 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 72
Slide 72 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 73
Slide 73 text
No content
Slide 74
Slide 74 text
http://railsgirlssummerofcode.org/campaign/
And I got a free hug!
Slide 75
Slide 75 text
Active Model
Slide 76
Slide 76 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 80
Slide 80 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 83
Slide 83 text
Action Mailer
Slide 84
Slide 84 text
Mailer with 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 85
Slide 85 text
Mailer with 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 with i18n subject
en:
subjects:
new_article: 'New article just published.'
Slide 87
Slide 87 text
Mailer with i18n subject
en:
notifier:
new_article:
subject: 'New article just published.'
Slide 88
Slide 88 text
Mailer with 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 with 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 with i18n subject
en:
subjects:
new_article: 'New article: %{title}.'
Mailer with 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| %>
redirects with params
get '/articles/:id' => redirect('/articles/%{id}/edit')
Slide 110
Slide 110 text
exceptions handling
$ ls public/*.html
!
404.html 422.html 500.html
Slide 111
Slide 111 text
exceptions handling
# config/application.rb
!
# Use the app's own router to display error pages.
config.exceptions_app = self.routes
Slide 112
Slide 112 text
exceptions handling
# config/routes.rb
!
get "/404", to: "errors#not_found"
get "/422", to: "errors#unprocessable_entity"
get "/500", to: "errors#server_error"
Slide 113
Slide 113 text
exceptions handling
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
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
application console helpers
# lib/console_helpers.rb
module ConsoleHelpers
def a(id)
Article.find(id)
end
end
Slide 120
Slide 120 text
application console helpers
# config/application.rb
module RailsConf2014
class Application < Rails::Application
console do
require 'console_helpers'
Rails::ConsoleMethods.send :include, ConsoleHelpers
end
end
end
console sandbox
$ rails console --sandbox
…
Any modifications you make will be rolled back on exit
!
!
!
>> exit
rollback transaction
Slide 124
Slide 124 text
Annotations
Slide 125
Slide 125 text
annotations
class Author < ActiveRecord::Base
# TODO: create inactive scope
scope :active, -> { where active: true }
end
!
class Article < ActiveRecord::Base
# FIXME: Fix for RailsConf
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 132
Slide 132 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 133
Slide 133 text
Every single part of
Rails has hidden tricks
Slide 134
Slide 134 text
They are sometimes
hard to spot
Slide 135
Slide 135 text
But they can make you
much more productive
Slide 136
Slide 136 text
So every time you start
something new
Slide 137
Slide 137 text
Before ever considering
about pattern X, Y or Z
Slide 138
Slide 138 text
Ask yourself:
How Rails can help me?
Slide 139
Slide 139 text
Search
API
Guidelines
Google
Stack Overflow
…
Slide 140
Slide 140 text
Ask
Stack Overflow
Rails Talk mailing list
IRC
…
Slide 141
Slide 141 text
Follow
Changelogs
Release Notes
Upgrading Guides
Commits (if you can)
Slide 142
Slide 142 text
And if you do find some
hidden trick, remember…
Slide 143
Slide 143 text
With great moustache!
!
!
!
comes great responsibility
Slide 144
Slide 144 text
Share
Tweet
Blog post
Tell your friends
…
Slide 145
Slide 145 text
Contribute
Back to Rails
Slide 146
Slide 146 text
And please ping me too!
!
@cantoniodasilva
Slide 147
Slide 147 text
Thank you ❤️
Slide 148
Slide 148 text
Stickers!
@plataformatec
Slide 149
Slide 149 text
Questions?
Get one of these mugs (5 only :D)
Slide 150
Slide 150 text
Tricks that Rails!
didn’t tell you about
Carlos Antonio - @cantoniodasilva