+14

[Rails] Tôi đã upgrade Rails 5 lên Rails 6 như thế nào?

Mayfest2023

Hi anh em, dạo gần đây mình được giao task về việc upgrade source Rails từ 5.1.6 lên Rails 6.1.7 và các Gem lên version mới nhất. Cơ bản đây cũng là lần đầu tôi làm việc này nên cũng trầy da tróc vẩy dữ dằn lắm. Thì nhân tiện đây mình cũng xin chia sẽ lại với mọi người cách mà mình đã thực hiện nó. Let's gooo

image.png

1. Thực hiện install Rails 6 và cập nhật Gem

Đầu tiên, phải đảm bảo Ruby version của bạn từ 2.5 trở lên (source của mình đã > 2.5 nên mình không cần xử lý bước này). Tiếp đó, mình sẽ thực hiện việc thay đổi version của Rails ở bên trong Gemfile. Ở bước này, thông thường mọi người sẽ thực hiện câu lệnh bundle update rails, nhưng nếu chúng ta làm theo cách này, thì sẽ có khá nhiều lỗi conflict giữa các Gem, và sẽ tốn kha khá thời gian để xử lý chúng, ví dụ như:

Bundler could not find compatible versions for gem "activesupport":
  In Gemfile:
    delayed_job_active_record was resolved to 4.1.3, which depends on
      delayed_job (>= 3.0, < 5) was resolved to 4.1.7, which depends on
        activesupport (>= 3.0, < 5.3)

    jbuilder (~> 2.5) was resolved to 2.9.1, which depends on
      activesupport (>= 4.2.0)

    kaminari (~> 1.1) was resolved to 1.1.1, which depends on
      activesupport (>= 4.1.0)

    rspec-rails (~> 3.5) was resolved to 3.8.2, which depends on
      activesupport (>= 3.0)

Ban đầu mình cũng làm như vậy, và ngồi fix mấy cái đống lỗi này rất nản luôn, nhưng may mắn là mình được 1 người anh, cũng là sếp của mình chỉ cho 1 cách nhẹ nhàng mà an toàn hơn, đó là:

  1. Tiến hành loại bỏ hết những version của các gem được chỉ định ở trong Gemfile, chỉ chừa lại gem 'rails', '~> 6.1.7'
  2. Xóa Gemfile.lock
  3. Tiến hành chạy bundle install

Chờ nó chạy xong là bạn đã hoàn thành được bước đầu tiên là upgrade version Rails và các Gem trong source 😃

2. Chạy lệnh rails app:update

Tiếp theo, mình sẽ tiến hành chạy lệnh rails app:update để rails thêm vào những file config mới cũng như bổ sung cho những file cũ. Ở bước này, đối với những file đã có từ trước, bạn cần kiểm tra các sự thay đổi để lựa chọn cho phù hợp.

Một số thay đổi nổi bật ở Rails 6 như:

  • Thêm tính năng active_storage bao gồm file storage.yml và 2 migration để thêm 2 table liên quan
  • Thêm file manifest.js để quản lý tài nguyên css, js, image
  • Bổ sung webpack là trình biên dịch js mặc định
  • ngoài ra còn một số thay đổi, bạn có thể tham khảo thêm ở đây

3. Xử lý code lỗi thời

Sau khi đã hoàn tất các bước config, thì bây giờ đến công đoạn căng nhất đó là fix lỗi. Dưới đây là một số lỗi mà mình đã gặp phải:

  1. FactoryBot thay đổi syntax khai báo, ở version cũ, khi bạn khai báo mặc định cho 1 model trong factory sẽ có dạng như:
FactoryBot.define do
  factory :user do
    name 'MyString'
    code 'MyString'
  end
end

Thì bây giờ cần thay đổi lại theo dạng

FactoryBot.define do
  factory :user do
    name { 'MyString' }
    code { 'MyString' }
  end
end
  1. update_attributes bị loại bỏ, thay vào đó ta có thể dùng update
  2. helper image_alt bị loại bỏ. Sau đó mình đã xử lý bằng cách bê nguyên cái define method từ Rails 5 (link đây) bỏ vào source 😃
  def image_alt(src)
    File.basename(src, ".*".freeze).sub(/-[[:xdigit:]]{32,64}\z/, "".freeze).tr("-_".freeze, " ".freeze).capitalize
  end
  1. template của Cell bị hiển thị plain text thay vì là html. Cách xử lý của mình là thêm dòng này vào các file Cell bị lỗi
include ActionView::Context
  1. Lỗi query order by có chứa điều kiện không nằm trong select => Bọc điều kiện lại bằng Arel.sql, ví dụ như:
User.active.order([Arel.sql('FIELD(users.id, ?)'), user_ids])
  1. fixture_file_upload bị lỗi đối với fixture_file_path không còn hoạt động, ví dụ như đoạn code bên dưới sẽ bị lỗi:
fixture_file_upload Rails.root.join('spec', 'fixtures', 'files', 'image', 'sample_image.jpg')

Thay vào đó, chúng ta có thể sử dụng:

Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'files', 'image', 'sample_image.jpg'))
  1. where.not thay đổi cơ chế từ NOR sang NAND. Ví dụ:

Ban đầu

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'

Thì bây giờ sẽ là

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
  1. belongs_to thay đổi behaviour từ default là optional sang require

Ngoài ra còn nhiều lỗi lặt vặt khác nữa mà mình không liệt kê hết được.

Kết Luận

Việc upgrade version đòi hỏi cần dành nhiều thời gian để rà soát lại hệ thống, phát hiện những chỗ có lỗi, nên chúng ta cần vạch ra 1 lộ trình cụ thể cho từng bước, cũng như tạo 1 bộ test case rõ ràng, 1 bộ unit test đầy đủ để tránh bị sót bug. Hi vọng những chia sẻ trên đây của mình sẽ giúp ích 1 phần nào đó cho các bạn, cảm ơn mọi người đã đọc, nếu thấy bổ ích thì hãy upvote + comment cho mình với nhé ❤️

Tài liệu tham khảo

https://edgeguides.rubyonrails.org/6_1_release_notes.html


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí