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]}'
Related
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.
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'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
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
I want to show a simple HTML template, so I added a new empty action to my controller:
def calulator
end
And created the view calculator.html.erb . Then added a link to it:
<%= link_to 'Calculator', {:controller => "mycontroller", :action => "calculator"} %>
When I click it my log shows the following error:
ActionController::UnknownAction (No action responded to show. Actions: calculator, create, destroy, edit, index, new, and update):
Why is looking for a "show" action ? I have map.resources for the controller, as I done it with scaffolding
Any ideas?
You need to add a custom route pointing to the action 'calculator'.
Something like this:
map.connect 'mycontroller/calculator', :controller => 'mycontroller', :action => 'calculator'
You can define members and collections for resources.
map.resources :samples, :member => {:calculator => :get}
Member means that it relates to an instance of the resources. For example /samples/1/calculator. If it doesn't relate to an instance you can define it for the collection and can be access via /samples/calculator.
map.resources :samples, :collection => {:calculator => :get}
This also creates a helper method calculator_samples_path for the collection and calculator_sample_path(sample) for a member. For more on this have a look at Railscast Episode 35.
You are getting No action responded to show because you have the controller routed as map.resources. When you do that, rails sets up several routes for you. One of which is show, which maps every get request matching /mycontroller/somevalue to the show action with somevalue as the id (params[:id]). In mycontroller, there is no show action, as seen in the error message.
To fix this, Nils or Trevoke's answer should work just fine.
map.resources documentation