How to avoid links to map.root getting shortened? - ruby-on-rails

I have a Reports controller and various reports:
http://localhost/reports/main/this_month
http://localhost/reports/main/last_month
http://localhost/reports/main/this_year
I wanted http://localhost to default to http://localhost/reports/main/this_month. That is easy enough using map.root in my routes.rb.
However when I do this any links to http://localhost/reports/main/this_month are now shortened to just http://localhost. I want the links to stay full

I think it is very possible in Rails 2. The url string that is generated depends on which url helper you call in your view.
map.reports '/reports/:action/:timeframe', :controller => :reports
# todo pretty this up with some more named routes for reports
map.root :controller => "reports", :action => "main", :timeframe => "this_month"
Now, root_url will be http://locahost/. When you use reports_url(:action => 'main', :timeframe => 'this_month'), it will be http://localhost/reports/main/this_month. They both render the same action.
It sounds like you have set up the root, but just don't create any links with root_url.

One option is using a dummy controller that makes a redirect_to.
Routes:
map.reports '/reports/:action/:timeframe', :controller => :reports
# this triggers the action 'index' on 'welcome'
map.root :controller => "welcome"
And then on the Welcome controller:
class WelcomeController < Application: ApplicationController
def index
redirect_to :controller => "reports", :action => "main", :timeframe => "this_month"
end
end

As far as I know this is not possible in Rails 2 by default. There is a plugin called Redirect Routing that will allow it, however, which you could look into. In Rails 3, this functionality is built in. You can read about it in The Lowdown on Routes in Rails 3 over at Engine Yard.

Related

Creating new rails action doesn't work?

i have a controller "Apps". It consists of one action "index". Now I want to add a new action called "buy":
def buy
respond_to do |format|
format.html
end
end
i added a buy.html.erb to the views, but when browsing to /apps/buy, i get following message:
Unknown action - The action 'show' could not be found for AppsController
in the routes I added this:
match '/apps/buy', :controller => 'apps', :action => 'buy'
thanks in advance!
The url is being caught by the standard /apps/:id route, I assume you also have resources :apps in your routes?
Simply place the buy route first:
match '/apps/buy', :controller => 'apps', :action => 'buy'
resources :apps
Bear in mind that routes are executed in the order they are defined, so the specific ones need to precede the general.
A simpler approach as #Ryan suggests is adding a collection route to the resource:
resources :apps, :collection => { :buy => :get }

Get SEO friendly URLS with Rails without method_missing?

Currently we are using method_missing to catch for calls to SEO friendly actions in our controllers rather than creating actions for every conceivable value for a variable. What we want are URLS like this:
/students/BobSmith
and NOT /students/show/342
IS there a cleaner solution than method_missing?
Thank you!
You can define a route for that particular format fairly easily.
map.connect "/students/:name", :controller => :students, :action => :show, :requirements => {:name => /[A-Z][A-Z]+/}
Then in your show action you can find by name using params[:name].
You can create a catch-all route. Put this at the bottom of config/routes.rb with whatever controller and action you want:
map.connect '*path', :controller => '...', :action => '...'
The segments of the route will be available to your controller in the params[:path] array.

Rails routes matching query parameters

Rails routes are great for matching RESTful style '/' separated bits of a URL, but can I match query parameters in a map.connect config. I want different controllers/actions to be invoked depending on the presence of a parameter after the '?'.
I was trying something like this...
map.connect "api/my/path?apple=:applecode", :controller => 'apples_controller', :action => 'my_action'
map.connect "api/my/path?banana=:bananacode", :controller => 'bananas_controller', :action => 'my_action'
For routing purposes I don't care about the value of the parameter, as long as it is available to the controller in the params hash
The following solution is based on the "Advanced Constraints" section of the "Rails Routing from the Outside In" rails guide (http://guides.rubyonrails.org/routing.html).
In your config/routes.rb file, include a recognizer class have a matches? method, e.g.:
class FruitRecognizer
def initialize(fruit_type)
#fruit_type = fruit_type.to_sym
end
def matches?(request)
request.params.has_key?(#fruit_type)
end
end
Then use objects from the class as routing constraints, as in:
map.connect "api/my/path", :contraints => FruitRecognizer.new(:apple), :controller => 'apples_controller', :action => 'my_action'
Unless there is a concrete reason why you can't change this, why not just make it restful?
map.connect "api/my/path/bananas/:id, :controller => "bananas_controller", :action => "my_action"
If you have many parameters, why not use a POST or a PUT so that your parameters don't need to be exposed by the url?

How do I get a custom action to go to the right place?

I'm new to Ruby on Rails, and I'm sure I'm just missing something simple and stupid, but I can't figure out how to get my 'account/signup' action working.
This is what I have in my routs file:
map.connect ':controller/:action'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
map.root :controller => "home"
I've added this to my accounts controller:
def signup
#account = Account.new
respond_to do |format|
format.html # signup.html.erb
format.xml { #account }
end
end
And I've added signup.html.erb to the accounts views folder.
Yet when I go to it in the browser I get this error:
ActiveRecord::RecordNotFound in AccountsController#show
Couldn't find Account with ID=signup
What am I doing wrong?
Add the following code right on top of your routes.rb file
ActionController::Routing::Routes.draw do |map|
map.connect 'account/signup', :controller => 'account', :action => 'signup'
...
...
...
end
Also I think you mean Account and not Accounts.
Here's a tip:
If you run rake routes it'll show you all the possible routes for your app.
It should then be obvious depending on what URL you are entering whether it'll be correctly resolved or not.
For a good overview of routes read this guide. There's really a lot of stuff you can do with routes so it's worth taking some time to read it.
If you want to follow the REST model, you controller should be called sessions and your signup action should be new, so in your routes you could do :
map.resources :sessions
This website is highly recommended to all newcomers to Rails :
http://guides.rubyonrails.org/
The following will do as well when added to the do |map| section of routes.rb
map.resource :account, :member => {:signup => :get}
Will create the standard routes for your accounts controller, as well as add the new route account/signup. It also provides the usual url helpers in addition to signup_account_url and signup_account_path

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

Resources