I want to write this library that responds to some json and html request. In the controller's action, I will call MyLib.search(params). Then in "module Something; class MyLib", I have "def search(params); respond_to ... render :json ...; end". If I try to use this library, I get "NoMethodError (undefined method `respond_to' ...".
How should I write this, so that I get respond_to and render in scope?
Perhaps a mixin would serve you better, something like this:
module Something
def search # params will be in scope so no need to pass it
#...
respond_to do |format|
format.json ...
end
end
end
and then in the controller:
class SomeController < ApplicationController
include Something
def whatever
# ...
search
end
end
Related
Is it possible to create an after_filter method in the Rails ApplicationController that runs on every action and renders to JSON? I'm scaffolding out an API, and I'd like to render output to JSON for every action in the controller.
clients_controller.rb
def index
#response = Client.all
end
application_controller.rb
...
after_action :render_json
def render_json
render json: #response
end
The after_action is never executed, and the code aborts with:
Template is missing. Missing template clients/index, ...
If the render json: #response is moved into the controller action, it works correctly.
Is there a filter that will allow me to DRY up the controllers and move the render calls to the base controller?
You can't render after_action/ after_filter. The callback after_action is for doing stuff after rendering. So rendering in after_action is too late.
But your exception is just because you miss the JSON template. I recommend using RABL (which offers a lot of flexibility to your JSON responses and there is also a Railscast about it). Then your controller could look like:
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def show
#client = Client.find params[:id]
end
end
And don't forget to create your rabl templates.
e.g. clients/index.rabl:
collection #clients, :object_root => false
attributes :id
node(:fancy_client_name) { |attribute| attribute.client_method_generating_a_fancy_name }
But in the case you still want to be more declarative you can take advantage of the ActionController::MimeResponds.respond_to like:
class ClientsController < ApplicationController
respond_to :json, :html
def index
#clients = Client.all
respond_with(#clients)
end
def show
#client = Client.find params[:id]
respond_with(#client)
end
end
Btw. beware if you put code in an after_action, this will delay the whole request.
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.
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)
I have a module in lib directory with the name "Transpo.rb":
module Transpo
class FT
def getCities
...
end
end
end
And in the controller I have
require 'Transpo.rb'
class TranspoController < ApplicationController
def index
#transpo = Transpo::FT.getCities()
respond_to do |format|
format.html # index.html.erb
format.json { render json: #transpo }
end
end
But when I run "http://localhost:3000/transpor" always gives the error:
NoMethodError in TranspoController#index
undefined method `getCities' for Transpo::FT:Class
Why? I've already set the auto_load lib in application.rb but continue with the same problem.
getCities is defined as an instance method, but you are calling it as a class method.
Either create an instance with something like instance = Transpo::FT.new, or change the definition of getCities to be def self.getCities to make it into a class method.
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.