Slide 1

Slide 1 text

Os truques que o Rails não te contou

Slide 2

Slide 2 text

Carlos Antonio @cantoniodasilva

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

~6 anos Ruby / Rails

Slide 5

Slide 5 text

Eu já participei em vários projetos com contextos diferentes

Slide 6

Slide 6 text

Eu já vi código

Slide 7

Slide 7 text

Eu já vi código complexo

Slide 8

Slide 8 text

Eu já vi código duplicado

Slide 9

Slide 9 text

! Eu já vi código do Rails reimplementado em apps

Slide 10

Slide 10 text

Com que frequência você vê eles? Todo o tempo. Eles estão em toda parte.

Slide 11

Slide 11 text

Você se acostuma com o código após algum tempo

Slide 12

Slide 12 text

E isso é um grande problema As pessoas têm medo de lidar com esse tipo de código.

Slide 13

Slide 13 text

Rails tricks / features Não usadas por muitos Não muito conhecidas e / ou documentadas

Slide 14

Slide 14 text

Que podem te ajudar a refatorar código existente ou melhorar sua vida como desenvolvedor um pouquinho.

Slide 15

Slide 15 text

TDD IS DEAD

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Devise Rails Summit 2009

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

http://blog.plataformatec.com.br/2014/08/thank-you-carlos-antonio/

Slide 21

Slide 21 text

https://www.enjoei.com.br

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

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'

Slide 46

Slide 46 text

find_by >> Article.where(title: 'ZOMG').first => # ! >> Article.find_by(title: 'ZOMG') => #

Slide 47

Slide 47 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 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}

Slide 58

Slide 58 text

pluck >> Author.all.map(&:name) ! SELECT "authors".* FROM "authors" ! => ["Author 2", "Author 1"]

Slide 59

Slide 59 text

pluck >> Author.select(:name).map(&:name) ! SELECT "authors"."name" FROM "authors" ! => ["Author 2", "Author 1"]

Slide 60

Slide 60 text

pluck >> Author.pluck(:name) ! SELECT "authors"."name" FROM "authors" ! => ["Author 2", "Author 1"]

Slide 61

Slide 61 text

pluck: múltiplos atributos >> Author.pluck(:name, :active) ! SELECT "authors"."name", "authors"."active" FROM "authors" ! => [["Author 2", true], ["Author 1", true]]

Slide 62

Slide 62 text

pluck: valores distintos >> Author.distinct.pluck(:name) ! SELECT DISTINCT "authors"."name" FROM "authors" ! => ["Author 2", "Author 1"]

Slide 63

Slide 63 text

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

Slide 71

Slide 71 text

benchmark >> start = Time.now => 2014-04-21 10:48:30 -0500 >> Article.expensive_operation => "did something very expensive" >> finish = Time.now => 2014-04-21 10:48:32 -0500 >> finish - start => 2.02356

Slide 72

Slide 72 text

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

Slide 76

Slide 76 text

ActiveModel::Model contact = EmailContact.new contact.name = 'Carlos' contact.email = '[email protected]' contact.message = 'ZOMG' contact.deliver

Slide 77

Slide 77 text

ActiveModel::Model <%= form_for @email_contact … %> ! <%= render @email_contact %>

Slide 78

Slide 78 text

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

Slide 80

Slide 80 text

ActiveModel::Model contact = EmailContact.new( name: 'Carlos', email: '[email protected]', message: 'ZOMG' ) contact.deliver

Slide 81

Slide 81 text

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}.'

Slide 91

Slide 91 text

Mailer com i18n subject en: notifier: new_article: subject: 'New article %{title}.'

Slide 92

Slide 92 text

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 %>

No article found.

<% end %>

Slide 99

Slide 99 text

render com coleções vazias <%= render(author.articles) || content_tag(:p, 'No article found.') %>

Slide 100

Slide 100 text

truncar textos <%= truncate article.body do %> <%= link_to 'read more...', article %> <% end %> ! ! Rails 4.2.0 is going to be ... read more…

Slide 101

Slide 101 text

I18n + html <%= link_to t('read_more'), article %> ! en: read_more: 'read more…' ! ! read more…

Slide 102

Slide 102 text

I18n + html <%= link_to t('read_more'), article %> ! en: read_more: 'read more…' ! ! read <b>more</b>…

Slide 103

Slide 103 text

I18n + html <%= link_to raw(t('read_more')), article %> ! en: read_more: 'read more…' ! ! read more

Slide 104

Slide 104 text

I18n + html <%= link_to t(‘read_more_html'), article %> ! en: read_more_html: 'read more…' ! ! read more

Slide 105

Slide 105 text

benchmark <% benchmark 'Rendering articles' do %> <%= sleep(2); render author.articles %> <% end %> ! ! # logger Rendering articles (2005.1ms)

Slide 106

Slide 106 text

Action Controller

Slide 107

Slide 107 text

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

Slide 115

Slide 115 text

helper >> helper.time_ago_in_words 30.days.ago => "about 1 month" ! >> helper.hello_ruby_conf => "Hello RubyConfBR 2014!" ! >> helper.class => ActionView::Base

Slide 116

Slide 116 text

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]] ! => #

Slide 119

Slide 119 text

console sandbox $ rails runner 'puts Article.count' 4 ! $ rails console --sandbox >> Article.create! title: 'I will be rolled back' => # >> Article.count => 5 ! $ rails runner 'puts Article.count' 4

Slide 120

Slide 120 text

console sandbox $ rails console --sandbox … Any modifications you make will be rolled back on exit ! ! ! >> exit rollback transaction

Slide 121

Slide 121 text

Atualizando Rails

Slide 122

Slide 122 text

rails update gem 'rails', '4.1.0' ! ! $ bundle update …

Slide 123

Slide 123 text

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 …

Slide 133

Slide 133 text

Siga Changelogs Release Notes Upgrading Guides Commits (se puder)

Slide 134

Slide 134 text

E se você encontrar algum truque, lembre-se…

Slide 135

Slide 135 text

No content

Slide 136

Slide 136 text

Compartilhe Tweet Blog post Diga aos seus amigos …

Slide 137

Slide 137 text

Contribua De volta para o Rails

Slide 138

Slide 138 text

E por favor me avise! :) ! @cantoniodasilva

Slide 139

Slide 139 text

Carlos Antonio https://speakerdeck.com/carlosantoniodasilva @cantoniodasilva Os truques que o Rails não te contou [email protected]