I created a users table and implemented Devise. I then added an enum role to the user table so that I can identify admins, guides, and members.
What I'd like to do is limit routes for certain resources based on the enum user type. I've tried:
resources :users.guide do
get 'guide/dashboard', :to => 'guides#dashboard'
end
and some variants, but with no success. The above gives me the error:
undefined method `guide' for :users:Symbol
I've done some poking around and can't seem to find a good response. I'm avoiding CanCanCan and Rolify as I'd like to keep things as basic as possible. Any ideas? Thanks!
Edited Per Below Suggestion
So I went and updated my routes as suggested below so that the file looks similar to this:
Rails.application.routes.draw do
devise_for :users
#open to public
root 'welcome#index'
resources :guides, only: [:show], param: :username
resources :itineraries, only: [:index] #, only: [:index, :show]
authenticate :user, ->(u) { u.guide? } do
resources :guides, only: [:edit, :destroy], param: :username
get 'guide/dashboard', :to => 'guides#dashboard'
end
resources :locations, only: [:new, :create, :edit, :update, :destroy, :index, :show]
end
For whatever reason, the guide profile show/edit works fine; the dashboard works fine; but the other things outside of the authenticate block (itineraries and locations resources) don't work, I get redirected to the Devise login page. They are outside (and in one case above) the authenticate block, not sure why this is occurring with some resources/routes and not others.
You can use devise helpers. With authenticate, it is possible to make resources and routes that will ask for authentication before they can be accessed. Replacing authenticate with authenticated causes the resource to simply be unavailable if the user is not authenticated. Both helpers take an optional scope and block to provide constraints on the model instance itself.
authenticate :user, ->(u) { u.guide? } do
get 'guide/dashboard', :to => 'guides#dashboard'
end
So your problem is limit routes for certain resources based on the enum user type. I suggest you should implement the authorization stuff instead of trying to create more resources/scopes in your route, it's correctness.
Related
So I have an app which has an admin section. The admin section has a challenges controller with an index method and a view index.
I have also a challenges controller seperate from the admin folder. This controller has the whole CRUD.
Every challenge belongs_to a subject. The controller subjects in the admin section has an index method and view. The controller subjects not in the admin section has the whole CRUD.
Now, in the view of subjects (NOT the admin section), I can do something like:
<%= link_to "New Challenge".html_safe, new_subject_challenge_path(#subject) %>
I would like to do the same in the admin-section, but I can't really figure out how to do it. Copying the code throws me an error:
No route matches {:action=>"new", :controller=>"challenges", :subject_id=>nil} missing required keys: [:subject_id]
But I was hoping I could do this without additional routes....
It seems like it should be easy but I don't really know how to handle this. Any help would be very much appreciated... I hope I explained myself well enough.
The admin routes are used with a namespace:
namespace :admin do
resources :paths, only: [:index, :new, :create, :update, :edit]
resources :users, only: [:index, :new, :create, :show, :edit, :update]
end
resources :challenges, except: [:index, :destroy] do
resources :solutions, only: [:create]
end
resources :subjects
The link you are creating points to a route that requires a subject id. In the subjects view it works because Rails can find the subject_id in the #subjectthat you are passing to the path helper.
When you copy and try to re-use the same link in your admin view, I expect that #subject is not assigned and so it cannot find the required subject_id. Provide your admin section view with the subject and it should work!
Also if you want to get a clearer idea of routing, the Rails docs are pretty great.
My app has a Company model and User models, I've been working on the path/url structure and have managed to get it working how I wish using nested resources as shown in the code below.
I've used FriendlyID to added company_names and usernames to the models which all works fine.
The path now look how I want they to look:www.mydomain.com/company_name/username
routes.rb
resources :companies, :path => '/', only: [:show] do
# constraints has been added for usernames that include a '.'
resources :users, :path => '/', only: [:show], :constraints => { :id => /.*/ }
end
PROBLEM: I still need the ability to add a new company record but it will no longer work. I understand this is due to changing the routes but I don't understand how to remedy this as it keeps viewing
www.mydomain.com/companies/new as an unknown user with the username 'new'.
I'd be grateful if anyone can point me in the right direction or give me a hard with this.
You have set only: [:show], which it means only show method is allowed.
To create a company, need to add :new and :create.
Something like this only: [:new, :create, :show]
Note:
Always after adding or changing smth in routes file, make sure to use rake routes, Rails 5 supports rails routes also.
You can see available routes!
I want to change my routes.rb file in a way so that it changes my current urls
localhost:3000/amitian/1
to localhost:3000/username
I will provide my routes.rb file for reference
Rails.application.routes.draw do
devise_for :amitians
root 'home#index'
resources :amitians do
member do
get :following, :followers
end
end
resources :confessions do
member do
get 'like' , to: 'confessions#upvote'
get 'dislike' , to: 'confessions#downvote'
end
resources :confessioncomments
end
resources :relationships, only: [:create, :destroy]
end
As Gregg mentioned, you can change the name of the parameter using:
resources :amitians, param: :username
But you're essentially just renaming a variable. Whether you expect an id or a username is determined in the controller action amitians#show:
#amitian = Amitian.find(param[:id]) # Treat :id as id
#amitian = Amitian.find_by_username(param[:id]) # Treat :id as username
Now, if you want to specifically route to /:username rather than /amitians/:username, you'll have to override that resource route:
resources :amitians, except: [:show] do
member do
get :following, :followers
end
end
get '/:username', to: 'amitians#show'
However, I would recommend against that. Having a parameter directly off root will cause lots of confusion for you when users type in the incorrect url and get a user page instead of a 404 error. Or even worse, what if a user chooses the username 'login' or 'register'? Either your register page would be unreachable or else that user's page would be.
I should also point out that rails convenience methods such as resources, Amitian.find, url_for #amitian, link_to #amitian etc. all use the REST standard which uses numerical IDs.
If you want to use a username instead of IDs, you'll have to stop relying on these methods and change your controllers and views, in addition to your routes file.
In rails 4 you can do the following in the route
resources :amitians, param: :username
Whenever I click the sign in or sign up button on my site, I receive this error:
Routing Error -- No route matches [POST]"/sessions/user"
Here is my routes file:
Here are my routes:
Any ideas as to why I am receiving this error?
I think you want to post to user/sessions instead of sessions/user
Devise uses it's own methods to generate routes, is it possible that resources :sessions is overriding the methods devise needs to exist? I'm almost positive devise defines a method called new_session_path(resource_name, resource). Your resources :sessions may be stepping on Devise's toes so to speak.
EDIT
I have just confirmed this by cloning devise and running grep -R session_path .. You need to remove resources :sessions from your code, or scope it into a different namespace using :as.
resources :sessions, :as => 'sessions_that_dont_anger_devise'
#=>
new_sessions_that_dont_anger_devise_path
sessions_that_dont_anger_devise_path
... etc.
resources :users, only: [:show, :new, :create] do
member do
get :following, :followers
end
end
Fixed by removing the first resources :users and stuffing it into this method.
I wasn't able to find anything here or elsewhere that covered both restricting a resource's routes and adding additional non-RESTful routes in Rails 3. It's probably very simple but every example or explanation I've come across addresses just one case not both at the same time.
Here's an example of what I've been doing in Rails 2:
map.resources :sessions, :only => [:new, :create, :destroy], :member => {:recovery => :get}
Pretty straightforward, we only want 3 of the 7 RESTful routes because the others don't make any sense for this resource, but we also want to add another route which is used in account recovery.
Now from what I gather doing either one of these things is very straightforward as well:
resources :sessions, :only => [:new, :create, :destroy]
Just like in Rails 2. And:
resources :sessions do
member do
get :recovery
end
end
So, how do I combine these two? Can I still use the old Rails 2 way of doing this? Is there a preferred way of doing this in Rails 3?
You can pass arguments and a block to resources:
resources :sessions, :only => [:new, :create, :destroy] do
get :recovery, :on => :member
end
And test it out with rake routes.
It should be working pretty much like this
resources :sessions, :only => [:new, :create, :destroy] do
member do
get :recovery
end
end
There is an even shorter way, as proposed by coreyward.
Check the rails guides, "Rails Routing from the Outside In".
I also can recommend "The Rails 3 Way" by Obie Fernandez, which got 2 pretty good chapters on Routing and RESt.
Cheers