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.
Related
I have a custom route that looks like:
match 'firsttime' => 'registrations#new', :via => :get, \
:defaults => {:promotion_path => :firsttime}
The goal with the above route is to be able to have a url like http://www.mysite.com/firsttime which maps to the "new" method for the registrations controller, with the promotion for that registration being the "firsttime" promotion.
In one of my models, I have a shortcut method to try and generate this url:
class Registration < ActiveRecord::Base
include ActionView::Helpers
include ActionDispatch::Routing
include Rails.application.routes.url_helpers
belongs_to :promotion
def get_share_link()
promotion_path = promotion.url_path
url_to_share = url_for :controller => 'registrations', :action => 'new', :promotion_path => promotion_path
end
end
Calling the method get_share_links() fails with the error:
No route matches {:controller=>"registrations", :action=>"new",
:promotion_path=>"firsttime"}
What am I doing wrong, or am I even using the right method for this?
routes are used in the Controller. Stick this as a before filter in your controller instead.
Edit #1
Is this devise or another gem for authentication?
EDIT #2
Can you get to http://www.mysite.com/registrations/new?promotion=firsttime presuming it's localhost for now of course, since you are in dev mode.
Question #1
What do you mean by "In one of my models, I have a shortcut method to try and generate this url:" what are you generating this for and where is it used? mailings? It seems your route works, it's just not being made when you want it to, and it's unclear where you want it to be used.
Answer
does it work with only_path: false supplied as an option?
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]}'
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
I want to copy the twitter profile page and have a url with a username "http://www.my-app.com/username" and while I can manually type this into the address bar and navigate to the profile page I can't link to the custom URL.
I think the problem is in the routes - here's the code in my routes.rb
map.connect '/:username', :controller => 'users', :action => 'show'
Also, I have Question and Answer models and I want to link to them with the customized URL like so:
http://www.my-app.com/username/question/answer/2210
There's nothing wrong with your route. Just remember to define it at the end, after defining all other routes. I would also recommend using RESTful routes and only if you want to have better looking URLs use named routes. Don't use map.connect. Here's some good reading about Rails routes.
Here's how this could look:
map.resources :questions, :path_prefix => '/:username' do |question|
question.resources :answers
end
map.resources :users
map.user '/:username', :controller => 'users', :action => 'show'
Just a draft you can extend.
To create urls you need to define to_param method for your user model (read here).
class User < ActiveRecord::Base
def to_param
username
end
end
I know this questions is old but it will help someone.
You could try the below. I've used it in a rails 4 project and all seems to be working great. The reason for the as: :admin is I also had a resources posts outside of this scope. It will add a admin to the helper calls e.g. admin_posts_path
scope ":username", module: 'admin', as: :admin do
get '', to: 'profiles#show'
resources :posts
end
I have used like this
In View part
portfolio.user.name,:id =>portfolio) %>
and in rout.rb
map.show_portfolio "portfolios/:username", :action => 'show_portfolio', :controller => 'portfolios'
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