Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tricks that Rails didn't tell you about @ RailsConf 2014

Tricks that Rails didn't tell you about @ RailsConf 2014

Carlos Antonio

April 23, 2014
Tweet

More Decks by Carlos Antonio

Other Decks in Technology

Transcript

  1. Tricks that Rails didn’t
    tell you about

    View full-size slide

  2. Carlos Antonio
    @cantoniodasilva

    View full-size slide

  3. ~6 years
    on Rails

    View full-size slide

  4. I’ve participated in
    several projects with
    different contexts

    View full-size slide

  5. I’ve seen code

    View full-size slide

  6. I’ve seen complex code

    View full-size slide

  7. I’ve seen duplicated code

    View full-size slide

  8. I’ve seen Rails code
    reimplemented in apps

    View full-size slide

  9. How often do you see them?
    All the time. They're everywhere.

    View full-size slide

  10. You get used to it after
    some time

    View full-size slide

  11. And that’s a big problem
    People are afraid to deal with code like that.

    View full-size slide

  12. Rails tricks / features

    View full-size slide

  13. That might help you
    refactor existing code
    or improve your developer life a bit

    View full-size slide

  14. Web Development
    Consultancy
    Mainly focused in Ruby and Rails
    Based in São Paulo - Brazil

    View full-size slide

  15. plataformatec/devise

    View full-size slide

  16. plataformatec/simple_form

    View full-size slide

  17. elixir-lang/elixir

    View full-size slide

  18. Rails Core Team
    ! Carlos Antonio

    View full-size slide

  19. Rails Core Team
    José Valim Carlos Antonio Rafael França

    View full-size slide

  20. We use pull requests
    and code review
    All the f*cking time.

    View full-size slide

  21. We do cross project
    code review
    Sometimes, when the time allows us.

    View full-size slide

  22. And it’s usual to learn
    new things in the process
    We’re always learning, right?

    View full-size slide

  23. ಠ_ಠ
    I had to do this talk after this look of disapproval

    View full-size slide

  24. Rails tricks / features
    Not used by many
    Not very well known and / or documented

    View full-size slide

  25. What we will see:
    Active Record
    Active Model
    Action Mailer
    Action View
    Action Controller
    Bonus Stuff

    View full-size slide

  26. Active Record

    View full-size slide

  27. 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

    View full-size slide

  28. 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

    View full-size slide

  29. 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

    View full-size slide

  30. 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

    View full-size slide

  31. 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

    View full-size slide

  32. 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

    View full-size slide

  33. Relation#merge
    class Author < ActiveRecord::Base
    has_many :articles
    !
    scope :with_draft_articles, -> {
    uniq.joins(:articles).
    where(articles: { status: 'draft' })
    }
    end

    View full-size slide

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

    View full-size slide

  35. Relation#merge
    class Author < ActiveRecord::Base
    has_many :articles
    !
    scope :with_draft_articles, -> {
    uniq.joins(:articles).merge(Article.draft)
    }
    end

    View full-size slide

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

    View full-size slide

  37. group counting
    >> Article.group(:status).count
    !
    SELECT COUNT(*) AS count_all, status AS status
    FROM "articles"
    GROUP BY status
    !
    => {"draft"=>7, "published"=>2}

    View full-size slide

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

    View full-size slide

  39. first!
    relation = Article.where(title: 'UNKNOWN')
    !
    relation.first or raise(ActiveRecord::RecordNotFound, 'ZOMG not found')
    # => ZOMG Article found (ActiveRecord::RecordNotFound)
    !
    relation.first!
    # => ActiveRecord::RecordNotFound

    View full-size slide

  40. last!
    relation = Article.where(title: 'UNKNOWN')
    !
    relation.last or raise(ActiveRecord::RecordNotFound, 'ZOMG not found')
    # => ZOMG Article found (ActiveRecord::RecordNotFound)
    !
    relation.last!
    # => ActiveRecord::RecordNotFound

    View full-size slide

  41. 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')

    View full-size slide

  42. 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')

    View full-size slide

  43. eager load strategies
    >> Article.includes(:author)
    !
    SELECT "articles".* FROM "articles"
    !
    SELECT "authors".* FROM "authors"
    WHERE "authors"."id" IN (980190962, 298486374)

    View full-size slide

  44. 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

    View full-size slide

  45. 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"

    View full-size slide

  46. eager load strategies
    >> Article.preload(:author)
    !
    SELECT "articles".* FROM “articles"
    !
    SELECT "authors".* FROM “authors"
    WHERE "authors"."id" IN (980190962, 298486374)

    View full-size slide

  47. 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)

    View full-size slide

  48. 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

    View full-size slide

  49. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  53. pluck: multiple attrs
    >> Author.pluck(:name, :active)
    !
    SELECT "authors"."name", "authors"."active"
    FROM "authors"
    !
    => [["Author 2", true], ["Author 1", true]]

    View full-size slide

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

    View full-size slide

  55. to_param
    class Article < ActiveRecord::Base
    def to_param
    "#{id}-#{title.parameterize}"
    end
    end
    !
    >> Article.all.map(&:to_param)
    => ["113629430-autoload",
    "281110143-rails-4-2-0-is-going-to-be-more-adequate",
    "298486374-rails-4-1-0-released",
    "980190962-rails-tricks"]

    View full-size slide

  56. to_param
    class Article < ActiveRecord::Base
    to_param :title
    end
    !
    !
    !
    >> Article.all.map(&:to_param)
    => ["113629430-autoload",
    "281110143-rails-4-2-0-is",
    "298486374-rails-4-1-0-released",
    "980190962-rails-tricks"]

    View full-size slide

  57. exists?/any? vs present?
    <% if @author.articles.exists? %>
    <%= render @author.articles %>
    <% else %>
    No article found.
    <% end %>

    View full-size slide

  58. exists?/any? vs present?
    >> author = Author.first
    !
    >> author.articles.exists?
    !
    SELECT 1 AS one FROM "articles"
    WHERE "articles"."author_id" = ? LIMIT 1 [["author_id", 298486374]]
    !
    => true
    !
    >> author.articles.each {}
    !
    SELECT "articles".* FROM "articles"
    WHERE "articles"."author_id" = ? [["author_id", 298486374]]

    View full-size slide

  59. exists?/any? vs present?
    >> author = Author.first
    !
    >> author.articles.present?
    !
    SELECT "articles".* FROM "articles"
    WHERE "articles"."author_id" = ? [["author_id", 298486374]]
    !
    => true
    !
    >> author.articles.each {}
    !
    # => already loaded, so no query

    View full-size slide

  60. benchmark
    class Article < ActiveRecord::Base
    def self.expensive_operation
    sleep 2
    'did something very expensive'
    end
    end

    View full-size slide

  61. 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

    View full-size slide

  62. benchmark
    >> ActiveRecord::Base.benchmark 'SUPER EXPENSIVE OPERATION' do
    ?> Article.expensive_operation
    >> end
    SUPER EXPENSIVE OPERATION (2000.9ms)
    => "did something very expensive"

    View full-size slide

  63. benchmark
    class Article < ActiveRecord::Base
    def self.expensive_operation
    benchmark 'SUPER EXPENSIVE OPERATION' do
    sleep 2
    'did something very expensive'
    end
    end
    end

    View full-size slide

  64. http://railsgirlssummerofcode.org/campaign/
    And I got a free hug!

    View full-size slide

  65. Active Model

    View full-size slide

  66. ActiveModel::Model
    class EmailContact
    attr_accessor :name, :email, :message
    !
    def deliver
    if name.present? && email.present? && message.present?
    puts 'deliver email'
    end
    end
    end

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  69. 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

    View full-size slide

  70. 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

    View full-size slide

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

    View full-size slide

  72. 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"

    View full-size slide

  73. Action Mailer

    View full-size slide

  74. 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

    View full-size slide

  75. 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

    View full-size slide

  76. Mailer with i18n subject
    en:
    subjects:
    new_article: 'New article just published.'

    View full-size slide

  77. Mailer with i18n subject
    en:
    notifier:
    new_article:
    subject: 'New article just published.'

    View full-size slide

  78. Mailer with i18n subject
    class Notifier < ActionMailer::Base
    default from: "[email protected]"
    !
    def new_article
    @greeting = "Hi"
    !
    mail to: "[email protected]"
    end
    end

    View full-size slide

  79. 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

    View full-size slide

  80. Mailer with i18n subject
    en:
    subjects:
    new_article: 'New article: %{title}.'

    View full-size slide

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

    View full-size slide

  82. 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

    View full-size slide

  83. content_tag_for
    <%= content_tag_for :article, @articles do |article| %>
    <%= article.title %>
    <%= simple_format article.body %>
    <% end %>

    View full-size slide

  84. 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

    View full-size slide

  85. content_tag_for
    <%= content_tag_for :section, @author do |author| %>
    <%= author.name %>

    <% end %>

    View full-size slide

  86. content_tag_for

    Author 2


    View full-size slide

  87. render empty collections
    <% if author.articles.exists? %>
    <%= render author.articles %>
    <% else %>
    No article found.
    <% end %>

    View full-size slide

  88. render empty collections
    <%= render(author.articles) ||
    content_tag(:p, 'No article found.') %>

    View full-size slide

  89. partial locals
    <%= render article, full: true %>
    <%= render author.articles, full: false %>
    !
    # article partial
    <%= content_tag_for :article, article do |article| %>
    <%= article.title %>
    !
    <% if full %>
    <%= simple_format article.body %>
    <% else %>
    <%= truncate article.body %>
    <% end %>
    <% end %>

    View full-size slide

  90. partial locals
    <%= render article, full: true %>
    <%= render author.articles %>
    !
    # article partial
    <%= content_tag_for :article, article do |article| %>
    <%= article.title %>
    !
    <% if local_assigns[:full] %>
    <%= simple_format article.body %>
    <% else %>
    <%= truncate article.body %>
    <% end %>
    <% end %>

    View full-size slide

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

    read more…

    View full-size slide

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

    read more…

    View full-size slide

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

    read <b>more</b>…

    View full-size slide

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

    read more…

    View full-size slide

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

    read more…

    View full-size slide

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

    View full-size slide

  97. Action Controller

    View full-size slide

  98. redirects with params
    get '/articles/:id' => redirect('/articles/%{id}/edit')

    View full-size slide

  99. exceptions handling
    $ ls public/*.html
    !
    404.html 422.html 500.html

    View full-size slide

  100. exceptions handling
    # config/application.rb
    !
    # Use the app's own router to display error pages.
    config.exceptions_app = self.routes

    View full-size slide

  101. exceptions handling
    # config/routes.rb
    !
    get "/404", to: "errors#not_found"
    get "/422", to: "errors#unprocessable_entity"
    get "/500", to: "errors#server_error"

    View full-size slide

  102. 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

    View full-size slide

  103. exceptions handling
    app/views/
    errors/
    not_found.html.erb
    server_error.html.erb
    unprocessable_entity.html.erb
    layouts/
    error.html.erb

    View full-size slide

  104. 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

    View full-size slide

  105. helper
    >> helper.time_ago_in_words 30.days.ago
    => "about 1 month"
    !
    >> helper.hello_rails_conf
    => "Hello RailsConf 2014!"
    !
    >> helper.class
    => ActionView::Base

    View full-size slide

  106. application console helpers
    # lib/console_helpers.rb
    module ConsoleHelpers
    def a(id)
    Article.find(id)
    end
    end

    View full-size slide

  107. 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

    View full-size slide

  108. application console helpers
    >> a(113629430)
    Article Load (0.1ms) SELECT "articles".* FROM "articles"
    WHERE "articles"."id" = ? LIMIT 1 [["id", 113629430]]
    !
    => #

    View full-size slide

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

    View full-size slide

  110. console sandbox
    $ rails console --sandbox

    Any modifications you make will be rolled back on exit
    !
    !
    !
    >> exit
    rollback transaction

    View full-size slide

  111. 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

    View full-size slide

  112. annotations
    $ rake notes
    !
    app/controllers/authors_controller.rb:
    * [2] [OPTIMIZE] eager load stuff
    !
    app/models/article.rb:
    * [9] [FIXME] Fix for RailsConf
    !
    app/models/author.rb:
    * [4] [TODO] create inactive scope

    View full-size slide

  113. custom annotations
    class Notifier < ActionMailer::Base
    # RAILSCONF: Hello!
    def new_article
    end
    end

    View full-size slide

  114. custom annotations
    $ rake notes:custom ANNOTATION=RAILSCONF
    !
    app/mailers/notifier.rb:
    * [4] Hello!

    View full-size slide

  115. Updating Rails

    View full-size slide

  116. rails update
    gem 'rails', '4.1.0'
    !
    !
    $ bundle update

    View full-size slide

  117. 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

    View full-size slide

  118. 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

    View full-size slide

  119. Every single part of
    Rails has hidden tricks

    View full-size slide

  120. They are sometimes
    hard to spot

    View full-size slide

  121. But they can make you
    much more productive

    View full-size slide

  122. So every time you start
    something new

    View full-size slide

  123. Before ever considering
    about pattern X, Y or Z

    View full-size slide

  124. Ask yourself:
    How Rails can help me?

    View full-size slide

  125. Search
    API
    Guidelines
    Google
    Stack Overflow

    View full-size slide

  126. Ask
    Stack Overflow
    Rails Talk mailing list
    IRC

    View full-size slide

  127. Follow
    Changelogs
    Release Notes
    Upgrading Guides
    Commits (if you can)

    View full-size slide

  128. And if you do find some
    hidden trick, remember…

    View full-size slide

  129. With great moustache!
    !
    !
    !
    comes great responsibility

    View full-size slide

  130. Share
    Tweet
    Blog post
    Tell your friends

    View full-size slide

  131. Contribute
    Back to Rails

    View full-size slide

  132. And please ping me too!
    !
    @cantoniodasilva

    View full-size slide

  133. Thank you ❤️

    View full-size slide

  134. Stickers!
    @plataformatec

    View full-size slide

  135. Questions?
    Get one of these mugs (5 only :D)

    View full-size slide

  136. Tricks that Rails!
    didn’t tell you about
    Carlos Antonio - @cantoniodasilva

    View full-size slide