In the profile page in my views I created an edit profile tab so the User can edit the profile in the show page. I am using a custom Devise controller but I am not sure how to tackle having the route for edit as the same as the show page.
This is my controller
class Teachers::RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create],
# before_action :configure_account_update_params, only: [:update]
#GET /resource/sign_up
def new
super
end
def sign_up_params
params.require(:teacher).permit(:name, :avatar, :email, :password)
end
#POST /resource/
def create
super
end
# GET /resource/edit
def edit
super
end
and this is my routes
Rails.application.routes.draw do
devise_for :teachers, controllers: { registrations: 'teachers/registrations' }
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "teachers#index"
resources :teachers
resources :students
resources :courses
resources :behavior_reports
resources :participation_reports
resources :grades
resources :parents
resources :attendances
end
Related
can I get your help on the error? I keep getting this oneπ
enter image description here
My routes π
enter image description here
Rails.application.routes.draw do
root 'static_pages#home'
resources :products, only: :show
devise_for :users
devise_scope :user do
authenticate :user, ->(u) { u.admin? } do
namespace :admin do
resources :products
end
end
end
end
And Controller π
enter image description here
class Admin::ProductsController < ApplicationController
before_action :authenticate_user!
def index; end
end
I use the same set_modpack method in several controllers, and I decided to make a concern for it. But when I add the concern to the controller, all routes give me AbstractController::ActionNotFound error.
My routes:
concern :commentable do
resources :comments, shallow: true
end
resources :modpacks, concerns: :commentable do
resources :releases, controller: 'modpack_releases'
end
My concern:
# app/controllers/concerns/modpack_derivative.rb
module ModpackDerivative
extend ActiveSupport::Concern
protected
def set_modpack
#modpack = Modpack.find(params[:id])
if #modpack.nil?
#modpack = Modpack.find_by!(slug: params[:id])
end
end
end
My controller:
# app/controllers/modpacks_controllers.rb
class ModpacksController < ApplicationController
include ModpackDerivative
before_action :set_modpack, only: [:show, :update, :destroy]
# 100% standard index, create, show,
# update and destroy actions (API)
private
def modpack_params
params.require(:modpack).permit(:logo, :name, :slug, :summary, :tags, :description)
end
end
It works fine if I delete the concern and paste the set_modpack methods inside the controllers.
About routing, If I do something like this:
resources :students
resources :teachers
I will get something like:
students GET /students(.:format) students#index
...
teachers GET /teachers(.:format) teachers#index
...
Changing to:
resources :students, controller: :users
resources :teachers, controller: :users
will give me:
students GET /students(.:format) users#index
teachers GET /teachers(.:format) users#index
Note that now, both resources are using the same controller Users and the same action index. But what I need, instead of using the same index action, is the students resource to use actions prefixed by students like students_index and teachers resources prefixed by teachers like teacher_index.
In other words, I want bin/rails routes to give me the following output:
students GET /students(.:format) users#students_index
teachers GET /teachers(.:format) users#teachers_index
I know that I can do the same with:
get 'students', to: 'users#students_index'
But there is a way to do the same with resources?
I don't think there's a way to do that with resources helper. What you could do (if it's only the index action you wanna override) is add an except, like this:
resources :students, controller: :users, except: [:index]
resources :teachers, controller: :users, except: [:index]
then, as you already suggested, do the individuals index actions like that:
get 'students', to: 'users#students_index', as: :student
get 'teachers', to: 'users#teachers_index', as: :teacher
Or you could reconsider the structure of your controllers... Good luck!
There is a far better way to do this as you might have surmised - inheritance.
# app/controllers/users_controller.rb
class UsersController < ApplicationController
delegate :singular, :plural, :param_key, to: :model_name
before_action :set_resource, only: [:show, :edit, :update, :destroy]
before_action :set_resources, only: [:index]
def initialize
#model_name = resource_class.model_name
super
end
def show
end
def index
end
def new
#resource = resource_class.new
set_resource
end
def create
#resource = resource_class.new(permitted_attributes)
if #resource.save
redirect_to #resource
else
set_resource
render :new
end
end
def edit
end
def update
if #resource.update(permitted_attributes)
redirect_to #resource
else
set_resource
render :edit
end
end
def destroy
#resource.destroy
redirect_to action: "index"
end
# ...
private
# Deduces the class of the model based on the controller name
# TeachersController would try to resolve Teacher for example.
def resource_class
#resource_class ||= controller_name.classify.constantize
end
# will set #resource as well as #teacher or #student
def set_resource
#resource ||= resource_class.find(params[:id])
instance_variable_set("##{singular}", #resource)
end
# will set #resources as well as #teachers or #students
def set_resources
#resources ||= resource_class.all
instance_variable_set("##{plural}", #resources)
end
def permitted_attributes
params.require(param_key).permit(:a, :b, :c)
end
end
# app/controllers/teachers_controller.rb
class TeachersController < UsersController
end
# app/controllers/students_controller.rb
class StudentsController < UsersController
end
# routes.rb
resources :students
resources :teachers
This lets you follow the regular Rails convention over configuration approach when it comes to naming actions and views.
The UsersController base class uses quite a bit of magic through ActiveModel::Naming both to figure out the model class and stuff like what to name the instance variables and the params keys.
So this is in routes file :
resources :users do
resources :lamps
end
I want to be able to display a user's "lamps" with something like :
http://localhost:3000/users/2/lamps
and show all the existing lamps whichever user owns it using :
http://localhost:3000/lamps
both are very different as the first one is more of a management view and the later is more what a user browsing would see.
The thing is they both go to the index action of the lamp_controller
How can I manage this in a clean way without having to use if statements in my action/view?
You can use the module option to route the nested routes to a namespaced controller:
Rails.application.routes.draw do
resources :users do
resources :lamps, only: [:index], module: 'users'
end
resources :lamps, only: [:index]
end
Another way to do this for a group of resources is:
Rails.application.routes.draw do
resources :users do
scope module: 'users' do
resources :lamps, only: [:index]
resources :chairs
resources :rugs
end
end
resources :lamps, only: [:index]
end
This would let you handle the different contexts in separate controllers:
# app/controllers/lamps_controller.rb
class LampsController < ApplicationController
# GET /lamps
def index
#lamps = Lamp.all
end
end
# app/controllers/users/lamps_controller.rb
class Users::LampsController < ApplicationController
before_action :set_user
# GET /users/:user_id/lamps
def index
#lamps = #user.lamps
# renders /views/users/lamps/index.html.erb
end
private
def set_user
#user = User.find(params[:user_id])
end
end
I am adding tagging functionality to my app, via acts_as_taggable_on.
That gem doesn't add controllers, but I would like to. I am adding the tagging functionality to my Node model.
On my NodeController, I know I could simply add the explicit actions like this:
def add_tagged_user
end
def remove_tagged_user
end
def tagged_users
end
But that doesn't feel very restful or Railsy.
The corresponding route would look like this:
resources :nodes do
match :add_tagged_user, via: [:post], on: :member
match :remove_tagged_user, via: [:delete], on: :member
match :tagged_users, via: [:get], on: :member
end
Is there a RESTful or a more Railsy way to do this?
You could go with a single TagsController, with routes that match the RESTful resource(s).
Something like
Routes
# routes.rb
resources :nodes do
resources :tags, only: [:show, :create, :update]
end
resources :other_resources do
resources :tags, only: [:show, :create, :update]
end
Controller
class TagsController < ApplicationController
before_action :load_taggable
def create
#taggable.tags.create(tag_params)
end
private
def load_taggable
# switches on params
#taggable = if params[:node_id]
Node.find(params[:node_id]
elsif # other things that are taggable
# OtherThing.find(...)
end
end
end