Rails 6 đã được release và nó được tích hợp nhiều tính năng rất có lợi cho các ứng dụng kể cả lớn lẫn nhỏ, có vẻ như rất nhiều cải thiện về tốc độ cũng như khả năng mở rộng đã được mang đến trong Rails 6.
Mình đã đọc qua CHANGELOGs từng phần của Rails 6 (ActiveRecord, ActionPack, ActiveSupport, etc.) và chọn ra một vài tính năng mà mình cảm thấy thú vị nhất.
Active Record
Thêm API cơ bản cho việc chuyển đổi kết nối nhằm mục đích hỗ trợ đa cơ sở dữ liệu.
Eileen M. Uchitelle đã trích xuất từ codebase của Github và đưa tính năng tuyệt vời này vào Rails, một giải pháp nhằm chuyển đổi kết nối cơ sở dữ liệu cho một model cụ thể hoặc một query cụ thể.
Code của tính năng này là một phần trong pull request sau: https://github.com/rails/rails/pull/34052, nó được viết doc khá là đầy đủ và dễ đọc.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">class</span> <span class="token class-name">AnimalsModel</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> <span class="token keyword">self</span><span class="token punctuation">.</span>abstract_class <span class="token operator">=</span> <span class="token boolean">true</span> connects_to database<span class="token punctuation">:</span> <span class="token punctuation">{</span> writing<span class="token punctuation">:</span> <span class="token symbol">:animals_primary</span><span class="token punctuation">,</span> reading<span class="token punctuation">:</span> <span class="token symbol">:animals_replica</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">class</span> <span class="token class-name">Dog</span> <span class="token operator"><</span> <span class="token constant">AnimalsModel</span> <span class="token comment"># connected to both the animals_primary db for writing and the animals_replica for reading</span> <span class="token keyword">end</span> |
Các truy vấn bị chậm trong job có thể được đọc từ một bản sao của cơ sở dữ liệu bằng một API đơn giản
1 2 3 4 | <span class="token constant">ActiveRecord</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span><span class="token punctuation">.</span>connected_to<span class="token punctuation">(</span>database<span class="token punctuation">:</span> <span class="token symbol">:slow_replica</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token constant">SlowReplicaModel</span><span class="token punctuation">.</span>first <span class="token keyword">end</span> |
Còn file database.yml
lúc này sẽ cần dài hơn một chút
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | development<span class="token punctuation">:</span> primary<span class="token punctuation">:</span> database<span class="token punctuation">:</span> my_primary_db user<span class="token punctuation">:</span> root primary_replica<span class="token punctuation">:</span> database<span class="token punctuation">:</span> my_primary_db user<span class="token punctuation">:</span> ro_user replica<span class="token punctuation">:</span> <span class="token boolean">true</span> animals<span class="token punctuation">:</span> database<span class="token punctuation">:</span> my_animals_db user<span class="token punctuation">:</span> root animals_replica database<span class="token punctuation">:</span> my_animals_db user<span class="token punctuation">:</span> ro_user replica<span class="token punctuation">:</span> <span class="token boolean">true</span> |
Gannon McGibbon sau đó đã thêm vào một chút code nhằm hỗ trợ hash và config url trong database hash của ActiveRecord::Base.connected_to
và giúp chúng ta có khả năng viết được đoạn code như sau
1 2 3 4 5 6 7 8 9 | <span class="token constant">User</span><span class="token punctuation">.</span>connected_to<span class="token punctuation">(</span>database<span class="token punctuation">:</span> <span class="token punctuation">{</span> writing<span class="token punctuation">:</span> <span class="token string">"postgres://foo"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token constant">User</span><span class="token punctuation">.</span>create<span class="token operator">!</span><span class="token punctuation">(</span>name<span class="token punctuation">:</span> <span class="token string">"Gannon"</span><span class="token punctuation">)</span> <span class="token keyword">end</span> config <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token string">"adapter"</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"sqlite3"</span><span class="token punctuation">,</span> <span class="token string">"database"</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"db/readonly.sqlite3"</span> <span class="token punctuation">}</span> <span class="token constant">User</span><span class="token punctuation">.</span>connected_to<span class="token punctuation">(</span>database<span class="token punctuation">:</span> <span class="token punctuation">{</span> reading<span class="token punctuation">:</span> config <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token constant">User</span><span class="token punctuation">.</span>count <span class="token keyword">end</span> |
Thêm Relation#pick
, pluck dành cho các giá trị đơn
Như tiêu đề, dùng pick sẽ tượng tự pluck nhưng dùng cho các giá trị đơn
1 2 3 4 | <span class="token constant">Person</span><span class="token punctuation">.</span>where<span class="token punctuation">(</span>id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span>pick<span class="token punctuation">(</span><span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:email_address</span><span class="token punctuation">)</span> <span class="token comment"># SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1</span> <span class="token comment"># => ['Tung K', '<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>']</span> |
Có thể config được các cột được sắp xếp ngầm
Đối với các ứng dụng không sử dụng ID tăng dần, tính năng này thật là quý giá. Bây giờ chúng ta sẽ có thể thiết lập thứ tự của các bản ghi chỉ với một dòng lệnh.
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token operator"><</span> <span class="token constant">ActiveRecord</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span> <span class="token keyword">self</span><span class="token punctuation">.</span>implicit_order_column <span class="token operator">=</span> <span class="token string">"created_at"</span> <span class="token keyword">end</span> |
Action Mailbox
Action mailbox đã được mang đến trong phiên bản thứ sáu của Ruby on Rails, mình chắc rằng sẽ có nhiều bài viết về tính năng này trong tương lai. Action Mailbox cung cấp một bộ công cụ cho phép các ứng dụng có thể tích hợp một cách tốt hơn các luồng emails đến.
Config cơ bản có trong Action Mailbox Basics Rails Guide, có một vài ý tưởng thú vị về tính năng này đó là các cuộc hội thoại sẽ có thể diễn ra tự động trong cả platform và email và có thể hoán đổi cho nhau, ví dụ như comment trên Github từ email, email trợ giúp biến thành ticket…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token constant">COMMENTS_REGEX</span> <span class="token operator">=</span> <span class="token regex">/^comment+(.+)@example.com/i</span> <span class="token comment"># app/mailboxes/application_mailbox.rb</span> <span class="token keyword">class</span> <span class="token class-name">ApplicationMailbox</span> <span class="token operator"><</span> <span class="token constant">ActionMailbox</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span> routing <span class="token constant">COMMENTS_REGEX</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token symbol">:comments</span> <span class="token keyword">end</span> <span class="token comment"># app/mailboxes/comments_mailbox.rb</span> <span class="token keyword">class</span> <span class="token class-name">CommentsMailbox</span> <span class="token operator"><</span> <span class="token constant">ApplicationMailbox</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">process</span></span> user <span class="token operator">=</span> <span class="token constant">User</span><span class="token punctuation">.</span>find_by<span class="token punctuation">(</span>email<span class="token punctuation">:</span> mail<span class="token punctuation">.</span>from<span class="token punctuation">)</span> post_uuid <span class="token operator">=</span> <span class="token constant">COMMENTS_REGEX</span><span class="token punctuation">.</span>match<span class="token punctuation">(</span>mail<span class="token punctuation">.</span>to<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> post <span class="token operator">=</span> <span class="token constant">Post</span><span class="token punctuation">.</span>find_by<span class="token punctuation">(</span>uuid<span class="token punctuation">:</span> post_uuid<span class="token punctuation">)</span> post<span class="token punctuation">.</span>comments<span class="token punctuation">.</span>create<span class="token punctuation">(</span>user<span class="token punctuation">:</span> user<span class="token punctuation">,</span> content<span class="token punctuation">:</span> mail<span class="token punctuation">.</span>body<span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Chú ý rằng Action Mailbox cần có Active Job và Active Storage và cần một bảng trong database cho nó. Hầu hết tài liệu về các class cũng có sẵn tại ActionMailbox::Base class documentation, ActionMailbox::InboundEmail class documentation và làm việc với email được parse được thực hiện bằng cách sử dụng gem Mail.
Action Text
Action Text chính là Trix editor của Basecamp được đưa vào trong các model của ActiveRecord. Nó cho chúng ta method has_rich_text
và chúng ta có thể apply nó vào model mà mình muốn và Action Text sẽ xử lý các việc còn lại. Action Text require ActiveStorage và một số bảng để lưu metadata của nó nên hãy lưu ý rằng bạn sẽ cần phải chuyển sang dùng một ActiveStorage provider mà không lưu trên ổ cứng nếu như ứng dụng của bạn cần có hơn một server.
Tài liệu về tính năng này có trên Rails Guides và nó khá là dễ hiểu.
Action Pack
ActionDispatch::HostAuthorization
Host Authorization là một middleware mới giúp chống lại các cuộc tấn công rebinding DNS (là kiểu tấn công bằng cách tạo các đường link dẫn tới các trang web có chứa mã JS độc hại) bằng cách chỉ ra rõ ràng các hosts có thể gửi request đến.
Theo mặc định, nó được đặt cho tất cả các ứng dụng Rails 6 và cho phép request tới các host sau ở môi trường development IPAddr.new (“0.0.0.0/0”), IPAddr.new (“:: / 0”), “localhost”]
, config của nó có thể nhận các mảng RegExp
, Proc
, IPAddr
và String
. Điều này có nghĩa là với Rails 6, chúng ta sẽ cần đặt tên miền của mình một cách rõ ràng trong các file cấu hình môi trường.
1 2 3 4 5 6 7 8 9 | <span class="token constant">Rails</span><span class="token punctuation">.</span>application<span class="token punctuation">.</span>config<span class="token punctuation">.</span>hosts <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token constant">IPAddr</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string">"0.0.0.0/0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment"># All IPv4 addresses.</span> <span class="token constant">IPAddr</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string">"::/0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment"># All IPv6 addresses.</span> <span class="token string">"localhost"</span> <span class="token comment"># The localhost reserved dom</span> <span class="token constant">Rails</span><span class="token punctuation">.</span>application<span class="token punctuation">.</span>config<span class="token punctuation">.</span>hosts <span class="token operator"><</span><span class="token operator"><</span> <span class="token string">"product.com"</span> <span class="token constant">Rails</span><span class="token punctuation">.</span>application<span class="token punctuation">.</span>config<span class="token punctuation">.</span>hosts <span class="token operator"><</span><span class="token operator"><</span> <span class="token regex">/.*.product.com/</span> |
metadata purpose
dành cho cookie được sign/encrypt
Rails giờ đây đã có thể ngăn chặn các cuộc tấn công bằng cách sao chép các giá trị đã được sign/encrypt của một cookie này và sử dụng nó làm giá trị của một cookie khác.
Rails có thể làm được như vậy bằng cách lưu trữ tên cookie trong trường purpose
rồi sign/encrypt nó cùng với giá trị của cookie. Sau đó, khi được đọc từ phía server, tên cookie sẽ được verify bằng purpose và khi đó các cookie đã bị tấn công sẽ được loại bỏ.
Enable config action_dispatch.use_cookies_with_metadata
để sử dụng tính năng này.
Truyền thêm tham số vào các request GET ngầm thông qua #follow_redirect!
Giờ đây chúng ta đã có thể truyền thêm các tham số vào các request GET ngầm bằng cách thêm tham số cho method follow_redirect!
.
1 2 3 4 5 6 | <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span> <span class="token comment"># do stuff</span> <span class="token comment"># method that have redirect</span> follow_redirect<span class="token operator">!</span><span class="token punctuation">(</span>params<span class="token punctuation">:</span> <span class="token punctuation">{</span> user<span class="token punctuation">:</span> params<span class="token punctuation">[</span><span class="token symbol">:user_id</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">end</span> |
Action View
Thêm allocations vào trong việc phân tích đo đạc render template
Rails 6 sẽ report về việc phân bổ đối tượng được thực hiện trong các views, điều này sẽ cho phép developer biết được lượng thời gian dành cho việc phân bổ và thu thập các đối tượng trong bộ nhớ của các process.
1 2 3 4 | <span class="token constant">Rendered</span> posts<span class="token operator">/</span>_form<span class="token punctuation">.</span>html<span class="token punctuation">.</span>erb <span class="token punctuation">(</span><span class="token constant">Duration</span><span class="token punctuation">:</span> <span class="token number">7.1</span>ms <span class="token operator">|</span> <span class="token constant">Allocations</span><span class="token punctuation">:</span> <span class="token number">6004</span><span class="token punctuation">)</span> <span class="token constant">Rendered</span> posts<span class="token operator">/</span><span class="token keyword">new</span><span class="token punctuation">.</span>html<span class="token punctuation">.</span>erb within layouts<span class="token operator">/</span>application <span class="token punctuation">(</span><span class="token constant">Duration</span><span class="token punctuation">:</span> <span class="token number">8.3</span>ms <span class="token operator">|</span> <span class="token constant">Allocations</span><span class="token punctuation">:</span> <span class="token number">6654</span><span class="token punctuation">)</span> <span class="token constant">Completed</span> <span class="token number">200</span> <span class="token constant">OK</span> <span class="token keyword">in</span> <span class="token number">858</span>ms <span class="token punctuation">(</span><span class="token constant">Views</span><span class="token punctuation">:</span> <span class="token number">848.4</span>ms <span class="token operator">|</span> <span class="token constant">ActiveRecord</span><span class="token punctuation">:</span> <span class="token number">0.4</span>ms <span class="token operator">|</span> <span class="token constant">Allocations</span><span class="token punctuation">:</span> <span class="token number">1539564</span><span class="token punctuation">)</span> |
Active Job
Thêm các instrumentation hooks enqueue_retry.active_job
, retry_stopped.active_job
, và discard.active_job
Rails có một lượng instrumentation hooks rất phong phú được tích hợp vào Active Support và nó dàn trải trên toàn bộ framework.
Nó cho phép bạn lắng nghe các sự kiện xảy ra trong suốt vòng đời của các request, các truy vấn SQL và các Job.
Với việc bổ sung thêm enqueue_retry.active_job
, retry_stopped.active_job
và discard.active_job
giúp cho việc phân tích và handle Job dựa trên status của chúng càng trở nên dễ dàng hơn.
Cho phép truyền vào #retry_on
và #discard_on
nhiều exception
1 2 | retry_on <span class="token constant">Errno</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">ECONNREFUSED</span><span class="token punctuation">,</span> <span class="token constant">SocketError</span><span class="token punctuation">,</span> <span class="token constant">Timeout</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Error</span><span class="token punctuation">,</span> attempts<span class="token punctuation">:</span> <span class="token number">5</span> |
Active Model
Cho phép đổi tên thuộc tính khi dùng #has_secure_password
Trước đây, thuộc tính mặc định khi dùng #has_secure_password là ‘pasword’
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token operator"><</span> <span class="token constant">ActiveRecord</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span> has_secure_password <span class="token symbol">:recovery_password</span><span class="token punctuation">,</span> validations<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token keyword">end</span> user <span class="token operator">=</span> <span class="token constant">User</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> user<span class="token punctuation">.</span>recovery_password <span class="token operator">=</span> <span class="token string">"42password"</span> user<span class="token punctuation">.</span>recovery_password_digest <span class="token comment"># => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."</span> user<span class="token punctuation">.</span>authenticate_recovery_password<span class="token punctuation">(</span><span class="token string">'42password'</span><span class="token punctuation">)</span> <span class="token comment"># => user</span> |
Active Storage
File upload lên khi được assign vào một record sẽ được lưu vào storage khi record được save thay vì lưu vào storage ngay lập tức
Ở Rails 5.2, files sẽ được lưu ngay lập tức vào storage khi được assign
1 2 | <span class="token variable">@user</span><span class="token punctuation">.</span>avatar <span class="token operator">=</span> params<span class="token punctuation">[</span><span class="token symbol">:avatar</span><span class="token punctuation">]</span> |
Giờ đây cơ chế của nó đã chính xác hơn khi chỉ lưu khi @user.save
Dùng gem ImageProcessing cho Active Storage và các biến thể của nó, deprecate MiniMagick
ImageProcessing hỗ trợ một số macro tốt hơn như :resize_to_fit
, :resize_to_fill
và có tích hợp sẵn libvips thay thế cho ImageMagick.
Việc thay đổi cũng dễ dàng bằng cách cấu hình Rails như thông thường
1 2 | <span class="token constant">Rails</span><span class="token punctuation">.</span>application<span class="token punctuation">.</span>config<span class="token punctuation">.</span>active_storage<span class="token punctuation">.</span>variant_processor <span class="token operator">=</span> <span class="token symbol">:vips</span> |
Action Mailer
Thêm MailDeliveryJob
, deprecate DeliveryJob
và Parameterized::DeliveryJob
MailDeliveryJob
được sử dụng cho cả việc send mail thường và mail được tham số hóa
Zeitwerk
Zeitwerk là một code loader mới cho Ruby. Nó hiệu quả, an toàn cho luồng và phù hợp với cú pháp hằng số của Ruby.
Với một cấu trúc thư mục theo đúng quy tắc, Zeitwerk sẽ load các class và module khi cần, nghĩa là bạn không cần phải viết các lệnh require trong file của mình.
Zeitwerk được mặc định kích hoạt trong Rails 6.
Tuy nhiên, nếu bạn không muốn sử dụng nó, bạn luôn có thể sử dụng loader cũ bằng cách config trong application.rb
.
1 2 | config<span class="token punctuation">.</span>autoload <span class="token operator">=</span> <span class="token symbol">:classic</span> |
Source: https://medium.com/rubyinside/whats-coming-to-rails-6-0-8ec79eea66da