Computer >> Máy Tính >  >> Lập trình >> Ruby

Một cách nhẹ nhàng để xử lý các tình huống xác thực khác nhau

(Tôi đã gửi bài đăng này vào danh sách của mình cách đây vài tháng. Nếu bạn thích nó và muốn đọc nhiều hơn giống như nó, bạn nên đăng ký!)

Tôi đã gặp phải một tình huống kỳ lạ vào ngày hôm trước với một trong các ứng dụng của mình.

Giả sử bạn có một Article và những bài báo này lần đầu tiên được tạo dưới dạng bản nháp. Những bản nháp này phải nhẹ. Bạn muốn có thể tạo và chỉnh sửa chúng ngay lập tức mà không cần nội dung hoặc thậm chí là tiêu đề.

Nhưng khi bạn xuất bản những bài báo này, bạn cần phải có thêm một số xác nhận. Chúng phải có tiêu đề, nội dung và tất cả phần còn lại của nội dung đó.

Bạn có thể viết một loạt if để tự kiểm tra. Nhưng bạn sẽ không có tất cả các xử lý biểu mẫu đẹp mà bạn nhận được từ Rails. Và tôi nhận thấy rằng việc sử dụng Rails cho những thứ mà Rails xử lý tốt sẽ giúp bạn đỡ phải đau đầu hơn rất nhiều sau này. Có cách nào để sử dụng xác thực cho việc này không?

ifunless ?

Hỗ trợ xác thực :if:unless nhưng điều này không phải lúc nào cũng rõ ràng:

app / models / article.rb
class Article < ActiveRecord::Base
  # validate in draft mode
  validates_presence_of :author_id 

  # validate only when published
  validates_presence_of :body, unless: lambda { |o| o.draft? }
  
  ...
end

Ngoài ra, đây không phải là cách tôi muốn xuất bản một bài báo. Tôi muốn nghĩ về việc xuất bản một bài báo như một hành động , không phải là một trạng thái trong bài viết, vì vậy việc đặt và kiểm tra một thuộc tính không phải là câu trả lời phù hợp.

Một tính năng Rails ít được biết đến, được ghi lại một cách nhẹ nhàng

Vấn đề này hoàn toàn phù hợp với các ngữ cảnh xác thực của Rails. Lần đầu tiên tôi biết về các ngữ cảnh xác thực tùy chỉnh từ một ý chính mà DHH đã viết. Nhưng thật khó để tìm ra lời giải thích tốt về những thứ này là gì và cách sử dụng chúng.

Tài liệu cho các ngữ cảnh tùy chỉnh khá mỏng. Đề cập duy nhất về nó mà tôi có thể tìm thấy trong tài liệu API là ở đây. Và tài liệu API cho những xác thực này hoàn toàn không đề cập đến ngữ cảnh tùy chỉnh! Điều này đặc biệt khó khăn - nếu bạn không biết cái gì đó được gọi là gì, làm cách nào để tìm thêm tài liệu về nó?

Cuối cùng tôi đã tìm thấy một cái nhìn tổng quan tốt ở đây:https://blog.arkency.com/2014/04/mastering-rails-validations-contexts/. Rất đáng để đọc. Hãy xem ví dụ của chúng ta trông như thế nào:

app / models / article.rb
class Article < ActiveRecord::Base
  # validate in draft mode
  validates_presence_of :author_id
 
  # validate only when published
  validates_presence_of :body, on: :publish
  ...
end
app / controllers / article_controller.rb
class ArticleController < ApplicationController
  respond_to :html

  def create
    @article = Article.create(article_params)
    respond_with @article
  end

  def publish
    @article = Article.find(params[:id])
    @article.attributes = article_params
    @article.save(context: :publish)
    respond_with @article
  end

  private
  def article_params
    params.
      require(:article).
      permit(:body, :title, :author_id)
  end
end

Không tệ lắm! Điều kỳ lạ duy nhất là phải sử dụng save thay vì update trong phương pháp xuất bản. Điều đó xảy ra vì update không thể lấy ngữ cảnh xác thực làm tham số. (Có thể đó là cơ hội cho một yêu cầu kéo?)

Ngoài tiết kiệm

Các ngữ cảnh xác thực tùy chỉnh hữu ích hơn là chỉ lưu bản ghi theo những cách khác nhau. Các ngữ cảnh hoạt động với valid? :

@article.valid?(:publish)

vì vậy bạn có thể sử dụng ngữ cảnh xác thực ở bất cứ đâu bạn muốn để trả lời câu hỏi: “Đối tượng này có thuộc tính này không? Và nếu không, tại sao không? ”

DHH tạo một phương thức kết thúc bằng -able? ủy quyền cho valid?(:context) ). Tôi thực sự thích giao diện của nó:

app / models / article.rb
class Article
  validate :has_been_published, on: :view
  validate :is_not_spam, on: :view

  def viewable?
    valid? :view
  end

  private

  def has_been_published
    if published_at.future?
      errors.add(:published_at, "is in the future")
    end
  end

  def is_not_spam
    if is_spam?(body)
      errors.add(:body, "has been detected as spam")
    end
  end
end

Bằng cách này, khi bạn kiểm tra nó, bạn sẽ nhận được nhiều thông tin hơn chỉ là true hoặc false :

unless @article.viewable?
  # @article.errors is now filled out
end

Bài báo có thể xem được không? Chà, tại sao không?

Vừa đủ nhẹ

Có một số cách khác để bạn có thể nhận được loại hành vi xác thực này. Bạn có thể tạo ActiveModel tùy chỉnh các đối tượng dịch vụ với các xác nhận của riêng chúng. Bạn có thể sử dụng :if hoặc :unless về xác nhận của bạn. Nhưng giống như nhiều thứ trong Rails, ngữ cảnh xác thực tùy chỉnh là sự thỏa hiệp tốt giữa khả năng đọc và tính linh hoạt.