Rails renders json including methods with parameters
I have this render in a method controller:
render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :methods => :translatedCity}}}}
but the translatedCity
method in the model must take a language parameter.
def translatedCity
language = params[:language]
puts "translatedCity.language=" + language
city = TranslationService.translateCity(self.city_id, language)
return city.element_translation
end
I have a parameter in my controller. Is there a way to pass this parameter to a method in the model?
source to share
All answers are good!
I just did something like:
In the controller:
boat.port.language = params[:language]
In the model:
attr_accessor :language
@language
def translatedCity
city = TranslationService.translateCity(self.city_id, language)
return city.element_translation
end
What do you think?
source to share
Unfortunately, the process of passing methods that require params to_json
is not well built into rails. the typical pattern is to change a method as_json
on the model to accept custom keys and then call to_json
(or render :json
)
# city.rb model
def translatedCity(language)
puts "translatedCity.language=" + language
city = TranslationService.translateCity(self.city_id, language)
return city.element_translation
end
def as_json(options = {})
json_to_return = super
if options.has_key? :translatedCity
city_translation = self.translatedCity(options[:translatedCity][:language])
json_to_return[:translatedCity] = city_translation
end
return json_to_return
end
now in your controller you all pass in accoringdly
render :json => @boats,
:include => {
:port => {
:only => [:id, :name],
:include => {
:city => {
:only => [:id, :name],
:translatedCity => params[:language]
}
}
}
}
source to share
as_json
doesn't support methods that require arguments, but it's a good place to use a Presenter (or Decorator or View Model, depending on who you're talking to) that builds the JSON you want. Draper is a gem that helps with this if you want one and your controller will look something like
data = @boats.map do |boat|
BoatPresenter.new(boat).as_json(language: params[:language])
end
render json: data
Then it BoatPresenter
will add extra data to its own as_json
:
def as_json(options = {})
@boat.as_json(options).tap do |json|
json[:translated_city] = @boat.translated_city(options[:language])
end
end
You can also BoatPresenter
add your default include
s and so on if you want the standard set not requiring the controller to worry about that. And if you don't want to do the array mapping yourself, Draper also offers collectors (for example BoatsPresenter
).
If you don't want to add a class, you can also override the option as_json
directly Boat
to handle language
:
def as_json(options = {})
super(options).tap do |json|
json[:translated_city] = translated_city(options[:language])
end
end
source to share
You can set "language" in session variable and set it before executing to_json in your controller and destroy the session in your method before returning like following
def translatedCity(language=nil)
language ||= session[:language]
puts "translatedCity.language=" + language
city = TranslationService.translateCity(self.city_id, language)
session[:language] = nil #destroy session, or use session.delete(:language)
return city.element_translation
end
session[:language] = language
render :json => @boats, :include => {:port => {:only => [:id, :name], :include => {:city => {:only => [:id, :name], :methods => :translatedCity}}}}
source to share