I have a rails application with 3 controllers :
categories, subcategories, all
The two firsts fetch data from the db and render it via
render "list"
The last one should get the result of the first controller, the result of the second one and render them.
Simplified example :
# categories_controller.rb
class CategoriesController < ApplicationController
def index
render "list"
end
end
# subcategories_controller.rb
class SubcategoriesController < ApplicationController
def index
render "list"
end
end
# all_controller.rb
class AllController < ApplicationController
def index
# should render the both and join them
end
end
# list.html.erb
this is the list
Result :
/category
=> "this is the list "
/subcategory
=> "this is the list "
/all
=> "this is the list this is the list "
I tried to call
render "subcategory"
render "subcategory/index"
But nothing seems to work.
Do you have an idea?
Thanks,
A previous approach was the following :
# categories_controller.rb
class CategoriesController < ApplicationController
def self.getAll
return Category.all
end
def index
#data = CategoriesController.getAll
# some logic here
render "list"
end
end
# subcategories_controller.rb
class SubcategoriesController < ApplicationController
def self.getAll
return Category.all
end
def index
#data = SubcategoriesController.getAll
# some logic here
render "list"
end
end
# all_controller.rb
class AllController < ApplicationController
def index
#categoriesData = CategoriesController.getAll
#subcategoriesData = SubcategoriesController.getAll
render 'all'
end
end
# list.html.erb
<%= #data %>
# all.html.erb
<%= #categoriesData %>
<%= #subcategoriesData %>
But I have to rewrite a part of logic that is already there ...
You need to explicitly build the output that you need in the all controller action, rather than trying to join the outputs of other actions.
Two approaches:
Retrieve the items you want with two database calls, join them together into one big set of list items, then render the list as in the other actions.
class AllController < ApplicationController
def index
categories = Categories.all
sub_categories = Subcategories.all
#all = categories + sub_categories
end
end
Retrieve both sets of data, make the list.html page call a partial called _sub_list.html or something, then have a new template for the all page called all.html, which renders the new partial twice, once for each set of data.
class AllController < ApplicationController
def index
#categories = Categories.all
#sub_categories = Subcategories.all
end
end
To reuse logic across controllers. use a concern:
module SharedLogic
extend ActiveSupport::Concern
def useful_function
# some code...
end
end
class SubcategoriesController < ApplicationController
include SharedLogic
def index
#data = SubcategoriesController.getAll
# some logic here
useful_function
render "list"
end
end
Related
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
I have a controller
class Api::V1::InvoicesController < ApplicationController
def index
#invoices = Invoice.all
render json: #invoices, each_serializer: Api::V1::InvoicePreviewSerializer
end
end
On every single controller i will be specify that the serializer used is the name spaced with
Api::V1::
Then model name and then model name followed by PreviewSerializer
How could I on the appication controller specify that on every index action append each_serializer: Api::V1::MODEL_NAMEPreviewController?
I've not tested this but I think it should work like this:
# in the ApplicationController
def render(*args)
if action_name == 'index'
options = args.extract_options!
options[:each_serializer] = Api::V1::InvoicePreviewSerializer
args << options
end
super(*args)
end
Hope that works and helps!
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
How can I create a dynamic function name in rails using the data in the database? I don't know if this is even possible.
here is a sample of my goal
class PageController < ApplicationController
def (PageModel.find(1)) #def stay
#codes here #codes here
end #end
end
I know the syntax is wrong. please help, thanks
Update
this function will only be called via routes, and in my routes I have this line
match "/:action", :controller => "page", :via => "get"
the function will look like this if it is manually generated
def stay
#some query
render 'stay_page', :layout => 'stay_page_layout'
end
def pleasure
#some query
render 'pleasure _page', :layout => 'pleasure _page_layout'
end
In routes.rb:
# Either this...
get "pages/:page", to: "pages#page"
# Or this, but make sure this is the last route in the file
get "/:page", to: "pages#page"
Your controller:
class PageController < ApplicationController
def page
#page = PageModel.find(params[:page])
end
end
If you just need to render different templates depending on the page, you can do this:
def page
#page = PageModel.find(params[:page])
render #page.name
end
If you need custom logic you could do something like this:
VALID_PAGES = ["contact_us"]
def page
#page = PageModel.find(params[:page])
execute(#page.name)
end
def execute(name)
if VALID_PAGES.include?(name)
send(name)
end
end
def contact_us
# do stuff
end
Assuming your PageModel have page_id and page_name, your url get page_id as an parameter, and will render with page_name.
code for controller:
class PageController < ApplicationController
def dynamic_action
page_name = PageModel.find(params[:page_id]).page_name
render "#{page_name}_page", :layout => "#{page_name}_page_layout"
end
end
route:
match '/:page_id' => 'page#dynamic_action'
I am trying to figure out how to routes the contents of one controller into that of another.
Currently, I have two controllers -
1. Static Pages controller - this is very simple, all it used for is to yield one page (with tabbable pages)
class StaticPagesController < ApplicationController
def home
end
end
2.Guides controller - This is where a user may (currently) add guides to the db.
class GuidesController < ApplicationController
def home
end
def show
#guide = Guide.find(params[:id])
end
def index
#guide = Guide.all
end
def new
#guide = Guide.new
end
def create
#guide = Guide.new(guide_params)
if #guide.save
redirect_to '/guides'
else
render 'new'
end
end
private
def guide_params
params.require(:guide).permit(:title, :description, :image, :date, :date_end, :extra_info)
end
end
I want to display the INDEX part of the controller (which currently works at '/guides', in my root directory, or 'home' in the static pages controller.
I've tried fiddling with all of this, and my last port of call is the routes.rb file. However I am not all together sure, this seems straight forward enough and its cheesing me off not being able to do it.
One of the possible solutions is that you use:
class StaticPagesController < ApplicationController
def home
#guide = Guide.all
end
end
and copy the code from index,html.erb of your guide to home.html.erb of static pages.
other than that you can use rendering: link
EDIT:
use partial to show index of guides:
class GuidesController < ApplicationController
def index
#guide = Guide.all
render partial: "index"
end
end
in your views of guide rename "index.html.erb" to "_index.html.erb" and
in static_pages/home.html.erb add:
<%= render :partial => "guides/index" %>