Slide 1

Slide 1 text

Slides at speakerdeck.com/claudiob Rails 5.1: awesome features & breaking changes

Slide 2

Slide 2 text

Rails 5.1: awesome features & breaking changes $ git log v4.2.0..v5.0.0 # => 18 months # => 962 authors # => 9,998 commits $ git log v5.0.0..v5.1.0.rc2 # => 9 months # => 450 authors # => 4,065 commits 5.0 5.1

Slide 3

Slide 3 text

Breaking changes

Slide 4

Slide 4 text

PR 27757 by Jeremy Evans Change Action View ERB handler from Erubis to Erubi Breaking change

Slide 5

Slide 5 text

commit 0952552 by Ryuta Kamizono Deprecate String options :if and :unless to set/skip callbacks Breaking change

Slide 6

Slide 6 text

Deprecate String options to set/skip callbacks class Post < ApplicationRecord before_save :set_no_title, unless: 'title.present?' skip_callback :save, :before, :set_no_title, if: 'persisted?' private def set_no_title self.title = 'No title' end end 5.0

Slide 7

Slide 7 text

Deprecate String options to set/skip callbacks class Post < ApplicationRecord before_save :set_no_title, unless: -> { title.present? } skip_callback :save, :before, :set_no_title, if: :persisted? private def set_no_title self.title = 'No title' end end 5.1

Slide 8

Slide 8 text

PR 23980 by Andrew White Deprecate :action and :controller path parameters Breaking change

Slide 9

Slide 9 text

Deprecate :action and :controller path parameters # config/routes.rb get ':controller(/:action(/:id))' [CVE-2014-0130] Directory traversal vulnerability (4.1.1, 4.0.5) [CVE-2015-7581] Object leak vulnerability (4.2.5, 4.1.13) # config/routes.rb get ':controller(/:action(/:id))' # deprecated get 'photos(/:id)', to: :display # acceptable 5.0 5.1

Slide 10

Slide 10 text

Active Record

Slide 11

Slide 11 text

Change default primary keys to BIGINT PR 26266 by Jon McCartie Active Record

Slide 12

Slide 12 text

Virtual column support for MySQL and MariaDB commit 65bf1c6 by Ryuta Kamizono Active Record

Slide 13

Slide 13 text

Virtual column support for MySQL and MariaDB class CreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| t.string :name t.virtual :upper_name, type: :string, as: "UPPER(name)" t.virtual :name_length, type: :integer, as: "LENGTH(name)", stored: true, index: true end end end 5.1

Slide 14

Slide 14 text

commit 451437c by Xavier Noria Adds support for limits in batch processing Active Record

Slide 15

Slide 15 text

Adds support for limits in batch processing Post.limit(500).find_each.map(&:title) # Scoped order and limit are ignored, it's forced to be batch order and batch size. # => Post Load (0.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 1000]] Post.limit(500).find_each.map(&:title) # => Post Load (0.1ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 500]] 5.0 5.1

Slide 16

Slide 16 text

Active Support

Slide 17

Slide 17 text

Update Unicode version to 9.0.0 PR 27822 by Fumiaki Matsushima Active Support

Slide 18

Slide 18 text

Update Unicode version to 9.0.0 ActiveSupport::Multibyte::Unicode::UNICODE_VERSION # => "8.0.0" " " ".mb_chars.grapheme_length # => 4 " " ".mb_chars.reverse # => " " ActiveSupport::Multibyte::Unicode::UNICODE_VERSION # => "9.0.0" " " ".mb_chars.grapheme_length # => 1 " " ".mb_chars.reverse # => " " " 5.0 5.1

Slide 19

Slide 19 text

Add Duration#after and #before as alias for #since and #until PR 27721 by Nick Johnstone Active Support

Slide 20

Slide 20 text

Add Duration#after and #before as aliases… 2.weeks.since(christmas_day) # => Mon, 08 Jan 2018 5.days.until(christmas_day) # => Wed, 20 Dec 2017 2.weeks.after(christmas_day) # => Mon, 08 Jan 2018 5.days.before(christmas_day) # => Wed, 20 Dec 2017 5.0 5.1

Slide 21

Slide 21 text

Introduce Module# delegate_missing_to PR 23930 by Genadi Samokovarov Active Support

Slide 22

Slide 22 text

Introduce Module#delegate_missing_to class OrderCompletion delegate :destroy, :update, …more methods…, to: :@order def initialize(order) @order = order end def create OrderCompletionNotification.deliver(@order) if @order.save end end 5.0

Slide 23

Slide 23 text

Introduce Module#delegate_missing_to class OrderCompletion delegate_missing_to :@order def initialize(order) @order = order end def create OrderCompletionNotification.deliver(@order) if @order.save end end 5.1

Slide 24

Slide 24 text

Action View

Slide 25

Slide 25 text

New syntax for Action View’s tag helpers PR 25543 by Marek Kirejczyk Action View

Slide 26

Slide 26 text

New syntax for Action View’s tag helpers tag(:br, nil, true) <%= content_tag :div, class: "strong" do -%> Hello world! <% end -%> tag.br <%= tag.div class: "strong" do %> Hello world! <% end %> 5.0 5.1

Slide 27

Slide 27 text

PR 26976 by Kasper Timm Hansen Add form_with to unify form_tag and form_for Action View

Slide 28

Slide 28 text

Add form_with to unify form_tag and form_for # app/views/posts/show.html.erb <%= form_for @post, method: :delete do |f| %> <%= f.submit 'Delete post' %> <% end %> # app/views/posts/show.html.erb <%= form_with model: @post, method: :delete do |f| %> <%= f.submit 'Delete post' %> <% end %> 5.0 5.1

Slide 29

Slide 29 text

Add form_with to unify form_tag and form_for # app/views/posts/index.html.erb <%= form_tag '/posts/1', method: :delete do %> <%= submit_tag 'Delete post #1' %> <% end %> # app/views/posts/index.html.erb <%= form_with url: '/posts/1', method: :delete do |f| %> <%= submit_tag 'Delete post #1' %> <% end %> 5.0 5.1

Slide 30

Slide 30 text

Railties

Slide 31

Slide 31 text

PR 25257 by ta1kt0me Display railtie class name in "rails initializers" Railties

Slide 32

Slide 32 text

Display railtie class name in `rails initializers` $ rails initializers set_load_path set_load_path [124 more…] $ rails initializers ActionView::Railtie.set_load_path ActionCable::Engine.set_load_path [126 more…] 5.0 5.1

Slide 33

Slide 33 text

commit e530534 by David Heinemeier Hansson Added a shared section to config/secrets.yml Railties

Slide 34

Slide 34 text

Added a shared section to config/secrets.yml # config.secrets.yml default: &default email_from: [email protected] development: <<: *default shared: email_from: [email protected] 5.0 5.1

Slide 35

Slide 35 text

Add encrypted secrets in config/ secrets.yml.enc PR 28038 by Kasper Timm Hansen Railties

Slide 36

Slide 36 text

Add encrypted secrets in config/secrets.yml.enc # config/secrets.yml # Do not keep production secrets in the repository, # instead read values from the environment. production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> $ rails console production > Rails.application.secrets # => {:secret_key_base=>nil, :secret_token=>nil} 5.0 5.0

Slide 37

Slide 37 text

Add encrypted secrets in config/secrets.yml.enc $ rails secrets:setup Adding config/secrets.yml.key to store the encryption key: 667c… $ more config/secrets.yml.enc # QMdWtttAU5ZJVrsJRFot4j0gCgaqjjsLA0CIFnAhdc+LByI4ILZFwR $ EDITOR=vi rails secrets:edit # See `secrets.yml` for tips on generating suitable keys. production: api_key: "466aac22e6a869134be3d09b9e89232fc2c228" 5.1

Slide 38

Slide 38 text

PR 27113 by Guillermo Iguaran Drop jQuery as a dependency and include rails-ujs Railties

Slide 39

Slide 39 text

Drop jQuery as a dependency and include rails-ujs <%= link_to 'Destroy', post, method: :delete, data: {confirm: 'Are you sure?'} %> Destroy

Slide 40

Slide 40 text

Drop jQuery as a dependency and include rails-ujs <%= link_to 'Destroy', post, method: :delete, data: {confirm: 'Are you sure?'} %> Destroy

Slide 41

Slide 41 text

Drop jQuery as a dependency and include rails-ujs # Gemfile gem 'jquery-rails' # app/assets/javascripts/application.js //= require jquery //= require jquery_ujs # app/assets/javascripts/application.js //= require rails-ujs 5.0 5.1

Slide 42

Slide 42 text

PR 26836 by Liceth Ovalles Rodriguez Add Yarn support in new apps using --yarn option Railties

Slide 43

Slide 43 text

Add Yarn support in new apps $ yarn init $ yarn add bootstrap $ echo ".node_modules/" >> .gitignore # config/initializers/assets.rb config.assets.paths << Rails.root.join('node_modules') # app/assets/stylesheets/application.css *= require bootstrap/dist/css/bootstrap # app/assets/javascripts/application.js //= require bootstrap/dist/js/bootstrap 5.0

Slide 44

Slide 44 text

Add Yarn support in new apps $ bin/yarn init $ bin/yarn add bootstrap # app/assets/stylesheets/application.css *= require bootstrap/dist/css/bootstrap # app/assets/javascripts/application.js //= require bootstrap/dist/js/bootstrap 5.1

Slide 45

Slide 45 text

PR 27288 by David Heinemeier Hansson Basic --webpack delegation to new webpacker gem Railties

Slide 46

Slide 46 text

Basic --webpack delegation to new webpacker gem $ rails new app_name --webpack 5.1 rails webpacker:install Creating javascript app source directory create app/javascript create app/javascript/packs/application.js Copying binstubs create bin/webpack-dev-server create bin/webpack-watcher create bin/webpack Copying webpack core config and loaders create config/webpack/configuration.js create config/webpack/development.js create config/webpack/development.server.js create config/webpack/development.server.yml create config/webpack/paths.yml create config/webpack/production.js create config/webpack/shared.js create config/webpack/loaders create config/webpack/loaders/assets.js create config/webpack/loaders/babel.js create config/webpack/loaders/coffee.js create config/webpack/loaders/erb.js create config/webpack/loaders/sass.js create .postcssrc.yml create .gitignore Installing all JavaScript dependencies run ./bin/yarn add webpack webpack-merge…

Slide 47

Slide 47 text

Basic --webpack delegation to new webpacker gem $ webpack-dev-server # app/views/layouts/application.html.erb <%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %> # app/javascript/packs/application.js.erb require('moment') <% helpers = ActionController::Base.helpers %> var railsImagePath = "<%= helpers.image_path('rails.png') %>" 5.1

Slide 48

Slide 48 text

Action Pack

Slide 49

Slide 49 text

PR 26703 by Eileen M. Uchitelle Capybara integration with Rails (AKA System Tests) Action Pack

Slide 50

Slide 50 text

Capybara integration with Rails (AKA System Tests)

Slide 51

Slide 51 text

Capybara integration with Rails (AKA System Tests) # Gemfile group :development, :test do gem 'capybara' gem 'selenium-webdriver' end # test/test_helper.rb require 'capybara/rails' require 'capybara/minitest' […] 5.0

Slide 52

Slide 52 text

Capybara integration with Rails (AKA System Tests) # test/test_helper.rb class ActionDispatch::IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new app, browser: :chrome end Capybara.default_driver = :selenium end 5.0

Slide 53

Slide 53 text

Custom URL helpers and polymorphic mapping $ rails test:integration # => 1 runs, 1 assertions, 0 failures # test/integration/posts_test.rb require "test_helper" class PostsTest < ActionDispatch::IntegrationTest test "visiting the 'posts' page" do visit '/posts' assert_selector 'h1', text: 'Posts' end end 5.0

Slide 54

Slide 54 text

Capybara integration with Rails (AKA System Tests) # test/system/posts_test.rb require "application_system_test_case" class PostsTest < ApplicationSystemTestCase test "visiting the 'posts' page" do visit posts_url assert_selector 'h1', text: 'Posts' end end $ rails test:system # => 1 runs, 1 assertions, 0 failures 5.1

Slide 55

Slide 55 text

PR 23138 by Andrew White Custom URL helpers and polymorphic mapping Action Pack

Slide 56

Slide 56 text

Custom URL helpers and polymorphic mapping

Slide 57

Slide 57 text

Custom URL helpers and polymorphic mapping Comment #2 <%= link_to "Comment #2", post_path(@comment.post, anchor: 'content-2') %> # config/routes.rb direct(:embedded_content) do |content| url_for [content.post, anchor: "content-#{content.id}"] end <%= link_to "Comment #2", embedded_content_path(@comment) %> 5.0 5.1

Slide 58

Slide 58 text

Custom URL helpers and polymorphic mapping …

Slide 59

Slide 59 text

Custom URL helpers and polymorphic mapping <%= form_for @profile do |f| %> … <% end %> 5.0

Slide 60

Slide 60 text

Custom URL helpers and polymorphic mapping # config/routes.rb resource :profile # app/assets/views/profiles/new.html.erb <%= form_for @profile, url: profile_path(@profile) do |f| %> # config/routes.rb resource :profile resolve('Profile') { [:profile] } # app/assets/views/profiles/new.html.erb <%= form_for @profile do |f| %> 5.0 5.1

Slide 61

Slide 61 text

Rails 5.1: awesome features & breaking changes speakerdeck.com/claudiob Thanks!

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

One more thing… Extra time? Extra slides!

Slide 64

Slide 64 text

PR 26988 by Pavel Evstigneev Add ActiveRecord::Base. connection_pool.stat Active Record

Slide 65

Slide 65 text

Add ActiveRecord::Base. connection_pool.stat p ActiveRecord::Base.connection_pool.stat { size: 25, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 } 5.1

Slide 66

Slide 66 text

PR 28242 by Andrew White DateTime#change supports :usec and :nsec options Active Support

Slide 67

Slide 67 text

DateTime#change supports :usec and :nsec options now = DateTime.now now.to_f # => 1490028163.625132 now.change(usec: 0).to_f # => 1490028163.625132 now = DateTime.now now.to_f # => 1490028163.625132 now.change(usec: 0).to_f # => 1490028163.0 DateTime.now.end_of_day.nsec # => 999_999_999 5.0 5.1

Slide 68

Slide 68 text

PR 27549 by Maksym Pugach Add option :check_parameters to current_page? Action View

Slide 69

Slide 69 text

Add :check_parameters option to current_page? # From http://www.example.com/shop/checkout?order=desc&page=1 current_page? 'http://www.example.com/shop/checkout' # => true # From http://www.example.com/shop/checkout?order=desc&page=1 current_page? 'http://www.example.com/shop/checkout', check_parameters: true # => false 5.0 5.1

Slide 70

Slide 70 text

Active Job

Slide 71

Slide 71 text

PR 25991 by David Heinemeier Hansson Add retry_on/ discard_on for better exception handling Active Job

Slide 72

Slide 72 text

Add retry_on/discard_on for exception handling class SiteScraperJob < ActiveJob::Base rescue_from(ApocalypticException) rescue_from(ErrorLoadingSite) do retry_job queue: :low_priority end def perform(*args) # raise ErrorLoadingSite if cannot scrape end end 5.0

Slide 73

Slide 73 text

Add retry_on/discard_on for exception handling class SiteScraperJob < ActiveJob::Base discard_on ApocalypticException retry_on ErrorLoadingSite # retry_on ErrorLoadingSite, wait: :exponentially_longer # retry_on ErrorLoadingSite, wait: ->(attempts) { attempts * 2 } def perform(*args) # raise ErrorLoadingSite if cannot scrape end end 5.1

Slide 74

Slide 74 text

Action Mailer

Slide 75

Slide 75 text

Add parameterized invocation of mailers PR 27825 by David Heinemeier Hansson Action Mailer

Slide 76

Slide 76 text

Add parameterized invocation of mailers class PartyMailer < ApplicationMailer def birthday_invite(guest) mail subject: "Come to my birthday!", to: guest.email_address end def wedding_invite(guest) mail subject: "Come to my wedding!", to: guest.email_address end end PartyMailer.birthday_invite(current_user) 5.0

Slide 77

Slide 77 text

Add parameterized invocation of mailers class PartyMailer < ApplicationMailer before_action { @guest = params[:guest] } default to: -> { @guest.email_address } def birthday_invite mail subject: "Come to my birthday!" end def wedding_invite; mail subject: "Come to my wedding!"; end end PartyMailer.with(guest: current_user).birthday_invite 5.1

Slide 78

Slide 78 text

Rails 5.1: awesome features & breaking changes Thanks!