How to pass params to a block in Rails routes? - ruby-on-rails

I am using Rails 3. And I'm wondering how to pass params to some blocks in routes.rb.
What I'm trying to do is to make a catch all route, that check from slugs database the model name of it by the id.
After getting the model name i pluralize it to get the controller name.
match '/:id', :controller => proc { Slug.find_by_iid(params[:id]).model.pluralize }, :action => :show
The table slugs
model iid
----- -----
post 4d2c7de0c5abe7f8a9000007
item 4d2c7de0c5abe7f809000004
When I try to access some pages like /4d2c7de0c5abe7f8a9000007 I got this error:
Started GET "/4d2c7de0c5abe7f8a9000007" for
127.0.0.1 at 2011-01-12 00:04:31 +0200
ActionController::RoutingError (wrong constant name #<Proc:0x0000010337c310#):
Rendered /Users/amr/.rvm/gems/ruby-1.9.2-p136#rails3/gems/actionpack-3.0.3/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.2ms)
The expected is to point to posts#view with iid: 4d2c7de0c5abe7f8a9000007

proc returns a Proc, but match is expecting a string. You could try adding .call to have the proc return its value. Though I'm not sure if this will end up calling the proc each time or only when routes is loaded...
EDIT
Seems I was way off-base with my earlier response and comments. Maybe something like this?:
match '/:id', :to => proc { |env|
id = env["action_dispatch.request.path_parameters"][:id]
model = Slug.find_by_iid(id).model
controller = [model.pluralize.camelize,"Controller"].join.constantize
controller.action("show").call(env)
}
Though this really ought to be defined in a library and included. Perhaps someone knows a better way?

Putting this in your routes seems really hacky. I would recommend creating a Slugs controller, passing this task onto that, and redirecting to the appropriate controller from there. Assuming your other pages use standard RESTful routes, you could do something like this:
Change route to this:
match '/:id', :controller => :slugs, :action => :show
Slugs controller:
def show
slug = Slug.find_by_iid(params[:id])
redirect_to send("#{slug.model}_url", params[:id])
end

Related

Rails Routes: How to match Rails routes based on a pattern

I have some routes like:
get 'route1' => 'controller#route1', as: 'route1'
get 'route2' => 'controller#route2', as: 'route2'
get 'route3' => 'controller#route3', as: 'route3'
How can I match more routes automatically with this pattern, e.g. 4, 5...
I am not sure how you can handle as part of route. But you can write this code in another way. You can create a route that handle all such routes at the end of your primary route as below:
get '/:route' => 'controller#route_for_all_views'
In your controller you should have this route_for_all_views action, which can handle all pages.
class SomeController < ApplicationController
def route_for_all_views
# handle your views and code with params[:route] here
end
end
I think you can do something like this:
get "/:action", to: "controller", constraints: {action: /route\d+/}
Please see dynamic segments for routes.
(also note that this would raise an exception if your controller doesn't have the method so you might need to use something like method_missing)
This may be a messy solution, but you could also do something like this, which will give you the *_path and *_url url helpers, that you get when you use the :as option.
%w{ route1 route2 route3 route4 route5 }.each do |route|
get route, to: "controller##{route}", as: route
end

Rails always routes an action to show action, and the action it self becomes the id

There's a strange behavior in rails I found recently related to routes and actions, specifically, it's on rails 2.3.5. I have a controller, let's call it Users. In the routes, I declare Users as a resources.
map.resources :users
And within the controller, I have the standard action: index, show, edit, update & destroy. Also, I added other action to fullfil certain requirements.
def generated_pdf_report
# do something
end
The problem is, when I go to page /users/generated_pdf_report, I get this on the console:
Processing UsersController#show (some timestamps) [GET]
Parameters: {"action"=>"show", "id"=>"generated_pdf_report", "controller"=>"users"}
As you can see, the server route the request to method show rather than to method generated_pdf_report. What's interesting, is that I have other controllers having similar action and is working fine.
The solution to the above problem is easy enough, make sure the added feed is above the resources:
map.feed 'users/generated_pdf_report', :controller => 'users', :action => 'generated_pdf_report'
map.resources :users
My question is: Anyone knows why rails behaves like that? The above solution is kind of sloppy, what do you think the best practices for such problem like one mentioned above.
As outlined in the Rails 2 routing guide, you can add collection routes like so:
map.resources :users, :collection => { :generated_pdf_report => :get }
When rails looks at
/users/generate_report
That is exactly the path it would use to show a user whose id was generate_report, so that is what it does, assuming you haven't told it otherwise.
A shorter alternative to what you wrote is
resources :users, :collection => {:generate_report => :get}
Which tells rails to map a GET to /users/generate_report to your generate_report action

Do you have to define controller helpers to get paths for new routes in rails?

I have a Customer model and I want his controller to repond to a find method
I added this in my routes.rb file:
match 'customers/find/:name' => 'mymodel#find' resources :customers
In my controller I have something like this:
def find
#customers = Customer.fin_all_by_name(params[:name])
end
in my views, when I need to create a link for that resource I'm using this:
= link_to 'Find By Name', :controller => "customers", :action => "find", :name => #customer.name
now, I'm trying integration tests with cucumber and I have a problem: I have to create a step definition in my customer_step.rb file for customers having same name:
when /^customers having same name as "(.*)"/
url_encode('/customers/find/' + $1)
now that line doesn't work, it says undefined method `url_encode'
I need to encode that string because if the name contains a space I get obvious errors.
I'm new to ruby and rails and I think I'm missing something here.
Am I following the right pattern to accomplish this search?
Should I define an helper method in my controller to generate search urls?
Is it correct that line I have in my _step.rb file?
I don't want urls to be like this: customers/find?name=test
but: customers/find/test
I just sorted it out, I slightly modified my match expression and added the :as parameter
and this gave me the possibility to call find_path() helper method
match 'customers/find/:name' => 'customers#find', :as => :find
Is this correct?
Using :as should indeed create a route helper for you. If you want to get a list of your matched routes, to which controller/action they route, and the name of the route helper, use rake routes in console.

Changing a Routes Key ID, from /threads/ID to /threads/uuid

I have a threads model, that has Thread (id, uuid, title...)
Here is the path:
When the controller redirects: http://localhost:3000/threads/828b9a2ffc
In the logs I then see:
Started GET "/threads/828b9a2ffc" for 127.0.0.1 at Sat Jul 09 17:24:02 -0700 2011
Processing by ThreadsController#show as HTML
Parameters: {"id"=>"828b9a2ffc"}
The issue here is I don't want 828b9a2ffc to be the ID, I want it to interpruted as uuid in the parameters, so I should see:
Parameters: {"uuid"=>"828b9a2ffc"}
How can that be made possible in the routes file?
Thanks
Maybe I'm missing something obvious but what's wrong with using a route like:
match '/threads/:uuid' => 'threads#show', :via => :get
in your routes.rb?
The simplest way would probably be:
match 'threads/:uuid' => 'threads#show', :as => :thread
That will make the last part of the url available as params[:uuid]. If you already have
resources :threads
defined then just put it above that in your routes file and it will override the threads#show path already defined.
In routes.rb you need something like this:
get 'threads/:uuid', :action => "show", :as => "by_uuid"
This will create a thread_by_uuid_path(thread_object) helper method but if you also have:
resources :threads
... there will still be the standard show route which uses :id and will conflict with the new one you define above. You could define each of the routes manually and deleted the resources :threads line, but I would tend to leave the route alone and change the controller method like this:
def show
#thread = Thread.find_by_uuid(params[:id]) || Thread.find(params[:id])
...
end
... this way the ID can still be used even though you expect the UUID to be passed.

make pretty url with routes.rb

I would like to do something to this effect, I believe:
map.connect 'show/:company_name/:id',
:controller => 'companies',
:action => 'show'
Basically, each time the show action is called, I would like it to take the company_name param and place it into the url as such (show/:company_name/:id)
However, it seems I am using old (rails 2.x routing api) and cannot use map.connect without getting an error. How can I upgrade this?
Is there some way to do this with "match"?
Thanks!
===================
This is the error I see when I try to use map.connect:
undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x103757458>
I think your routes lack a "/" symbol in the first line.
Try this:
match '/show/:company_name/:id' => 'companies#show'
You can check your routes path with command rake routes.
--
Besides, the show action is the default RESTful method in Rails. I'll suggest you change a equivalent word, and reserve "show" action for future or other situation.
In Rails convention, you can write resources :companies, and the path will be /companies/:id using show action.
Some adjustment, in app/models/company.rb
def to_param
self.name
end
So your url will look like http://yourdoamin.com/companies/37signals.
In app/controllers/companies_controller.rb
#company = Company.find_by_name(params[:id])
If I'm understanding your goal, try
match 'companies/show/:company_name/:id' => 'companies#show'

Resources