Service Object returning status

3k Views Asked by At

I am making a rails json api which uses service objects in controllers actions and basing on what happend in service I have to render proper json. The example looks like this.

star_service.rb

class Place::StarService
  def initialize(params, user)
    @place_id = params[:place_id]
    @user = user
  end

  def call
    if UserStaredPlace.find_by(user: user, place_id: place_id)
      return #star was already given
    end

    begin
      ActiveRecord::Base.transaction do
        Place.increment_counter(:stars, place_id)
        UserStaredPlace.create(user: user, place_id: place_id)
      end
    rescue
      return #didn't work
    end

    return #gave a star
  end

  private

  attr_reader :place_id, :user
end

places_controller.rb

def star
  foo_bar = Place::Star.new(params, current_user).call

  if foo_bar == #sth
    render json: {status: 200, message: "sth"}
  elsif foo_bar == #sth
    render json: {status: 200, message: "sth"}
  else
    render json: {status: 400, message: "sth"}
end

And my question is, if I should return plain text from service object or there is some better approach?

1

There are 1 best solutions below

2
On BEST ANSWER

It'll be opinionated of course but still...

Rendering views with data, returning data, redirecting etc are the responsibilities of controllers. So any data, plain text and other things you have to handle in your controller.

Service object have to provide one single public method for any huge complex operation performing. And obviously that method has to return simple value which tells controller if operation was completed successfully or not. So it must be true or false. Maybe some recognizable result (object, simple value) or errors hash. It's the ideal use case of course but it's the point.

As for your use case your service may return the message or false. And then controller will render that message as json.

And your star method must live in your controller, be private probably and looks like that:

def star
  foo_bar = Place::Star.new(params, current_user).call

  if foo_bar
    render json: {status: 200, message: foobar} 
  else
    render json: {status: 400, message: "Failed"}
  end
end

Your Service:

class Place::StarService
  def initialize(params, user)
    @place_id = params[:place_id]
    @user = user
  end

  def call
    if UserStaredPlace.find_by(user: user, place_id: place_id)
      return "Message when star is already given"
    end

    begin
      ActiveRecord::Base.transaction do
        Place.increment_counter(:stars, place_id)
        UserStaredPlace.create(user: user, place_id: place_id)
      end
    rescue
      return false
    end

    return "Message if gave a star"
  end

  private

  attr_reader :place_id, :user
end