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

Living on the Rails Edge

Living on the Rails Edge

In this talk we would take a look in why keeping your application up-to-date is important and different strategies to upgrade Rails application to the newest version taking as example a huge monolithic Rails application. We will learn what were the biggest challenges and how they could be avoided.

Rafael França

June 29, 2018
Tweet

More Decks by Rafael França

Other Decks in Technology

Transcript

  1. 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013

    2014 2015 2016 2017 2018 1.0 2.0 2.1 2.3 3.0 3.1 3.2 4.0 4.1 4.2 5.0 5.1 2.2 Initial commit Beginning of history 1.1 1.2 2.0 2.1 2.2 2.3 3.0 3.2 4.0 4.1 4.2 5.0 Rails Shopify 5.1 5.2 5.2
  2. class MyJob < ActiveJob::Base self.queue_adapter = :sidekiq queue_as :realtime def

    perform(name) # heavy lifting end end MyJob.perform_later("rafael")
  3. &'(

  4. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  5. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  6. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  7. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  8. class SendMailJob < BackgroundQueue::Job def perform(params) mailer_class = params[:mailer_class] mail_message

    = Marshal.load(Base64.decode64(params[:message])) method = params[:method] message = BackgroundMailing::MailerProxy.new(mailer_class: mailer_class.constantize, message: mail_message, method: method) message.deliver_now end end
  9. class SendMailJob < BackgroundQueue::Job def perform(params) mailer_class = params[:mailer_class] mail_message

    = params[:message] method = params[:method] message = BackgroundMailing::MailerProxy.new(mailer_class: mailer_class.constantize, message: mail_message, method: method) message.deliver_now end end
  10. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  11. module BackgroundMailing class MailerProxy < Struct.new(:mailer_class, :message, :method) delegate_missing_to :message

    def initialize(mailer_class:, message:, method:) self.mailer_class = mailer_class self.message = message self.method = method end def deliver(queue: nil) marshal_encoded_message = Base64.encode64(Marshal.dump(message)) params = { queue: queue, mailer_class: mailer_class.to_s, message: marshal_encoded_message, method: method } BackgroundQueue.push(SendMailJob, params) message end def deliver_now mailer_class.wrap_delivery_behavior(message) message.deliver message end end end
  12. class Foo def initialize(value) @value = value end def value

    @value end end foo = Foo.new(1) # => #<Foo:0x00007ff59304f138 @value=1>o encoded = Marshal.dump(foo) # => "\x04\bo:\bFoo\x06:\v@valuei\x06" new_object = Marshal.load(encoded) # => #<Foo:0x00007ff59385cbf0 @value=1> new_object.value # => 1
  13. class Foo def initialize(value) @value = value end def value

    @value end end foo = Foo.new(1) # => #<Foo:0x00007ff59304f138 @value=1> encoded = Marshal.dump(foo) # => "\x04\bo:\bFoo\x06:\v@valuei\x06" new_object = Marshal.load(encoded) # => #<Foo:0x00007ff59385cbf0 @value=1> new_object.value # => 1
  14. class Foo def initialize(value) @value = value end def value

    @value end end foo = Foo.new(1) # => #<Foo:0x00007ff59304f138 @value=1> encoded = Marshal.dump(foo) # => "\x04\bo:\bFoo\x06:\v@valuei\x06" new_object = Marshal.load(encoded) # => #<Foo:0x00007ff59385cbf0 @value=1> new_object.value # => 1
  15. class Foo def initialize(value) @value = value end def value

    @value end end foo = Foo.new(1) # => #<Foo:0x00007ff59304f138 @value=1> encoded = Marshal.dump(foo) # => "\x04\bo:\bFoo\x06:\v@valuei\x06" new_object = Marshal.load(encoded) # => #<Foo:0x00007ff59385cbf0 @value=1> new_object.value # => 1
  16. Mail::VERSION.version # => "2.5.4" mail = Mail.new do to '[email protected]'

    body 'Some simple body' end # => #<Mail::Message:70352211207020> message = Marshal.dump(mail) Mail::VERSION.version # => "2.6.0" mail = Marshal.load(message) # => ArgumentError (dump format error (user class))
  17. Mail::VERSION.version # => "2.5.4" mail = Mail.new do to '[email protected]'

    body 'Some simple body' end # => #<Mail::Message:70352211207020> message = Marshal.dump(mail) Mail::VERSION.version # => "2.6.0" mail = Marshal.load(message) # => ArgumentError (dump format error (user class))
  18. module ActionMailer class MessageDelivery def deliver_later ActionMailer::DeliveryJob.perfor_later( @mailer_class.name, @action.to_s, “deliver_now",

    *@args ) end def deliver_now processed_mailer.handle_exceptions do message.deliver end end end end
  19. module ActionMailer class MessageDelivery def deliver_later ActionMailer::DeliveryJob.perfor_later( @mailer_class.name, @action.to_s, “deliver_now",

    *@args ) end def deliver_now processed_mailer.handle_exceptions do message.deliver end end end end
  20. module ActionMailer class MessageDelivery def deliver_later ActionMailer::DeliveryJob.perfor_later( @mailer_class.name, @action.to_s, “deliver_now",

    *@args ) end def deliver_now processed_mailer.handle_exceptions do message.deliver end end end end
  21. Long-running branch strategy 1. Create a branch 2. Make all

    changes necessary 3. Merge it 4. ?????? 5. Profit!
  22. if ENV['RAILS_NEXT'] # monkey patching to support dual booting module

    Bundler::SharedHelpers def default_lockfile=(path) @default_lockfile = path end def default_lockfile @default_lockfile ||= Pathname.new("#{default_gemfile}.lock") end end Bundler::SharedHelpers.default_lockfile = Pathname.new("#{Bundler::SharedHelpers.default_gemfile}_next.lock") # Bundler::Dsl.evaluate already called with an incorrect lockfile ... fix it class Bundler::Dsl # A bit messy, this can be called multiple times by bundler, avoid blowing the stack unless self.method_defined? :to_definition_unpatched alias_method :to_definition_unpatched, :to_definition end def to_definition(bad_lockfile, unlock) to_definition_unpatched(Bundler::SharedHelpers.default_lockfile, unlock) end end end if ENV['RAILS_NEXT'] gem 'rails', github: 'rails/rails', branch: '5-0-stable' else gem 'rails', '~> 4.2.7' end
  23. Gemfile.shared gem 'sqlite3' gem 'puma', '~> 3.7' gem 'sass-rails', '~>

    5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.2' gem 'turbolinks', '~> 5' gem 'jbuilder', '~> 2.5'
  24. config/boot.rb if ENV['RAILS_NEXT'] ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile.next', __dir__) else ENV['BUNDLE_GEMFILE'] ||=

    File.expand_path('../Gemfile', __dir__) end require 'bundler/setup' # Set up gems listed in the Gemfile.
  25. class MyTest < MiniTest::Test def test_foo ActiveSupport::Deprecation.warn('This code is deprecated')

    ActiveSupport::Deprecation.warn('Another deprecation') end def test_bar ActiveSupport::Deprecation.warn('This code is deprecated') end end
  26. --- test_foo: - DEPRECATION WARNING: This code is deprecated -

    DEPRECATION WARNING: Another deprecation test_bar: - DEPRECATION WARNING: This code is deprecated