I'm trying to achieve the following:
I have a simple page where a visitor can view a list of albums (index action) and by clicking on any of the albums, view the photos of every album (show action). No other actions (edit, new, update, destroy) should be reachable.
When the user logs in she can see the index and show pages, plus all the other actions should be available, but now index and show will lock differently. The URLs shouldn't show up any different from before, though.
I created these routes:
users_albums GET /users/albums(.:format) users/albums#index
users_album GET /users/albums/:id(.:format) users/albums#show
new_users_album GET /users/albums/new(.:format) users/albums#new
.... and so on ...
albums GET /albums(.:format) albums#index
album GET /albums/:id(.:format) albums#show
I also created user directories under the app/controllers and app/views directories where I placed the namespaced (logged in) user controllers and views.
So now for the visitor (no login) the default controller/views should be used but once the user signs in (using devise) the controller/views under the user directory should take over.
How can I put this redirection in place?
This is my routes.rb so far:
devise_for :users, skip: [:registrations]
as :user do
get "/sign_in" => "devise/sessions#new" # custom path to login/sign_in
get "/sign_up" => "devise/registrations#new", as: "new_user_registration" # custom path to sign_up/registration
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
namespace :users do
resources :albums do
resources :photos
end
end
resources :albums, only: [:index, :show] do
resources :photos, only: [:index, :show]
end
root to: "albums#index"
As I understand you need to redirect users only after login if different views/controllers are used. You can use devise after_sign_in_path_for method. Add it to your application controller:
def after_sign_in_path_for(resource)
users_albums_path #or any route that you want use
end
But for allow/deny actions or show/hide links better approach use something like gem pundit and avoid DRY.
Related
Pretty simple question, but I can't seem to find the answer with a good ole fashioned Google.
The error:
undefined method `user_signed_in?' for #<ActionDispatch::Routing::Mapper:0x007fc6369320e8> (NoMethodError)
Server won't even start.
My code:
Rails.application.routes.draw do
devise_for :users, :path_prefix => 'u'
resources :users
devise_scope :user do
get "login", to: "devise/sessions#new", as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
get 'user/edit', to: 'devise/registrations#edit', as: :change_password
end
resources :apps do
resources :elements, controller: 'apps/elements'
end
resources :elements do
resources :features, except: [:index], controller: 'apps/elements/features'
end
if user_signed_in?
root to: 'apps#index'
else
devise_scope :user do
root to: 'devise/sessions#new'
end
end
end
What's the best way to go about making this work? I'm hesitant to try to work around it and make the site vulnerable, being a new RoR user. Thanks in advance.
In Rails access control is done on the controller layer - not on the routing layer.
Routes just define matchers for different sets of params and request urls. They are processed before rails even starts processing the request, they don't know anything about the session. Rails could have even used YML to define routes, except that Ruby is better at DSLs.
If you want to require that the user is signed in you would use:
class SomeController < ApplicationController
before_action :authenticate_user! # a Devise helper method
end
I have read up on the Rails Guides.
What I want to set up are the following routes that are routed to the 'profiles' controller:
GET profiles/charities - Should display all the charities
GET profiles/charties/:id should display a specfic charity
GET profiles/donors - Should display all the donors
GET profiles/donors/:id - Should display a specfic donor
I have created the profile controller and two methods: charities and donors.
Is this all I need?
The following will set up routes for what you want, but will map them to :index and :show of CharitiesController and DonorsController:
namespace :profiles do
# Actions: charities#index and charities#show
resources :charities, :only => [:index, :show]
# Actions: donors#index and donors#show
resources :donors, :only => [:index, :show]
end
When it's more appropriate to set up custom routes, something like this would do:
get 'profiles/charities', :to => 'profiles#charities_index'
get 'profiles/charities/:id', :to => 'profiles#charities_show'
get 'profiles/donors', :to => 'profiles#donor_index'
get 'profiles/donors/:id', :to => 'profiles#donor_show'
Here are relevant sections in the guide that you were going through:
Resource Routing: the Rails Default - Controller Namespaces and Routing
Non-Resourceful Routes - Naming Routes
The charities and donors seem to be nested resources. If so, in your config/routes.rb file you should have something like,
resources :profiles do
resources :charities
resources :donors
end
Because these are nested resources, you do not need the two methods named charities and donors in your profiles controller. In fact, depending on your app, you may need separate controllers and/or models for your charities and donors.
I want to replace the normal /users/:id route that is created by the resources command, with a more abstract /profile route. It won't be possible to view other users profiles in my app, and therefor the current route is unnecessary specific.
I have tried to overwrite the route created by 'resources :users' with:
get '/profile', to: 'users#show'
and other variances and combinations, but can't seem to get it right. Either the controller can't find the user because of a missing id or it simply can't find the route.
Thanks for the help!
You can use this code in routes.rb file:
resources :users, :except => :show
collection do
get 'profile', :action => 'show'
end
end
It will generate url "/users/profile".
But, if u want to use only '/profile', then don't create route as collection inside users resources block.
resources :users, :except => :show
get 'profile' => "users#show", :as => :user_profile
It will redirect '/profile' to show action in users controller.
I suggest simply adding a users/me route pointing to the show action of your UsersController like so:
resources :users, only: [] do
collection do
get 'me', action: :show
end
end
You can also use the match keyword in routes.rb file.
match 'users/:id' => 'users#show', as: :user_profile, via: :get
If I use the below code as is (dashboards#show), I get a record cannot be found error Couldn't find Dashboard without an ID because I don't have a user id in the path (I typically link to this dashboard using this: user_dashboard_path(current_user, current_user.dashboard)<-- cannot use that in routes.rb).
I think I need to pass the user into the code block that sets the root path, but I'm not sure how this would be accomplished.
Any tips?
Here is my entire routes.rb file:
MyApp::Application.routes.draw do
resources :users do
resources :dashboards, only: [:show, :edit, :update, :destroy]
end
authenticated :user do
# Instead of the show action, I want to go to user_dashboard which is
# GET /users/:user_id/dashboards/:id(.:format) dashboards#show
root :to => "dashboards#show", as: :authenticated_root
end
root :to => "home#index"
devise_for :users, :controllers => { :registrations => :registrations }
end
Option 1:
In your dashboards controller, change to:
#user = current_user
#dashboard = current_user.dashboard
This will also prevent users from seeing other user's dashboards.
Option 2:
Now, if users can see other user's dashboard fine, then change your route:
root :to => "dashboards#find", as: :authenticated_root
and in your dashboards controller:
def find
redirect_to user_dashboard_path(current_user, current_user.dashboard)
end
struggling for a few hours on this one now. I have integrated the Devise gem into my Rails project after originally making my own auth system but I am facing an issue I can't understand.
When the user signs in the method:
def after_sign_in_path_for(resource_or_scope)
user = resource_or_scope
user_path(user.username)
end
Is triggered to redirect the user to their profile.
I have an edit user route which takes the user to a page in which they can edit their details and add a 'wanted item'. Two separate forms with two separate controllers and actions.
The 'add wanted item' method posts to a different controller that rendered the view called WantsController and adds a wanted item for the user through an association.
For some reason the after_sign_in_path_for method is called when submitting this form? It has nothing to do with signing in...
Here are my routes:
#users/auth
devise_for :users, :skip => [:sessions, :registrations]
devise_scope :user do
# registration
get "/signup", to: "users#new", as: :sign_up
post "/signup", to: "users#create", as: :sign_up_create
# account
get "/:username/account", to: "users#edit", as: :user_account
put "/users/:id", to: "users#update", as: :user_update
# shows
get "/:username", to: "users#show", as: :user
get "/:username/interests", to: "users#interests", as: :user_interests
get "/:username/offers", to: "users#offers", as: :user_offers
get "/:username/trades", to: "users#trades", as: :user_trades
# auth
post "/signin" => 'devise/sessions#create', as: :sign_in
delete "/signout", to: "devise/sessions#destroy", as: :sign_out
#wants
resources :wants, only: [:create, :destroy]
end
If I place the wants resource outside of the devise scope (which is where I expect it should go) I receive the following:
Could not find devise mapping for path "/wants"
What's happening here? Stumped!
Thanks.
Argh, silly mistake. Why is it after hours of struggling that when you post a question on Stack Overflow you figure it out after like 5 minutes?!
I had copied and pasted my RegistrationsController into the WantsController file to save typing the controller code but forgot to make it inherit from ApplicationController rather than Devise::RegistrationsController.
Lesson: Don't copy and paste!