RoR routing very basic problem - ruby-on-rails

I have Exam controller.
In routes.rb there is "resources :exams"
In controller there are REST-like generated methods.
I want to add my own method there:
def search
#exams = Exam.where("name like ?", params[:q])
end
In view file:
<%= form_tag(search_path, :method => "post") do %>
<%= label_tag(:q, "Szukaj: ") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Szukaj") %>
<% end %>
I know, there is no results presentation yet, it doesn't work at all at this moment (:
When i go to http://localhost:3000/exams/search it's mapping it to show controller and search is a :id paramether then...
How to get http://localhost:3000/exams/search to run the seach controller?

You forgot to add route. Put this in routes.rb, before resources :exams
map.search '/exams/search', :controller => :exams, :action => :search
Note, that resources :exams doesn't generate routes for all public methods of the controller, it generates very specific set of routes. You can find more information in the rails routing guide. (see section 3.2 in particular)

You'll need to add additional parameters to your mapping. You can add "collection" methods like so:
map.resources :exams, :collection => {:search => :get}
When you rake routes, you'll see that it generates something like so:
search_exams GET /exams/search(.:format) {:controller=>"exams", :action=>"search"}
exams GET /exams(.:format) {:controller=>"exams", :action=>"index"}
POST /exams(.:format) {:controller=>"exams", :action=>"create"}
new_exam GET /exams/new(.:format) {:controller=>"exams", :action=>"new"}
edit_exam GET /exams/:id/edit(.:format) {:controller=>"exams", :action=>"edit"}
exam GET /exams/:id(.:format) {:controller=>"exams", :action=>"show"}
PUT /exams/:id(.:format) {:controller=>"exams", :action=>"update"}
DELETE /exams/:id(.:format) {:controller=>"exams", :action=>"destroy"}

Related

How do I create a Rails form that submits to a RESTful action?

With Rails 5, how do I create a form that submits to a RESTful action?
In my routes file I have:
resources :people do
collection do
get 'image/:id', :to => "people#image", :as => 'image'
get 'ranks', :to => "people#ranks", :as => 'ranks'
get 'search/:search(.:format)', to: 'people#search'
end
end
So I'm wondering how to construct a form that submits to the "search" action.
I tried the following:
<%= form_tag(people_search_path) do %>
<%= text_field_tag :search %>
<%= submit_tag 'Search' %>
<% end %>
But this results in the following error:
undefined local variable or method `people_search_path' for #<#<Class:0x00007ff2afc59168>:0x00007ff2afc50f68>
If you do rake routes, you'll see that your search path has no name:
image_people GET /people/image/:id(.:format) people#image
ranks_people GET /people/ranks(.:format) people#ranks
GET /people/search/:search(.:format) people#search
people GET /people(.:format) people#index
POST /people(.:format) people#create
new_person GET /people/new(.:format) people#new
edit_person GET /people/:id/edit(.:format) people#edit
person GET /people/:id(.:format) people#show
PATCH /people/:id(.:format) people#update
PUT /people/:id(.:format) people#update
DELETE /people/:id(.:format) people#destroy
If, instead, you do:
resources :people do
collection do
get 'image/:id', :to => "people#image", :as => 'image'
get 'ranks', :to => "people#ranks", :as => 'ranks'
get 'search/:search(.:format)', to: 'people#search', as: :search
end
end
You'll see your path now has a name:
image_people GET /people/image/:id(.:format) people#image
ranks_people GET /people/ranks(.:format) people#ranks
search_people GET /people/search/:search(.:format) people#search
people GET /people(.:format) people#index
POST /people(.:format) people#create
new_person GET /people/new(.:format) people#new
edit_person GET /people/:id/edit(.:format) people#edit
person GET /people/:id(.:format) people#show
PATCH /people/:id(.:format) people#update
PUT /people/:id(.:format) people#update
DELETE /people/:id(.:format) people#destroy
Which you can use as search_people_path.
BTW, I believe that (.:format) bit is unnecessary and you can simply do:
post 'search/:search', to: 'people#search', as: :search
If I were you, however, I would do:
resources :people do
collection do
get 'image/:id', :to => "people#image", :as => 'image'
get 'ranks', :to => "people#ranks", :as => 'ranks'
get :search, to: 'people#search', as: :search
end
end
Which will give you:
image_people GET /people/image/:id(.:format) people#image
ranks_people GET /people/ranks(.:format) people#ranks
search_people GET /people/search(.:format) people#search
people GET /people(.:format) people#index
POST /people(.:format) people#create
new_person GET /people/new(.:format) people#new
edit_person GET /people/:id/edit(.:format) people#edit
person GET /people/:id(.:format) people#show
PATCH /people/:id(.:format) people#update
PUT /people/:id(.:format) people#update
DELETE /people/:id(.:format) people#destroy
Then, I would update your form as Anees Muhammed suggests (I used :terms instead of :search):
<%= form_tag search_people_path, method: :get do %>
<%= text_field_tag :terms %>
<%= submit_tag 'Search' %>
<% end %>
Then, when you submit, you should get something like:
Started GET "/people/search?terms=foo" for ::1 at 2018-01-29 13:49:40 -0800
Processing by People#search as HTML
Parameters: {"utf8"=>"✓", "terms"=>"foo", "commit"=>"Search"}
And you can access your terms by doing params[:terms].
There is, BTW, nothing "non-RESTful" about this. It is, IMO, completely consistent the Guide for adding RESTful actions.
You should use search_people_path instead of people_search_path. But I don't think that change will take you to what you are trying to achieve. Since your route is get 'search/:search(.:format)', to: 'people#search' rails will expect you to provide a value for the keyword :search to build a url like /people/search/yoursearchterm with something like search_people_path(yoursearchterm). For your functionality to work, change the form to
<%= form_tag search_people_path, method: :get do %>
<%= text_field_tag :search %>
<%= submit_tag 'Search' %>
<% end %>
and then in your controller
def search
params[:search]
end
Also the routes to
get 'search', to: 'people#search'
Updated
Since you are trying to submit the form directly to a rest route like search/:search, I believe there is no direct rails way to achieve the same because that's how forms work. But if all you want is just a rest route like yourwebsite.com/search/yoursearchterm when you submit the form, there are some workarounds to achieve this. Either you have to write a piece of js on submitting the form and forward the request to your route with the input value or you can perform a redirect in the controller to the route, like this.
Routes:
resources :people do
collection do
get 'search', to: 'people#search_people'
get 'search/:search', to: 'people#search'
end
end
So, first the form will be submitted to search_people action and then redirect to search with params. In controller
def search_people
redirect_to search_people_path(params[:search])
end
This will then give you a url yoursite.com/people/search/searchterm and then in your search action, you can use the search params.
Hope this helps.

ActionController::MethodNotAllowed (Only get, put, and delete requests are allowed.)

I'm getting this error, ActionController::MethodNotAllowed (Only get, put, and delete requests are allowed.), when I try to submit a form. My route looks like this,
admin.resources :email_launcher
and this is my form_for,
form_for :email_launcher, :url => new_admin_email_launcher_path, :method => :get do |f|
This is not in a form but in the new view, and I'm using HAML. I've google searched all day on the error but never found anything of value. Any help would be great, thanks.
Here's my rake routes for admin_email_launcher
admin_email_launcher_index GET /admin/email_launcher(.:format) {:controller=>"admin/email_launcher", :action=>"index"}
POST /admin/email_launcher(.:format) {:controller=>"admin/email_launcher", :action=>"create"}
new_admin_email_launcher GET /admin/email_launcher/new(.:format) {:controller=>"admin/email_launcher", :action=>"new"}
edit_admin_email_launcher GET /admin/email_launcher/:id/edit(.:format) {:controller=>"admin/email_launcher", :action=>"edit"}
GET /admin/email_launcher/:id(.:format) {:controller=>"admin/email_launcher", :action=>"show"}
PUT /admin/email_launcher/:id(.:format) {:controller=>"admin/email_launcher", :action=>"update"}
DELETE /admin/email_launcher/:id(.:format) {:controller=>"admin/email_launcher", :action=>"destroy"}
routes.rb
namespace :admin do
resources :email_launcher
end
admin/email_launchers_controller.rb
def new
#email_launcher = EmailLauncher.new
end
admin/email_launcher/new
<%= form_for([:admin,#email_launcher]) do |f| %>
...
<%= f.submit %>
<% end -%>
It will POST new email_launcher.
Why do you need to GET the new action? You're already on the new page.

Rails link_to with acts_as_taggable_on_steroids

I am using acts_as_taggable_on steroids and I am having problem with this piece of code that generates a link to a tag:
<%= link_to tag, tag_path(:id => tag.name) %>
when I access the URL:
http://localhost:3000/tags/rails
I get the error:
No action responded to rails. Actions: show
However, this URL works:
http://localhost:3000/tags/show/rails
I have defined the show action in my tags_controller.rb
class TagsController < ApplicationController
def show
#stories = Story.find_tagged_with(params[:id])
end
end
I have the following routes generated by rake:routes :
tags GET /tags(.:format) {:controller=>"tags", :action=>"index"}
POST /tags(.:format) {:controller=>"tags", :action=>"create"}
new_tag GET /tags/new(.:format) {:controller=>"tags", :action=>"new"}
edit_tag GET /tags/:id/edit(.:format) {:controller=>"tags", :action=>"edit"}
tag GET /tags/:id(.:format) {:controller=>"tags", :action=>"show"}
PUT /tags/:id(.:format) {:controller=>"tags", :action=>"update"}
DELETE /tags/:id(.:format) {:controller=>"tags", :action=>"destroy"}
so I know that URL tags/rails points to the route tags/:id, and I've provided an additional param to link_to to assign the tag name as the :id param, but as you can see, it's not working. A forum suggested I use the to_param but I have not Tag model and the book suggested against it. Am I missing anything?
I am following the Sitepoint book Simply Rails 2
EDIT: added working URL, see top
Try adding this to your route resource:
:requirements => { :id => /.*/ }
Shooting in the dark here but should
<%= link_to tag, tag_path(:id => tag.name) %>
be
<%= link_to tag, tag_path(:id => tag.id) %>
or
<%= link_to tag, tag_path(tag) %>
Try this for your link:
link_to tag.name, { :action => :tag, :id => tag.name }
I don't know what version of rails you're using, I'm assuming 3.
Basically, you're using the tag_path which goes off of the id. If you haven't changed anything, that means something like tag/43, tag with id 43. The reason you were suggested to override to_param is in case you want it to go off the name of the tag instead, so something like tag/rails. For that, you do something like this:
class Tag
def to_param
name
end
end
Finally, you will have to change the show action to use the name, not the id. So #stories = Story.find_tagged_with(params[:name]). Then I believe that you will want to create a route to compensate for this, so above your resources :tags, add match "/tags/:name" => "tags#show".
For me it looks like the difference in routes.rb between
resources :tags
and
resource :tags
The first will have as its default index action, the second will not have :index, but it will respond with show on default route.

rails 3 routes: different auto route for model

so i've got a model class named Photoset and a controller named Sets.
ive got resources :sets working for everything except when paths are generated off an instance of the model. for example if i use:
<%= form_for(#photoset) do |f| %>
i get the error:
no route matches {:controller=>"sets"}
ultimately i want all the uris to be .../sets/...(controller name) instead of .../photosets/...(model name)
is there any way to do this and still be able to use the helpers?
--EDIT--
heres my rake routes output:
sets GET /sets(.:format) {:controller=>"sets", :action=>"index"}
POST /sets(.:format) {:controller=>"sets", :action=>"create"}
new_set GET /sets/new(.:format) {:controller=>"sets", :action=>"new"}
edit_set GET /sets/:id/edit(.:format) {:controller=>"sets", :action=>"edit"}
set GET /sets/:id(.:format) {:controller=>"sets", :action=>"show"}
PUT /sets/:id(.:format) {:controller=>"sets", :action=>"update"}
DELETE /sets/:id(.:format) {:controller=>"sets", :action=>"destroy"}
that all works just dandy, the problem is when i try to build a form off an instance of the model. I understand that rails has no way of knowing that im trying to tie the Photoset model directly with the Set controller, but I don't know how to specify that.
You have a Photoset model, Sets controller and urls need to be in form /sets/1/edit.
resources :sets, :as => "photosets"
Works with a simple form like this:
<%= form_for(#photoset) do |f| %>
<%= f.text_field :title %>
<%= f.submit "Save" %>
<% end %>
You should set
resources :photosets, :as => "sets"
which allow you to use photosets_path, photoset_path, new_photoset_path, etc... but shows the url as sets
See here if you need more info

Link redirects to "show" action instead of the indicated in routes.rb

I'm working with Ruby on rails 2.3.4 and I'd like to have a link that executes an action when clicked.
The relevant part of the routes.rb file looks like this:
map.search_filter_relevance "/anuncios/buscar", :controller => 'announcements', :action => 'search_filter_relevance'
My view(it's the model's index page) looks like this:
<%= link_to 'MÁS RELEVANTES', search_filter_relevance_path %>
And my controller looks like this:
def search_filter_relevance
raise params.inspect
unless params[:announcements].nil? or params[:announcements].empty?
#announcements = params[:announcements].order_by_featured
end
end
The problem is that when I click the link I get an error due to some null value in the Show action! I'm not accessing that action at all...why is executing it?
EDIT:
Here is the routes output:
search_filter_relevance_announcements GET /anuncios/search_filter_relevance(.:format) {:controller=>"announcements", :action=>"search_filter_relevance"}
announcements GET /anuncios(.:format) {:controller=>"announcements", :action=>"index"}
POST /anuncios(.:format) {:controller=>"announcements", :action=>"create"}
new_announcement GET /anuncios/new(.:format) {:controller=>"announcements", :action=>"new"}
edit_announcement GET /anuncios/:id/edit(.:format) {:controller=>"announcements", :action=>"edit"}
announcement GET /anuncios/:id(.:format) {:controller=>"announcements", :action=>"show"}
PUT /anuncios/:id(.:format) {:controller=>"announcements", :action=>"update"}
DELETE /anuncios/:id(.:format) {:controller=>"announcements", :action=>"destroy"}
search /anuncios/buscar {:controller=>"announcements", :action=>"search"}
power_search /anuncios/buscar {:controller=>"announcements", :action=>"power_search"}
Try this code in your routes.rb file
map.resources :announcements,:collection=>{:search_filter_relevance=>:get}
And, comment the below line of code in your routes.rb file
map.search_filter_relevance "/anuncios/buscar", :controller => 'announcements', :action => 'search_filter_relevance'
When you go into console and
rake routes
can you paste all the lines that have search_filter_relevance as well as anuncios ?
Updated:
Since you want to call the search_filter_relevance action in the announcements controller you will need to use the listed route search_filter_relevance_announcements_path
<%= link_to 'MÁS RELEVANTES', search_filter_relevance_announcements_path %>
One other option would be to specify the controller and action manually
<%= link_to 'MÁS RELEVANTES', {:controller => "announcements", :action => "search_filter_relevance"} %>

Resources