Changing a Routes Key ID, from /threads/ID to /threads/uuid - ruby-on-rails

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.

Related

route is redirecting to wrong path

This is what my routes currently look like:
which gives
On my homepage I have a create vacancy button
<%= link_to "plaats", new_employer_vacancy_path(:employer_id)%>
Which should be linked to the line from the first image
get '/employers/:employer_id/vacancies/new', to: 'vacancies#new', as: 'new_employer_vacancy'
In the vacancies_controller#new - create I have:
def new
#vacancy = Vacancy.new
#employervacancy = Employervacancy.new
end
def create
#vacancy = Vacancy.create(vacancy_params)
createEmployervacancy
redirect_to employer_vacancy_path(current_employer, #vacancy)
end
def createEmployervacancy
#employer = current_employer
Employervacancy.create(vacancy_id: #vacancy.id, employer_id: #employer.id)
end
But whenever I click the button I get redirected to some other method in my vacancies_controller that is totally irrelevant.
How is this even possible? Don't I clearly define that when that path is clicked he should go to vacancies#new? and not to vacancies#show_specific_employer_vacancies?
EDIT
After following the answers I am indeed being linked to the correct route.
First, it gave me this error.
After trying to pass the current_employer.id instead of #employer like suggested I got following error:
For your routes, you'd better to change into nested route for easily maintaining routes.
Remove these codes:
get '/employers/:employer_id/vacancies/:id', to:"vacancies#show_specific_employer_vacancies", as: "employer_vacancy"
get '/employers/:employer_id/vacancies/edit/:id' ...
get '/employers/:employer_id/vacancies/index' ...
get '/employers/:employer_id/vacancies/new' ...
path '/employers/:employer_id/vacancies/:id' ...
change into:
resources :employers do
resources :vacancies
end
Try to use basic routes here because you use standard simple form url. For example:
<%= simple_form_for(#employee, #vacancy) %>
The simple_form_for will generate url well if you use nested routes above.
Finally, in your link you have to add #employer_id
<%= link_to "plaats", new_employer_vacancy_path(:employer_id => #employer_id)%>
I hope this help you
Your router cannot tell the difference between your employer_vacancy and new_emplyer_vacancy routes because the :id parameter accepts anything. Because of this, when you point your browser to "/employers/5/vacancies/new", the route is taking your employer_vacancy route and assigning {:employer_id => 5, :id => "new"} instead of going to your new_employer_vacancy route (because routes are first-come-first-serve).
To correct this, add a constraint to your first route to ensure that only numbers (and not the string "new") is accepted into the employer_vacancy route:
get '/employers/:employer_id/vacancies/:id',
to: 'vacancies#show_specific_emplyer_vacancies',
as: 'employer_vacancy',
constraints: { id: /\d+/ } # <- This line
As Wes Foster said rails router is trying to find a first match.
It means that given a path /employers/999/vacancies/new your router looks through the routes and when it sees get '/employers/:employer_id/vacancies/:id he thinks that this route matches. So :employer_id is 999 and :id is new.
I'd suggest to put the route with :id at the end of employers routes:
...
get '/employers/:employer_id/vacancies/new'
...
get '/employers/:employer_id/vacancies/:id'
Btw this is better than adding a constraint because:
It is easier
It doesn't pollute routes file
Later you may want to change ids to be hashed or alphabetic and then you'd have to change the constraint

Rails 3 Finding the right :id in a controller using a specific route

I have my routes arranged so that when visiting the site the :id is displayed before the slug like so
match "/causes/:id/:slug" => "causes#show", :as => :cause, :via => 'get'
But I also have a nested attribute called "post" that belongs to causes like so
match "/causes/:id/:slug/posts" => "causes#posts", :via => 'get', :as => :posts
When I use this, everything works great for the causes, but not for the posts.
If I use
#post = Post.find(params[:id])
in causes or posts controller it always looks for the ID of the causes, and not the :id of the posts. So if the post :id is 9, and the cause :id is 1, and I use
#post = Post.find(params[:id])
it will always look for post[1] and not 9 or whatever the post id really is.
What am I doing wrong? Is there a way to make this work in the routes, or maybe a different way to find the id of a nested object in the controller?
I need the route to be the way I have it set up, :id/:slug...
rake routes information:
cause GET /causes/:id/:slug(.:format) causes#show
edit_cause GET /causes/:id/:slug/edit(.:format) causes#edit
PUT /causes/:id/:slug(.:format) causes#update
posts GET /causes/:id/:slug/posts(.:format) causes#posts
POST /causes/:id/:slug/posts(.:format)
PUT /causes/:id/:slug/posts(.:format) causes#update_post
DELETE /causes/:id/:slug/posts(.:format) causes#destroy_post
causes GET /causes(.:format) causes#index
POST /causes(.:format) causes#create
Any help would be great.
To solve your immediate problem, you'll want to add something like this to routes.rb
# config/routes.rb
match "/causes/:cause_id/:slug/post/:id" => "causes#update_post", :via => 'put', :as => :update_post
And then you can generate the URL in your views like this...
link_to 'Update this post', update_post_path(#cause, #post)
...and access the parameters in your controller as params[:id] (for the post) and params[:cause_id] (for the cause).
More generally, though, the way you are specifying your routes is pretty cumbersome, and I suspect you're making your life harder than it needs to be. If this were me, I would do something like
# config/routes.rb
resources :causes do
resources :posts
end
This would accomplish something pretty close to what you have now, the main difference being that it wouldn't contain slugs. I'm not sure why you need to have both slugs and IDs, maybe you could just identify your causes by their slugs? Stringex is a good gem for generating slugs, and you can set it so that slugs are guaranteed to be unique.
Here is the section of the Rails guide on nested resources
http://guides.rubyonrails.org/routing.html#nested-resources
And here is a Railscast about using slugs with nested resources
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=comments
Hope this helps.
This is because you're using the id of the cause, and if you're doing /causes/:id/posts shouldn't you be doing #posts = #cause.postsanyway?
I would look into the new router syntax for rails 3 if I were you, as there is a nicer way to nest resources http://guides.rubyonrails.org/routing.html
edit:
use the friendly_id gem and nest your resources, to avoid confusion follow REST best practises that resource in question is at the end so
/causes/:slug/posts/:slug

Rails 3 path parameters not part of params

I'm trying to upgrade a Rails 2 app to Rails 3, and I'm really having problems with a route. Here's what I have in the routes.rb file
get 'profile/:login' => 'account#profile', :as => :profile
When I go to http://localhost:3000/profile/MyUsername, it does not correctly add :login to the params hash. See here:
Started GET "/profile/MyUsername?foo=bar" for 127.0.0.1 at Tue Mar 20 21:39:03 -0400 2012
Processing by AccountController#profile as HTML
Parameters: {"foo"=>"bar"}
For some reason, :login is not part of the regular params. On a hunch, I inspected the request.env and found this:
action_dispatch.request.path_parameters"=>{:action=>"profile", :controller=>"account", :login=>"MyUsername"}
I'm totally stumped at this point. Am I missing something? Where should I look next to figure out what is going on here?
Update
I started playing with removing gems and this magically worked. I just commented out gems from the Gemfile until I got the absolute minimal set needed to load the homepage. At that point, the params were exactly as expected. Then I added gems back a few at a time in order to find the cause. I added everything back and...it works now. Crazy, but whatever it takes, I guess.
Looks like you mixed the syntax for 'match' with 'get'. Please try:
match 'profile/:login' => 'account#profile', :as => :profile, :via => :get
Or
get 'profile/:login', :to => 'account#profile', :as => :profile
in your config/routes.rb
When I want to use URL params I always use resource(s) when defining the route. That is convention with Rails 3.x so you can try it.
resources :accounts , :exclude => :all do
member do
get :profile
end
end
This should help or any other way of defining resource URL.
Something like this should work
match 'profile(/:login)' => "account#profile", :as => :profile
If it does not, there may be something else in your routes file that conflicts. Make sure any match ':controller(/:action(/:id(.:format)))' (or similar "match everything" routes) are at the very bottom of your routes file.

Different controller actions for POST and GET requests at the same route in Rails

I want to route the same address, e.g., 'http://server/path' to different controller actions depending on the request type, whether it is a GET or POST request.
How can I do that in Rails?
Thanks!
get "/path" => "controller#get_action"
post "/path" => "controller#post_action"
I think you could do this:
match '/path' => 'controller#action', :via => :get
match '/path' => 'controller#another_action', :via => :post
Generate a resource using the Rails scaffold and you'll see how it should be done:
./script/generate scaffold Person name:string
EDIT
Got downvoted so maybe I should expand my answer. The scaffold demonstrates how to build a RESTful resource. By convention, a POST will map to the create method in the controller, a GET will map to the index method (or the show method if an ID is present), etc. All you need add to your routes.rb is:
resources :people

How to pass params to a block in Rails routes?

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

Resources