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

What's new in Rails 6?

What's new in Rails 6?

RubyConf Taiwan 2019: What's new in Rails 6?

vipulnsward

July 27, 2019
Tweet

More Decks by vipulnsward

Other Decks in Technology

Transcript

  1. 9

  2. 11

  3. 13

  4. 17

  5. class Mailman < ActionMailer::Base def receive(email) page = Page.find_by(address: email.to.first)

    page.emails.create( subject: email.subject, body: email.body ) if email.has_attachments? email.attachments.each do |attachment| page.attachments.create({file: attachment,
 description:email.subject}) end end end end $ rails runner 'Mailman.receive(STDIN.read)' 18
  6. # Generate new mailbox rails generate mailbox forwards # frozen_string_literal:

    true class ForwardsMailbox < ApplicationMailbox def process if forwarder record_forward else bounce_with Forwards::BounceMailer.missing_forward( inbound_email, forwarder: forwarder ) end end private def forwarder Person.where(email_address: mail.from) end def record_forward Forward.new forwarder: forwarder, subject: message.subject, content: mail.content end end 22
  7. class ForwardsMailboxTest < ActionMailbox::TestCase test "directly recording a client forward

    for a forwarder and forwardee corresponding to one project" do assert_difference -> { people(:david).buckets.first.recordings.count } do receive_inbound_email_from_mail \ to: '[email protected]', from: people(:david).email_address, subject: "Fwd: Status update?", body: <<~BODY --- Begin forwarded message --- From: Frank Holland <[email protected]> What's the status? BODY end recording = people(:david).buckets.first.recordings.last assert_equal people(:david), recording.creator assert_equal "Status update?", recording.forward.subject assert_match "What's the status?", recording.forward.content.to_s end end 24
  8. $ bin/rails zeitwerk:check Hold on, I am eager loading the

    application. All is good! -classic mode infers file names from missing constant names (underscore): 
 "FOO".underscore is "foo" -zeitwerk mode infers constant names from file names (camelize): 
 "foo".camelize is "Foo", not "FOO"
  9. # Autoloading in this class' body matches Ruby semantics now.

    class Admin::UsersController < ApplicationController # ... end # Before class Foo::Bar Baz end 
 # Now class Foo::Bar Foo::Baz end 30
  10. rails action_text:install # app/models/message.rb class Message < ApplicationRecord has_rich_text :content

    end <%# app/views/messages/_form.html.erb %> <%= form_with(model: message) do |form| %> <div class="field"> <%= form.label :content %> <%= form.rich_text_area :content %> </div> <% end %> 38
  11. # Display <%= @message.content %> class MessagesController < ApplicationController def

    create message = Message.create
 params.require(:message).permit(:title, :content) redirect_to message end end 39
  12. - Webpacker is the new default JavaScript compiler - Guard

    against DNS rebinding attacks by default - bin/rails db:system:change --to=postgresql - Secrets => Credentials and multi-env credentials 42
  13. ActionDispatch::HostAuthorization Rails.application.config.hosts = [ IPAddr.new("0.0.0.0/0"), # All IPv4 addresses. IPAddr.new("::/0"),

    # All IPv6 addresses. "localhost" # The localhost reserved domain. ] # Allow requests only from `product.com` Rails.application.config.hosts << "product.com" # Allow requests from subdomains like `www.product.com` and # `beta1.product.com`. Rails.application.config.hosts << /.*\.product\.com/ 44
  14. $ rails routes --expanded --[ Route 1 ]------------------------------------------------------------ Prefix |

    high_scores Verb | GET URI | /high_scores(.:format) Controller#Action | high_scores#index --[ Route 2 ]------------------------------------------------------------ Prefix | new_high_score Verb | GET URI | /high_scores/new(.:format) Controller#Action | high_scores#new --[ Route 3 ]------------------------------------------------------------ Prefix | blog Verb | URI | /blog Controller#Action | Blog::Engine [ Routes for Blog::Engine ] --[ Route 1 ]------------------------------------------------------------ Prefix | cart Verb | GET URI | /cart(.:format) Controller#Action | cart#show 45
  15. => cookies.signed[:lastname] = {value: "Smith", expires: Time.now + 1.hour }

    47 - Purpose and expiry metadata is now embedded inside 
 signed and encrypted cookies for increased security
  16. class SomeControllerTest < ActionController::TestCase def test_some_action post :action, body: {

    foo: 'bar' } assert_equal({ "foo" => "bar" }, response.parsed_body) end end 48
  17. - image_alt has been removed - Rails npm packages moved

    into a @rails scope.
 @rails/webpacker
 @rails/rails-ujs 51
  18. - mini_magick replaced by image_processing gem - Wrapper for mini_magick

    and vips gem(libvips) - New variant formats: BMP, TIFF, progressive JPEG 53
  19. class User < ApplicationRecord has_many_attached :highlights end user.highlights.attach(filename: "funky.jpg", ...)

    user.higlights.count # => 1 blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", user.update!(highlights: [ blob ]) user.highlights.count # => 2 user.highlights.first.filename # => "funky.jpg" user.highlights.second.filename # => "town.jpg" 54
  20. user.highlights.attach(filename: "funky.jpg", ...) user.highlights.count # => 1 blob = ActiveStorage::Blob.create_after_upload!(filename:

    "town.jpg", user.update!(highlights: [ blob ]) user.highlights.count # => 1 user.highlights.first.filename # => "town.jpg" 55
  21. class ActiveSupport::TestCase parallelize_setup do |worker| # setup databases end parallelize_teardown

    do |worker| # cleanup databases end parallelize(workers: :number_of_processors, with: :threads) end 60
  22. - Lots of improvements due to removal of AS::Multibyte::Unicode table

    - Remove ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase - Removes ActiveSupport::Multibyte::Chars wrappers around String methods: - #downcase, #upcase, #swapcase, and #capitalize.
 - Reduce memory usage(about 1M) - Reduce repo/gem size(about 1M) 63
  23. - before? and after? methods to Date, DateTime, Time, and

    TimeWithZone
 Time.current.before?(2.weeks.ago) - Add ActiveSupport::ParameterFilter
 ActiveSupport::ParameterFilter.new(["credit_card.code"]) => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not change { file: { code: "xxxx"} } 64
  24. [ 1, 2, 3, 4, 5 ].excluding([4, 5]) => [

    1, 2, 3 ] [1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ] post.authors.including(Current.person) numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] odd_numbers = numbers.extract! { |number| number.odd? } 
 # => [1, 3, 5, 7, 9] numbers # => [0, 2, 4, 6, 8] 65
  25. 68

  26. - rails db:prepare to create a database if it doesn't

    exist, and run its migrations. - rails db:seed:replant that truncates tables of each database 
 for the current environment and loads the seeds 70
  27. -touch_all -destroy_by / delete_by -reselect
 => unscope(:select).select(fields)
 -insert_all/insert_all!/upsert_all
 => Post.insert_all([row1,

    row2]
 -account.memberships.extract_associated(:user) => account.memberships.preload(:user).collect(&:user)
 -after_save_commit 
 => after_commit :hook, on: [ :create, :update ]
 -Person.where(id: 1).pick(:name) 
 => Person.limit(1).pluck(column_name).first 71 AR RELATION
  28. - Relation#annotate for SQL comments
 
 Post.annotate("this is a comment").where(id:

    123).to_sql
 => # SELECT "posts".* FROM "posts" WHERE "posts"."id" = 123 /* this is a comment */ - Add support for setting Optimizer Hints on databases
 class Job < ApplicationRecord default_scope { optimizer_hints("MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(jobs)") } end 72
  29. -Lots of changes to support Multi-DB -Add the ability to

    automatically switch database connections. -while_preventing_writes => prevent writes to DB in block -ActiveRecord::Base.find_or_create_by/! => ActiveRecord::Base.create_or_find_by/! / SELECT/INSERT race condition 73
  30. 74

  31. # app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user

    def connect self.current_user = find_verified_user end private def find_verified_user ... end end end 77
  32. class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase test "connects with params" do #

    Simulate a connection opening by calling the `connect` method connect params: { user_id: 42 } # You can access the Connection object via `connection` in tests assert_equal connection.user_id, "42" end test "rejects connection without params" do # Use `assert_reject_connection` matcher to verify that # connection is rejected assert_reject_connection { connect } end end 78
  33. # app/channels/application_cable/channel.rb module ApplicationCable class Channel < ActionCable::Channel::Base end end

    # app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel end 79
  34. require "test_helper" class ChatChannelTest < ActionCable::Channel::TestCase test "subscribes and stream

    for room" do # Simulate a subscription creation by calling `subscribe` subscribe room: "15" # You can access the Channel object via `subscription` in tests assert subscription.confirmed? assert_has_stream "chat_15" end end 80
  35. # app/jobs/chat_relay_job.rb class ChatRelayJob < ApplicationJob def perform_later(room, message) ChatChannel.broadcast_to

    room, text: message end end # test/jobs/chat_relay_job_test.rb require 'test_helper' class ChatRelayJobTest < ActiveJob::TestCase include ActionCable::TestHelper test "broadcast message to room" do room = rooms(:all) assert_broadcast_on(ChatChannel.broadcasting_for(room), text: "Hi!") do ChatRelayJob.perform_now(room, "Hi!") end end end 81