2

I am working on a Rails API backend with a separate Rails/Angular front-end codebase. The responses from the Rails API must be structured in a certain way to match with the front-end flash messages. A(n) (slightly boiled down) example controller response is

render json: {status: "Unauthorized", code: "AUTH.UNAUTHORIZED", fallback_msg: "Unauthorized request"}

so basically all my controllers are full of this, and sometimes when there are 5 possible responses for a method (ex: if a user resets their email the response can be invalid password, invalid email, email is already registered etc). A coworker suggested abstracting these methods out into the model, so the model is response for sending back these messages and then the controller can just have

def ctrl_method user = User.update(password: password) render json: user, status(user) end (where status is another method that provides the HTTP status code based on the object's status attribute) My question is is this best practice? I understand Rails MVC and feel like the responsibility of sending the json message belongs to the controller not the model.

kittyminky
  • 475
  • 5
  • 25

3 Answers3

1

I say you're both right. The controller should have the responsibility of sending the message, and the methods should be abstracted out--just not into the model, more like a class that lives in /lib.

This should make testing easier as well.

TK-421
  • 9,600
  • 3
  • 35
  • 34
0

Without knowing any more details, I would think about utilizing concerns to deal with the statuses. This allows business logic to be encapsulated without muddying up your models. So you could have

module Concerns
  module Statuses
    def deal_with_show
      # business logic goes here to generate the status
    end
    def deal_with_index
      # business logic goes here to generate the status
    end
    def deal_with_create
      # business logic goes here to generate the status
    end
    def default_status
      # set a default status here
    end
  end
end

and then in the controllers

class MyController < ApplicationController
  include Concerns::Statuses

  def index
    render json: deal_with_index
  end
end

Of course, that breakdown of statuses in the concern is arbitrary. It can be handled however makes sense: by method, by verb, or by some other distinction.

This is a good bit on concerns.

Community
  • 1
  • 1
jjk
  • 516
  • 7
  • 15
  • Each controller method has its own response, it isn't just get vs post it's index vs show vs create etc. – kittyminky Dec 11 '14 at 17:13
  • OK, but that doesn't change the idea. Using concerns to organize logic that is replicated across controllers is fit for purpose. I revised the answer to be more appropriate. – jjk Dec 12 '14 at 20:07
0

If you want to deal with ActiveRecord errors I think you use errors.full_messages and use the same code and status for such errors (status: 'Forbidden', code: '').

Note that you should customize your messages in locale files see guide. But it's useful if you want to translate your app in different languages.

Success messages can be inferred from the controller and the action (see below).

class ApplicationController < ActionController::Base
 ...

 def render_errors(object)
   render json: {status: 'Forbidden', code: 'WRONG_DATA', fallback_msg: object.errors.full_messages.join(", ")} 
 end

 def render_success(message = nil)
   message ||= I18n.t("#{self.controller_name}.message.#{self.action_name}"
   render json: {status: 'OK', code: 'OK', fallback_msg: message}
 end
end

class SomeController < ApplicationController
  def update
    if @some.update(params)
      render_success
    else
      render_errors(@some)
    end
  end
end
palkan
  • 171
  • 1
  • 4