routes.rb explicitly choose template - ruby-on-rails

I would like to create one .erb file to be the output for a number of tiny actions that are just returning JSON. So with routes similar to:
map.json 'contacts_json', :controller => 'contacts', :action => 'get_json'
map.json 'cal_json', :controller => 'calendar', :action => 'get_json'
...
but this requires I create a contacts erb, and a calendar erb so on and so forth. Is there a way to explicitly tell them to use a json erb? Something like:
map.json 'contacts_json', :controller => 'contacts', :action => 'get_json', :view => 'layouts/json.html.erb'

I'm not sure it answers your question but using render :text => #foobar.to_json does wonders in some cases.

No -- you specify what view template to render in the controller action.

Related

Rails routes with clean urls

My problem is i want to remove the querystring part of this URL and make it clean.
http://staging.mysite.com/mycontroller?name=/philippines/about-manila
currently i have MyController.index() and I parse the name parameter in this method.
What i would eventually want is this :
http://staging.mysite.com/mycontroller/philippines/about-manila
the parameter part 'philippines/about-manila' can have arbitrary number of parameters, like
http://staging.mysite.com/mycontroller/philippines/about-manila/people
How can i do this in routes?
It sounds like you want route globbing. If you use:
map.my_route '/mycontroller/*parts',
:controller => :mycontroller,
:action => :index
and then go to the URL http://staging.mysite.com/mycontroller/philippines/about-manila/people, then your mycontroller controller's index action will be called, and params[:parts] will contain the array ["philippines", "about-manila", "people"].
Is it really arbitrary — or just variable?
If it's just variable, you can enclose optional parameters in parentheses:
match '/:city(/:section(/:subsection))', :controller => :mycontroller,
:action => :index
For Rails 2.x:
map.connect '/:city(/:section(/:subsection))', :controller => :mycontroller,
:action => :index
For Rails 2.x you can use
map.my_route '/:my_param', :controller => :mycontroller, :action => :index
Then in your controller you can access
params[:my_param]
If you want links just go with
my_route_path(:my_param => "mytekst")

How can I route to different controllers with the same URL format in Rails?

I have two different models that can show up in a category. In my routes.rb, I would like to have something like this:
ActionController::Routing::Routes.draw do |map|
map.top_list ':category/:foo', :controller => 'foo', :action => 'show'
map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end
This works fine when I load a URL like "/some-category-name/some-foo-name", where "some-foo-name" can be loaded by the FooController like so:
class FooController < ApplicationController
def show
#foo = Foo.find_by_url! params[:foo]
end
end
But when I try to request a Bar, like "/some-category-name/some-bar-name", I get a "ActiveRecord::RecordNotFound in FooController#show". I know that I can solve this problem by requiring that all Foo names start with "foo-" and all Bar names start with "bar-", then defining routes like this:
ActionController::Routing::Routes.draw do |map|
map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => /^foo-/ }
map.top_list ':category/:bar', :controller => 'bar', :action => 'show', :requirements => { :name => /^bar-/ }
end
But forcing this restriction on names is quite suboptimal. I found the following, which looks like it might work for me: Different routes but using the same controller for model subclasses in Rails. However, I don't quite follow the example, so I don't know if this would solve my problem. It is also not great that my Foo and BarController would have to inherit from CategoryController.
One thought that occurred to me is that I could try to look up the Foo, then fall back to the BarController if that fails. I can easily do this in the FooController and redirect to the BarController, but this is not really OK, since all the requests for Bars will be logged as FooController#show in the Rails log. However, if I could somehow configure the routes to call a method to determine what to route to based on the basename of the URL, I could get the behaviour that I need; e.g.
ActionController::Routing::Routes.draw do |map|
is_a_foo = Proc.new {|name| Foo.find_by_url! name && true }
map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end
Is there any way to do this in bog-standard Rails 2?
I ended up writing a little Rails plugin that allows me to write a route like this:
map.with_options :category => /[-A-Za-z0-9]+/ do |m|
# Since both foos and bars can appear under category, we define a route for foo that only matches when
# the foo can be found in the database, then fall back to the bar route
m.foo ':category/:foo', :controller => 'foo', :action => 'show', :conditions => {
:url => { :find_by_url => Foo }
}
m.bar ':category/:bar', :controller => 'bar', :action => 'show'
end
Until I get permission to open source the code, I have to leave the implementation as an exercise for the reader, but here's how I got there:
Monkey-patching Rails: Extending Routes #2
Under the hood: route recognition in Rails
The Complete Guide to Rails Plugins: Part II
I'll update this answer with a github link when I can. :)
This seems wrong since you are using the same route to two controllers. Rails will choose one only as you noticed.
It would be nicer to use something like this:
map.top_list ':category/foo/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
map.top_list ':category/bar/:bar', :controller => 'bar', :action => 'show'
Since in provided routes you give two params each time. It does not matter that you name them differently. It just matches both.

creating a link_to for custom route

I have a custom route (if I am doing that correctly, this is the first time I have done this) that looks like this:
map.connect 'purchases/type/:type', :controller => 'purchases', :action => 'index'
so I want to create a link_to that would use that url /purchases/type/(somenumber)
or I am completely open for a better way to do it.
Edit:
I am trying to use a category (type) to filter on the index. So if I click the link that would be /purchases/type/1 that would show all the items from type 1. I dont want this in the show, and I could do it with /purchases/?type=1, but im trying to make the urls look better.
Untested but I believe this is what you want...
map.purchase_type 'purchases/type/:type', :controller => 'purchases', :action => 'index'
Then
link_to 'foo', purchase_type_path(:type => 'your_type')
Good luck.
Based on http://www.tutorialspoint.com/ruby-on-rails-2.1/rails-routes.htm (section "named routes"), I'd try the following:
map.purchases_for_type 'purchases/type/:type', :controller => 'purchases', :action => 'index'
And I assume you'd then call it with link_to 'link text', purchases_for_type(#type_param)
For reference, I'll include the Rails3 way to do it:
match '/purchases/type/:type' => 'purchases#index', :as => "purchases_for_type", :via => "get"
Or better yet (RESTful):
match '/type/:type/purchases' => 'purchases#index', :as => "purchases_for_type", :via => "get"
You'd then call it with link_to 'link text', purchases_for_type(#type_param)

Creating a Custom Rails Route

I am attempting to create a custom route in rails and am not sure if I am going about it in the right way. Firstly I have a RESTful resource for stashes that redirects to mystash as the controller:
map.resources :stashes, :as => 'mystash'
site.com/mystash goes to :controller => 'stashes', :action => 'show'
Which is what I want. Now is where it gets somewhat confusing. I would like to be able to add conditional params to this route. Ultimately I would like to have a route that looks like this:
site.com/mystash/zoomout/new/quiz_on/
I have places this in routes:
map.connect 'mystash/:zoom/:nav_option/:quiz',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash/:zoom/:nav_option',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash/:zoom',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash',
:controller => 'stashes',
:action => 'show'
My routes have ended up looking like this in the browser:
site.com//mystash/zoomin?nav_option=New&quiz=quizon
and this is what one of my links looks like:
<%= link_to "In", stash_path("zoomin", :nav_option => #nav_option, :quiz => #quiz) %>
Any help is appreciated, I am pretty new to custom routes!
You should be giving these routes different names instead of the default, or you should be specifying your route with a hash and not a X_path call. For instance:
map.stash_zoom_nav_quiz 'mystash/:zoom/:nav_option/:quiz',
:controller => 'stashes',
:action => 'show'
map.stash_zoom_nav 'mystash/:zoom/:nav_option',
:controller => 'stashes',
:action => 'show'
Keep in mind that when you declare a named route, the parameters in the path must be specified in the X_path call with no omissions, and not as a hash.
link_to('Foo', stash_zoom_nav_quiz_path(#zoom, #nav_option, #quiz))
link_to('Bar', stash_zoom_nav_path(#zoom, #nav_option))
The alternative is to not bother with named routes and let the routing engine figure it out on its own:
link_to('Foo', :controller => 'stashes', :action => 'show', :zoom => #zoom, :nav_option => #nav_option, :quiz => #quiz)
If you're uncertain what routes are defined, or how to call them, always inspect the output of "rake routes" very carefully. You can also write functional tests for routes with the assert_routing method.

make dynamic routes from content

I am trying to setup dnamic routes in my rails application.
i.e.
I have a acts model that has a name attribute.
name:string.
What I a trying to do is use that name as my url.
in my route if have
map.connect 'blacktie/:id', :controller => 'acts', :action => 'show', :id => 3
That takes me to http://0.0.0.0:3000/blacktie
I know that i can do something along the lines of
def map.controller_actions(controller, actions)
actions.each do |action|
self.send("#{controller}_#{action}", "#{controller}/#{action}", :controller => controller, :action => action)
end
Just not sure if it is even possible.
Add the following to the bottom of your config/routes.rb
map.connect '*url', :controller => 'acts', :action => 'show_page'
Then define the following in app/controllers/acts_controller.rb
def show_page
url = params[:url]
if Array === url
url = url.join('/')
else
url = url.to_s
end
# you now have the path in url
#act = Acts.find_by_name(url)
if #act
render :action => :show
else
redirect_to some_error_page, :status => 404
end
end
A few gotchas with the above approach.
The route is a catch all. You will be trapping everything that doesn't match a route above it. So make sure it's last and make sure you are ready to handle 404s and the like.
The :url param is an array or a string depending on the route coming in. For example /blacktie/night will be an array with a value of ['blacktie', 'night']. That's why I joined them with in the beginning of show_page. So your find_by_name function could be really smart and allow for nested acts and the such.
Hope this helps.
OR...
Add this to routes (at the bottom):
map.connect ':name', :controller => "acts", :action => "show_page",
:requirements => {:name => /[\w|-]*/}
This tells rails to send anything matching the requirements to your handler. So your show_page would be like the following:
def show_page
#act = Acts.find_by_name(params[:name])
if #act
render :action => :show
else
redirect_to some_error_page, :status => 404
end
end
This gets rid of the some of the gotchas but gives you less options for nesting and the like.

Resources