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
Related
as I am trying to create web with subodmain for every user i coded as below:
In below controller i am checking if subdomain from sign_in devise page belons to user in User.rb table. If not i am logging out.
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :check_domain
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:slug])
devise_parameter_sanitizer.permit(:account_update, keys: [:slug])
end
def check_domain
unless current_user.nil?
#domain = User.find_by(email: current_user.email)
if #domain.slug != request.subdomain
sign_out_and_redirect(current_user)
flash.alert = "User not found."
end
end
end
end
At this moment I have routes.rb as below:
Rails.application.routes.draw do
resources :devise
resources :places
resources :people_items
resources :people, only: [:edit, :update]
resources :gift_items
resources :gifts, only: [:edit, :update]
resources :program_items
resources :story_items
devise_for :users
root to: 'pages#index'
get '/home', to: 'output#home'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :pages do
member do
delete :delete_file
end
end
resources :stories, only: [:edit, :update]
resources :programs, only: [:edit, :update]
resources :galleries, only: [:edit, :update] do
member do
delete :delete_image
end
end
get 'informations/information'
resources :aboutus, only: [:edit, :update]
end
I modified one more settings in config/environments/development.rb
config.action_dispatch.tld_length = 1
Everything is working fine.
In tutorials i found out that for working subdomains properly I should modify routes.rb as below. But what is the purpose of modification file below? Do I really need to touch routes.rb? That's my question.
constraints subdomain: /.*/ do
resources :pages
end
Do I really need to modify this? Or what is purpose of this modification? Is that really necessary?
Thank you so much for your advices!
Constraints in routes.rb allow you to restrict when routes should or shouldn't be matched, returning a 404 in the event that they do not.
You can do this on a per-route basis for params like so:
get 'products/:category', to: "products#index", category: /(fruit|electronics|medicine)/, as: :products
This would allow requests to http://yourdomain.com/products/fruit but not to http://yourdomain.com/products/vehicles for example. Accessing any forbidden domains would provide you with a 404 response.
You can provide generic constraints to a bunch of different routes by enclosing them in a constraints block, like so:
constraints ->(req) { Site.exists?(subdomain: req.subdomain) } do
resources :articles, path: "blog", only: [:index, :show]
...
end
So here, we're checking that a Site record exists with the subdomain provided. If not, we return a 404.
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
In my routes.rb I have
namespace :admin do
resources :clients do, only: [:index] do
resources :products, only: [:index, :new, :create]
end
resources :products, only: [:index]
end
Notice that I have two lines for resources :products. One is nested within resources :clients and the other as a top-level resource on :admin; each of these two has a different purpose in the application.
rake routes gives me:
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/products#index
POST /admin/clients/:client_id/products(.:format) admin/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/products#new
admin_products GET /admin/products(.:format) admin/products#index
I have a admin_client_products for the nested product#index resource. I also have admin_products for the top-level product#index resource. However, they point to the same controller action: admin/product#index.
Question: At this point, I need rails to deduce that these are two different actions. Using rails conventions, is there a way to tell rails that these two resources should have different controller actions i.e. one that should hit admin/products#index and the other should hit admin/clients/products#index?
The nested route should hit this:
class Admin::Clients::ProductsController < Admin::BaseController
def index; end
end
The top-level route should hit this:
class Admin::ProductsController < Admin::BaseController
def index; end
end
Definitely you can!
Here you need to customize your resourceful route by explicitly specifying a controller to use for the resource. The :controller option will let you do that.
So, in your case, specifying the controller as clients/products for the admin_clients_products resource would work in your desired way.
namespace :admin do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end # ------------------------------
resources :products, only: [:index]
end
rails routes will now give you what you want:
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/clients/products#index
POST /admin/clients/:client_id/products(.:format) admin/clients/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/clients/products#new
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_products GET /admin/products(.:format) admin/products#index
=========================
Extra bits:
If you want to omit the /admin portion from the url (I mean, if your application's routing design permits to), then you could use: scope module: 'admin' do...end like the following:
scope module: 'admin' do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end
resources :products, only: [:index]
end
and suddenly your routes will start looking awesome :)
client_products GET /clients/:client_id/products(.:format) admin/clients/products#index
POST /clients/:client_id/products(.:format) admin/clients/products#create
new_client_product GET /clients/:client_id/products/new(.:format) admin/clients/products#new
clients GET /clients(.:format) admin/clients#index
products GET /products(.:format) admin/products#index
I have a weird error when I want to redirect users to the root_url when they try to access blogs/new url in my app.
My routes are
resources :blogs, only: [:index, :show] do
resources :comments, only: [:create]
end
namespace :admin do
resources :blogs
resources :users, only: [:index, :show]
resources :comments, only: [:create, :new, :destroy]
end
My non-admin blogs controller looks like this:
class BlogsController < ApplicationController
before_action :set_blog, only: [:show]
def show
unless #blog
redirect_to blogs_path
flash[:notice] = "You are not authorized to create a post."
end
end
def index
#blogs = Blog.all
end
private
def set_blog
#blog = Blog.find(params[:id])
end
end
I get the error Couldn't find Blog with 'id'=new.
In rails, the priority of routes goes from top to bottom. Meaning, when you try to hit /blogs/new, the route gets matched with the show action of blogs defined in the top of your routes.rb.
blogs/new gets matched with /blogs/:id which is mapped to blogs#show action.
In the set_blog method, params[:id] is new and since there is no record with the id of new, you're getting that weird error.
How to get around this? Change the priority of your routes.
Move the following block below the admin namespaced routes.
namespace :admin do
resources :blogs
resources :users, only: [:index, :show]
resources :comments, only: [:create, :new, :destroy]
end
resources :blogs, only: [:index, :show] do
resources :comments, only: [:create]
end
By the way, your question says that you want to avoid non-admin users to access blogs#new. If that's the case, you should try to hit /admin/blogs/new and not /blogs/new.
If you had done that, you wouldn't have gotten the error in the first place. But still, its good to know about the priority of routes in rails.
Hope this helps!
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