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?
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