Route depending if user is an artist - ruby-on-rails

I'd like to set up my routes depending if the user is an artist. So for example, something like:
namespace 'dashboard' do
if current_user.is_artist
get '/settings', :to => 'users#edit', :as => 'account_settings'
put '/settings', :to => 'users#update', :as => 'account_settings'
delete '/settings', :to => 'users#destroy', :as => 'account_settings'
else
get '/settings', :to => 'artists#edit', :as => 'account_settings'
put '/settings', :to => 'artists#update', :as => 'account_settings'
delete '/settings', :to => 'artists#destroy', :as => 'account_settings'
end
end
Unfortunately, I cannot access current_user in routes.rb. However, the logic of the code above, explains what my intentions are.
Is there a way I can achieve this?

The routing table doesn't run at the context of a specific user. This is because it routes the request before reaching your session management code, so it can't tell who's the current user.
I can think of two solutions:
Render your settings links through some method that rewrites them if the user is an artist. This way 'artists' and 'users' get different URLs.
Add a before_filter to your UserSettings controller that catches requests from artists and redirects them to ArtistSettings (not sure of your internal structure, but you probably get the general idea).

from what you have it looks like you have set up a role called artists. all you have to do to make your current_user method work is: in your account controller you can set up a before filter like saying before_filter :check_user_role :only => :account_setting. then you would set your account_settings for the various roles and their various redirects_to call methods you have. i am working on a similar project and it works for me. i hope this helps you

You could create a piece of middleware that figures out the user on a level before it hits the actual application. This is what warden/devise are doing for example. Add the user object to the env variable and you can access it anywhere lower in the stack.

Your routing file simply determines which controller action is being invoked based on the URL. You can map both artists and users to a single controller, and place the logic for which screen to render in that controller by looking at the user. By default, controllers render a view that is based on the name of the controller and method, but you're free to override this and render whatever view you want. Use your route to dispatch requests to the appropriate edit/update/destroy actions, and put your user/artist logic into those actions to render either the users/edit.html.haml or the artists/edit.html.haml file...

As explained by Elad, the routing itself can not tell if the current user is an artist or not.
If you want to keep a single url with two differents behaviours and views, you may go with a single controller such as :
def settings
if current_user.is_artist
do something
render :action => "artist_settings"
else
do something else
render :action => "users_settings"
end
end

Related

Is there an easy way to route an entire controller to a site root?

I know how to route a page to a site root, and how to individually specify routes for every single page and action, but I feel like I am creating unnecessary work for myself at times by doing this. Is it possible to make it so that example.com/method always equates to some_controller#method?
Something like:
root "some_controller#index"
controller :some_controller do
get "/:method" => :method
end
instead of
root "some_controller#index"
controller :some_controller do
get "/" => :index, as: :index
get "/contact" => :contact, as: :contact
get "/photos" => :photos, as: :photos
...
end
or is that the best way?
You're pretty close already:
controller :some_controller do
get ":controller/:action"
end
:controller/:action will dynamically match the :action segment to a method of that name on the controller referenced by the symbol passed to the controller method.
I would be careful about security here. Take note on what methods your controller inherits from its parent class (probably ApplicationController), as any public methods will be accessible using this routing style.

How to handle this routing situation is Rails

i prefer writing the routes of my Rails applications by hand and i now have a situation where i am not sure what is the best way to do things. I want to have a building controller that shows a different page for every building like :
building/town_center
building/sawmill
..
Each of the above should have its own action and view page. I would normally create a route like:
scope :path => '/building', :controller => :building do
get '/view/:slug' => :view, :as => 'view_building'
end
But this only specifies a single action that would then need to call another internal controller method to redirect to the needed template to show. So, i would like your opinion, would you just specify a different route for every building(and action) explicitly ? Or just redirect in the view_building action ?
I think you are after something like this:
match "/building/:name", :to => "buildings#show", :as => :building
Then in your controller action 'show' just render template for the building name:
render :template => 'buildings/#{params[:name]}'

constraints in ruby-on-rails routing

Could someone describe what this is all about?
It's in the routing file:
match "photo", :constraints => {:subdomain => "admin"}
I can't understand it.
thanks
It's saying that the photo route will only be recognised and routed to a controller if the request contains the subdomain admin. For example, the Rails application would respond to a request of http://admin.example.org/photo, but not http://example.org/photo.
One our guys posted this today which describes how you could reuse the same routes with different contexts (in this case whether the user is logged in)
For instance if you create a simple class to evaluate true/false:
class LoggedInConstraint < Struct.new(:value)
def matches?(request)
request.cookies.key?("user_token") == value
end
end
You can then use the evaluator in the routes to determine what routes apply:
root :to => "static#home", :constraints => LoggedInConstraint.new(false)
root :to => "users#show", :constraints => LoggedInConstraint.new(true)
Obviously you can design the constraints to your needs, but Steve described a couple different variants.

How do I route user profile URLs to skip the controller?

Right now my user profile URLs are like so:
http://example.com/users/joeschmoe
And that points to the show method in the user controller.
What I'd ideally like to do is offer user profile URLs like this:
http://example.com/joeschmoe
So, what sort of route and controller magic needs to happen to pull that off?
I disagree with what jcm says about this. It's not a terrible idea at all and is used in production by the two biggest social networks Facebook and MySpace.
The route to match http://example.com/username would look like this:
map.connect ':username', :controller => 'users', :action => 'show'
If you want to go the subdomain route and map profiles to a URL like http://username.example.com/, I recommend using the SubdomainFu plugin and the resulting route would look like:
map.root :controller => 'users', :action => 'show' , :conditions => {:subdomain => /.+/}
These broad, catch all routes should be defined last in routes.rb, so that they are of lowest priority, and more specific routes will match first.
I also recommend using a validation in your User model to eliminate the possibility of a user choosing a username that will collide with current and future routes:
class User < ActiveRecord::Base
validates_exclusion_of :username, :in => %w( messages posts blog forum admin profile )
…
end
This does not make sense unless you have no controllers. What happens when you want to name a controller the same as an existing user? What if a user creates a username the same as one of your controllers? This looks like a terrible idea. If you think the /user/ is too long try making a new custom route for /u/
So your custom route would be...
map.connect 'u/:id', :controller => 'my/usercontroller', :action => 'someaction'
In routes.rb this should do the trick:
map.connect ":login", :controller => 'users', :action => 'show'
Where login is the name of the variable passed to the show method. Be sure to put it after all other controller mappings.
Well, one thing you need is to ensure that you don't have name collisions with your users and controllers.
Once you do that you, can add a route like this:
map.connect ':username', :controller => 'users', :action => 'show'
Another thing people have done is to use subdomains and rewrite rules in the web server, so you can have http://joeshmoe.example.com
In Rails 4 to skip controller from url you have to do add path: ''.
resources :users, path: '' do
end

Ruby on Rails routing

I am a newcomer to RoR and have a question about routing. I have set up an application and a simple user login system (yes I know there is already a login generator out there, I just wanted to try it for myself). I have a before_filter on a certain action that checks if the user is logged in or not. If not, it redirects them to the login page. This is how I have it routed, does this look right?
map.resources :user, :collection => { :login => :get }
also, why if I change
:login => :get
to
:login => :post
does it display the 'show' view?
map.resources :user, :collection => { :login => :get }
will correspond to a URL like "/users/login"
map.resources :users, :member => { :login => :get }
will correspond to a URL like "/users/123/login"
Because :user is referring to a resource, it will map the url to an action depending on the request method in addition to the url.
So, the first route above will map a GET to /users/login to the login action,
but when you change it to :post, it will then map a GET to /users/login to the default, which is the 'users' controller and 'show' action for a User with an ID of 'login' - which is not what you want.
(and it will map a POST to /users/login to the 'login' action)
It's close, but you may want to try:
map.resources :users, :member => { :login => :get }
They key difference is that the :collection keyword should be used when your new route is dealing with collections of that resource (such as 'index'), and not a single instance of the resource (such as a single user logging in).
Also if /users/user_id/login redirects to the login page, you're still performing a GET operation there. It's when you submit a form that you want to use a POST. So in essence you were telling Rails to expect a POST when it was receiving a GET. I can only speculate is was ignoring the login route altogether and just returning the whatever user was represented in the URL.
I'd recommend using a separate resource for your login system. A lot of people use session so you'd have
map.resource :session
You would then use the normal crud methods such as #new and #create for displaying the login form and submitting it respectively. You'll also need to create a session controller to handle these requests.

Resources