How do I use respond_with with custom classes in Rails 3? - ruby-on-rails

I am making a JSON API with Rails and it seemed to work fine except for when I use respond_with custom classes (not an ActiveRecord inherited one).
Here is my class:
class JsonResponse
def initialize(data, status)
#data = data
#status = status
end
def as_json(options={})
{
:data => #data,
:status => #status
}
end
end
which is a simple response wrapper. When I try doing this:
def create
unless(Match.find_by_user_id(params[:user_id]))
Match.create(:user_id => params[:user_id])
end
time_response = JsonResponse.new("5", "success")
respond_with(time_response)
end
I get this error:
NoMethodError (undefined method `model_name' for JsonResponse:Class):
app/controllers/matches_controller.rb:9:in `create'
Any ideas? respond_with is driving me crazy.

Your class should response to to_json method
Obviously set :location option in respond_with method. Rails try to create restful route from the object you pass to the method, but because your object is not resource, the error is raised.

I am not sure if this helps but I do not see respond_to...
respond_with works together with respond_to...
respond_to :html, :xml, :json
This can be defined on Controller level
example:
class UsersController < ApplicationController::Base
respond_to :html, :xml, :json
def index
respond_with(#users = User.all)
end
def create
#user = User.create(params[:user])
respond_with(#user, :location => users_url)
end
end
and then you can define your json template... don't know if you leave the json template empty if it takes your "JSONResponse" class...
just a thought...

Related

How to make Rails helper methods available when calling render_to_string to render a template

I have a ActiveMailer class, and inside I am sending emails with attached PDF template using the render_to_string method like this:
def send_sales_order(email_service)
#email_service = email_service
#sales_order = SalesOrder.find_by_cid(email_service.order[:id])
mail(:subject => I18n.t("custom_mailer.sales_order.subject", company_name: "test"), :to => #email_service.recipients) do |format|
format.html
format.pdf do
attachments['purchase_order.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string('sales_orders/show.pdf.erb', locals: {current_company: #sales_order.company})
)
end
end
end
Inside the show.pdf.erb template, I called my helper methods defined elsewhere such as the current_company method defined in ApplicationController like follow:
class ApplicationController < ActionController::Base
helper_method :current_company, :current_role
def current_company
return if current_user.nil?
if session[:current_company_id].blank?
session[:current_company_id] = current_user.companies.first.id.to_s
end
#current_company ||= Company.find(session[:current_company_id])
end
But these helper methods are not available when I use the render_to_string method to render the template, is there a way around this?
Thanks
ApplicationController.new.render_to_string works for me
Starting with Rails 5 you can use:
rendered_string = ApplicationController.render(
template: 'invoice/show',
assigns: { invoice: invoice }
)
This create a request-like object in a controller like env, assign a #invoice instance variable accessible in the template.
See documentation here for more options:
http://api.rubyonrails.org/classes/ActionController/Renderer.html#method-i-render
Just beat my head on the wall for a couple hours on this one. Finally found the add_template_helper method which did the trick.
class ApplicationController < ActionController::Base
add_template_helper ApplicationHelper
...
def foo
#foo = render_to_string(partial: 'some_file.rb')
end
end
This will make all methods from ApplicationHelper available, even when using render_to_string with a partial.

How to call methods of another controller

I need to call methods from another controller. What is the best way? For example:
catalogues_controller.rb
class Site::CataloguesController < ApplicationController
respond_to :js, :html
def index
produc_list # call method other controller
end
end
other_controller.rb
class OtherController < ApplicationController
respond_to :js, :html
def produc_list
myObj = Catalagues.find(params[:id])
render :json => myObj
end
end
You could implement a module, and include it in your Controller.
Let's call this module "Products Helper":
# In your app/helpers
# create a file products_helper.rb
module ProductsHelper
def products_list(product_id)
catalague = Catalagues.where(id: product_id).first
render :json => catalague
end
end
And then, in the controllers you need to use this method:
class Site::CataloguesController < ApplicationController
include ProductsHelper
respond_to :js, :html
def index
products_list(your_id) # replace your_id with the corresponding variable
end
end
You can call dispatch directly on your controller's method. Pass in an ActionDispatch::Response instance and it will be populated with the response. Assuming a json response in this example:
def other_controller_method
req = ActionDispatch::Request.new(request.env)
resp = ActionDispatch::Response.new
YourControllerClass.dispatch(:your_controller_method_name, req, resp)
render json: resp.body, status: resp.status
end
If you have RESTful routes (and acccess to the helper methods that come with them), then you should just be able to use redirect_to to redirect to whatever action you want to call,
# something like... controller_name_action_name_url
# In your case, in the catalouges/index method
# Note this also assumes your controller is named 'other'
redirect_to others_product_list_url(product_id)

Rails RABL respond_with error template

Using RABL in Rails 3.2.x, given the following controller action:
respond_to :html, :json
def create
#foo = Foo.create(params[:foo])
respond_with #foo
end
Assuming the validation fails, how do you get respond_with to use a RABL template instead of the standard JSON hash of errors -- IE. I would like other model attributes besides the validation error message sent back along with the request.
Suggestions?
I found this one out the hard way. You should create a custom responder for your application controller, or at least your individual response. See Three reasons to love ActionController::Responder for more details.
My solution:
# app/responders/api_responder.rb
class ApiResponder < ActionController::Responder
def to_format
case
when has_errors?
controller.response.status = :unprocessable_entity
when post?
controller.response.status = :created
end
default_render
rescue ActionView::MissingTemplate => e
api_behavior(e)
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
#...
self.responder = ApiResponder
#...
end
You could also use respond_with #foo, responder: ApiResponder instead.
Thanks to haxney for sending me in the right direction.
I guess, you need to remove the respond_to call at the top of the controller and remove the respond_with call within the action to get rabl render your rabl template.
Just add a respond_to block at the end of each action where you don't need RABL.

Overriding as_json method with params

First of all, I'm using Rails 3.0.6 and Ruby 1.9.2
I have a controller with two different actions, both should return a json object, but with different formats. Therefore I'm overriding the as_json method to write the JSON object in my own format. Problem is that I don't know how to pass params to as_json method since it's being automatically called by Rails.
My code looks like this:
class MyController < ApplicationController
def action1
# my code
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects }
end
end
def action2
# a different code
respond_to do |format|
# This action should return a JSON object but using a different format
format.js { render :json => #myobjects }
end
end
end
class MyModel < ActiveRecord::Base
def as_json(options = {})
# I would like to add a conditional statement here
# to write a different array depending on one param from the controller
{
:id => self.id,
:title => self.description,
:description => self.description || "",
:start => start_date1.rfc822,
:end => (start_date1 && start_date1.rfc822) || "",
:allDay => true,
:recurring => false
}
end
end
Note that #myobjects are a collection of objects which class is MyModel.
Any help would be appreciated. Thank you!
Call it explicitly in controller and pass params. as_json will return string and calling as_json on string returns itself. It is quite common practice.
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects.as_json(params) }
end

Best way to handle 404 in Rails3 controllers with a DataMapper get

It's very simple, I want to handle a normal [show] request with a call to DataMapper like I did in Merb.
With ActiveRecord I could have done this:
class PostsController
def show
#post = Post.get(params[:id])
#comments = #post.comments unless #post.nil?
end
end
and it handles the 404 by catching the resource's exceptions.
DataMapper instead doesn't do this automatically so right now I'm solving it with this solution:
[moved in the answers]
It is possible to tell the controller to halt inside the not_found function?
I like to use exception throwing, and then use ActionController's rescue_from.
Example:
class ApplicationController < ActionController::Base
rescue_from DataMapper::ObjectNotFoundError, :with => :not_found
def not_found
render file => "public/404.html", status => 404, layout => false
end
end
class PostsController
def show
#post = Post.get!(params[:id]) # This will throw an DataMapper::ObjectNotFoundError if it can't be found
#comments = #post.comments
end
end
Done 'the old Merb way':
class ApplicationController
def not_found
render file: "public/404.html", status: 404, layout: false
end
end
class PostsController
def show
#post = Post.get(params[:id])
not_found; return false if #post.nil?
#comments = #post.comments
end
end
again: It is possible to tell the controller to halt inside the not_found function instead of explicitly calling 'return false' in the show action?
edit: thanx to Francois that found a better solution:
class PostsController
def show
#post = Post.get(params[:id])
return not_found if #post.nil?
#comments = #post.comments
end
end
As DM documentation says, you can use #get!

Resources