To keep to restfull protocol, I need to do /api/backup_jobs/777/errors.
In rails, the parent controller- I have:
module Api
class BackupJobsController < ApplicationController
respond_to :json
def show
#backup_job = #backup_jobs.find(params[:id])
respond_with data: #backup_job
end
end
end
in the child controller:
module Api
class ErrorsController < BackupJobsController
respond_to :json
def index
respond_with data: #backup_jobs.find(params[:id]).backup_events.errors
end
end
end
But obvisouley this isn't going to work because params[] doesn't exist for /api/backup_jobs/777/errors
How can I pass the #backup_job = #backup_jobs.find(params[:id]) from the parent controller's def show to the child controller and have it accessible in the child's def index?
You cannot do that because when an ErrorsController is created and used, you will not have a BackupsJobsController that ran before it.
This comes down to the nature of HTTP being a request-response protocol.
Instead, you can extract the line of code you wrote into a method that will be inherited by the ErrorsController.
backup_jobs_controller.rb:
module Api
class BackupJobsController < ApplicationController
def show
find_backup_job
respond_with data: #backup_job
end
protected
def find_backup_job
#backup_job = #backup_jobs.find(params[:id])
# or maybe #backup_job = BackupJob.find(params[:id])
end
end
end
errors_controller.rb:
module Api
class ErrorsController < BackupJobsController
respond_to :json
def index
respond_with data: find_backup_job.backup_events.errors
end
protected
def find_backup_job
#backup_job = BackupJob.find(params[:backup_job_id])
end
end
end
Related
Doing API for my first Rails project.
I have base class ApiController for all the APIs:
module Api
class ApiController < ::ApplicationController
respond_to :json
skip_before_action :verify_authenticity_token
def index
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
private
def resource_class
raise NotImplementedError
end
def as_json_collection
{}
end
end
end
And I have child class UsersController:
module Api
class UsersController < ApiController
private
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
My routes:
namespace :api do
resources :users
end
Then I'm going to my_app/api/users I have error:
The action 'index' could not be found for Api::UsersController
But then I changing UsersController writing it's own index class, everything works fine and I'm having all my Users in JSON format.
I've alrady tried to comment all private marks in both classes, but that doesn't help.
I don't want to write an API for every entity in my project and I'd like to avoid this problem in future.
I got it to work with this:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json { render json: '"abc"' }
end
end
end
end
module Api
class UsersController < ApiController
end
end
The URL was http://localhost:3000/api/users.json
So for you I suggest:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json do
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
end
end
end
end
module Api
class UsersController < ApiController
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
Its supposed to be like this:
class Api::ApiController < ApplicationController
and do not forget to remove extra end, end of the file!
#sample
- api(folder)
-- api_controller.rb (Api::ApiController < ApplicationController)
-- users_controller.rb (Api::UsersController < Api::ApiController)
application_controller.rb
You need to read this my friend:
rails routes
When you do this:
namespace :api do
resources :users
end
rails creates CRUD routes automatically which means that my_app/api/users will translate to: .../users#index.
Do this to see your routes created by rails:
rails routes and for specific word (e.g. user): rails routes | grep user
Seeing is believing ;)
To have a cleaner code I want to split my controller in some concerns.
In my routes.rb how to redirect to concern without redefine the methods of concern index show destroy create ...
class SomeController
include SomeConcern
def index
end
end
module SomeConcern
def index
end
end
Sorry for my bad english.
Lets say we have a CarsController and AirplanesController that have the typical create and new actions.
class AirplanesController < ApplicationController
def new
#airplane = Airplane.new
end
def create
#airplane = Airplane.new(create_params)
if #airplane.save
redirect_to #airplane
else
render :new
end
end
# ...
end
class CarsController < ApplicationController
def new
#car = Car.new
end
def create
#car = Car.new(create_params)
if #car.save
redirect_to #car
else
render :new
end
end
# ...
end
To dry this up we can extract the shared code to a module:
module Createable
extend ActiveSupport::Concern
included do
attr_accessor :resource
alias_attribute :self.controller_name.to_sym, :resource
end
def new
#resource = resource_class.new
yield #resource if block_given?
end
def create
#resource = resource_class.new(create_params)
if #resource.save
yield #resource if block_given?
redirect_to #resource
else
render :new
end
end
private
def create_params
raise "not implemented controller!"
end
def resource_class
#resource_class ||= self.controller_name.classify.constantize
end
end
We can then apply it to the controller classes by:
class CarsController < ApplicationController
include Createable
def create_params
params.require(:car)
.permit(:model) # ...
end
end
class AirplanesController < ApplicationController
include Createable
def create_params
params.require(:airplane)
.permit(:model) # ...
end
end
But a very important point here is that you are not routing to the module. The module is providing methods to the controller class.
You have to always map to your controller. Concerns are modules where you can put shared logic (it makes sense only in case you need 2 absolutely similar methods in 2 different controllers).
I think, that such code should work:
class SomeController
include SomeConcern
end
module SomeConcern
def index
end
end
Isn't it?
But concerns mostly used to move out some private helper methods from controller, rather actions as we do in this code piece
In the below, will the implicit render still get called in the SpecialArticlesController? Or will it get skipped to the implicit render in ArticlesController?
class SpecialArticlesController < ArticlesController
def index
...
super
end
end
class ArticlesController
def index
...
end
end
You will not get a double render error, unless you are explicitly rendering twice. This code will cause an error:
class ParentController < ApplicationController
def index
render
end
end
class ChildController < ParentController
def index
render
super
end
end
Whereas the code below will not cause an error:
class ParentController < ApplicationController
def index
end
end
class ChildController < ParentController
def index
super
render
end
end
I have a method in my Rails application controller that I call when I am creating a new Post. I have also created an API to create a new Post. However, it seems that I need to repeat the code for my application controller method in my API BaseController. Where is the best place to put the application controller method in my Rails app so that I do not have to repeat the code for the API? Is there a way that the API base controller can inherit from the ApplicationController?
Rails app
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def foo_action(string)
return string
end
end
Rails API
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
respond_with(#post)
end
end
end
class Api::V1::BaseController < ActionController::Base
respond_to :json
def foo_action(string)
return string
end
end
Based on #phoet's recommendation in the comments above, I moved the foo_action method to the Post model:
class Post < ActiveRecord::Base
def foo_action
string = self.text
return string
end
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
respond_with(#post)
end
end
end
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)