Mở đầu

Khi code hoặc refactor code trong rails, bạn có thể tách code trong action trong controller thành nhiều method con.

Nhưng khi tách ra thành các method con, bạn có thể phải xử lý render hoặc redirect_to ngay trong method con.

Nếu ở cả method con và method cha đều có render hoặc redirect_to thì rails sẽ ném lỗi bị lặp render hoặc redirect_to cho bạn.

Dưới đây là một số cách để xử lý được lỗi trên nhưng vẫn khiến code của bạn dễ đọc.

Ví dụ với một đoạn code có lỗi trên như sau:

class MyController
  def show
    redirect_to payment_path(@order) if @order.open? && @order.payment.blank?
    render json: {"status": "success"} if @order.valid?
    # more code here ...
  end
end

Khi không có method con

Đơn giản chỉ cần gọi return khi gặp redirect_to hoặc render là được:

class  MyController
  def show
    return redirect_to payment_path(@order) if @order.open? && @order.payment.blank?
    render json: {"status": "success"} if @order.valid?
    # more code here ...
  end
end

 

Nhưng thay vì return như trên thì bạn nên dùng and return vì nó khiến code dễ đọc dễ hiểu khi có redirect_to hoặc render:

class  MyController
  def show
    redirect_to payment_path(@order) and return if @order.open? && @order.payment.blank?
    render json: {"status": "success"} if @order.valid?
    # more code here ...
  end
end

 

Chú ý: Không dùng && return thay vì and return.

Khi tách thành method con

Lúc tách thành method con thì có một rắc rối là bạn phải return ngay lập tức ở method cha khi method con gọi render hoặc redirect_to.

Bạn có thể làm như sau:

class  MyController
  def show
    valid_order and return
    # more code here ...
  end

  private

  def valid_order
    redirect_to payment_path(@order) and return true if @order.open? && @order.payment.blank?
    render json: {"status": "success"} and return true if @order.valid?
    # more other code here ...
  end
end

Ở đoạn code trên, nếu method valid_order gọi redirect_to hoặc render thì sẽ return true về cho method show, lúc đó valid_order sẽ có giá trị true và câu lệnh return trong câu lệnh valid_order and return sẽ được thực thi (vì true and return = return).

 

Nếu làm như trên, có một điểm xấu là khi đọc tới câu lệnh valid_order and return sẽ khiến lập trình viên bối rối và đặt ra câu hỏi:

Đúng ra thì valid_order thành công thì phải thực hiện code phía dưới tiếp chứ vì sao lại return ở đây? 

Để sửa điểm xấu trên có thể làm như sau:

class  MyController
  def show
    valid_order or return
    # more code here ...
  end

  private

  def valid_order
    redirect_to payment_path(@order) and return if @order.open? && @order.payment.blank?
    render json: {"status": "success"} and return if @order.valid?
    # more other code here ...
    return true
  end
end

Lúc này câu lệnh trong hàm show đã thành valid_order or return, câu lệnh này sẽ dễ hiểu hơn vì theo logic thông thường thì nếu valid_order thành công thì sẽ thực hiện tiếp còn nếu không thì sẽ return (nên sẽ dùng or thay vì and).

Giải thích cho đoạn code trên chỉ cần để ý khi redirect_to hay render được gọi thì hàm valid_order sẽ trả về nil, lúc đó câu lệnh valid_order or return sẽ gọi return (vì nil or return = return).

Làm như cách trên thì code trong hàm valid_order sẽ khá rắc rối.

Có một cách đẹp nhất như sau:

class  MyController
  def show
    valid_order; return if performed?
    # more code here ...
  end

  private

  def valid_order
    redirect_to payment_path(@order) and return if @order.open? && @order.payment.blank?
    render json: {"status": "success"} and return if @order.valid?
    # more other code here ...
  end
end

Câu lệnh perfomed? sẽ trả về true nếu như trong hàm được gọi có render hoặc redirect_to được thực thi.

 

Link tham khảo:

https://blog.arkency.com/2014/07/4-ways-to-early-return-from-a-rails-controller/

https://apidock.com/rails/v4.2.7/ActionController/Metal/performed%3F