Rails - Pointing form to controller action - ruby-on-rails

I have a simple controller "Users" which has a show action (for each user). Now, I want to have a textbox on the home page which allows to search for a specific user by username.
So on show action, I accept a username param and I respond with the user object. I've seen form_for() but it seems I need an instance of #user which I don't quite understand, since I am only just retrieving on the next request. I've seen tutorials but they seem to have it under index controller action but I really want it to point to users#show so it will use the assets, template, url path, etc.
So I want a form that would just perform a get request with /users/. Am I just missing something simple?

You don't need instance if you use form_tag. You can read more about form_tag here
Example:
<%= form_tag("/search", method: :get) do %>
<%= text_field_tag :user %>
<%= submit_tag "Search" %>
<% end %>

The html snippet from #Mihail well help you but the example given will require you to create a custom route for the search action.
Add in your routes:
GET '/search', to: 'users#show'
Then in your users show action attempt to look up the user by calling:
#user = User.find_by(id: params[:user])
Be sure to not use User.find, because you will get an error when the user doesn't exist. The last thing you'll need to do is make sure your users/show template is able to handle the case when #user is nil, or instead render some sort of custom user_not_found page instead of the users/show if #user is nil.

You can use form_tag instead of form_for. Also, all the form helpers have their _tag equivalents. I.e. select_tag instead of f.select and so on. And don't forget the case when user could not be found, You have to deal with it somehow.

I'll just add in what I did.
As advised, I've added a route for /search on config/routes.rb.
get '/search', to: 'users#search'
Then on UsersController, I've added a redirect on search action.
def search
#username = params[:search]
redirect_to "#{users_path}/#{#username}"
end
This would now allow me to reuse whatever I have under users#show (templates, url and everything). From what I understand, this will return a 301/302 that would cause another HTTP request so this is not at all optimal. It would probably be cleaner to just share functionality between show and search.

Related

Tyring to add followers to microposts, error: 'missing parameter microposts'

I have been trying to add followers to microposts with the following controller:
microposts_controller.rb :
def follow
#user = current_user
set_micropost
if #micropost.user_id != current_user.id
#micropost.followed += 1
#micropost.save
end
end
The thing is there is always a 'missing parameter' error 'microposts'. Meaning I am not passing a #micropost to the controller. The
<%= link_to 'Follow' uses micropost_new_path(micropost), method: 'follow' %>
So I don't know what I'm doing wrong. Defined the appropriate routes (the error states it is a microposts#create error, and I'm not sure why.
There's likely a few things that aren't quite right here, but the first one that stands out is that it looks like you've misunderstood what the method option for link_to is for. Here, "method" doesn't mean the name of the method your controller, it means the HTTP method to use for the request (such as POST, PUT, DELETE and and so on.) So it's likely this link is unintentionally calling the create action instead of the follow action.
For adding additional actions take a look at the section in the routing guide on Adding More RESTful Actions
As a brief example for creating a method to add followers:
If you have an micropost defined as a resource in your routes:
resources :microposts
and would like a new action (such as "follow") that applies to individual microposts you can update your resource to be:
resources :microposts do
post 'follow', on: :member
end
You can then add a follow method in your microposts_controller.rb, similar how you've done already. After editing your routes this will also provide a follow_micropost_path helper function that can be used to link to this action.

Search box to find user in rails

I'm somewhat new to rails. I'm going through making the classic twitter clone right now. I want to have a search bar on my homepage that allows the user to search for a twitter handle, and if the handle exists, it will send the user to the show page for that twitter handle.
I've been following a RailsCast on how to implement a simple search, but instead of doing it on the index like the video, I want to do it on the show action. I've run into some problems though. The form sits on my user index view.
Here is the error:
ActionController::UrlGenerationError in Users#index
Showing c:/Sites/Projects/twitterapp/twitter/app/views/users/index.html.erb where line #2 raised:
No route matches {:action=>"show", :controller=>"users"} missing required keys: [:id]
Here is the form:
<%= form_tag(user_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
Here is my show action:
def show
#user = User.search(params[:search])
end
And here is my search method in my user model:
def self.search(search)
if search
find(:all, conditions:['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
Actually you cannot use the show method as a search result finder. Because according to the rails convention:
For any resource like users, rails scaffold generates index,new, show, create, update, delete methods based on your routes files.
Thus based on the conventional way, show method always asks for an object. Lets say you are using UserContoller show method. It asks for a user object. Which you haven't provide in the form. that's why :id missing error is given.
I would tell you to do some more learning. And for searching create a different method in a different controller and define that controller method to the routes.rb file. This is the best way to do.
If you still want to use the show method, then change the show methods routing from the routes.rb file. You've to manually declare the show action on routes file.
you are using user_path and path need to inform id from present user
you can do this in action :index but I recommend you to create a action to this
view
<%= form_tag(search_users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
routes.rb
resources :users do
post 'search', :on => :collection
end
users_controller.rb
def search
#user = User.search(params[:search])
end
You should to create a view search.html.erb similar as index.html.erb
As Emu and Breno pointed what causing the problem user_path requires an user id
Solution idea:
Why not just point to users index action? like this:
<%= form_tag(users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
users_controller.rb:
def index
if params[:search]
#user = User.search(params[:search])
end
end
and you can use ajax remote: true to handle the returned user object
Found your question via Google, but the responses and suggestions didn't work for me. Found another solution that did, so seems worth posting here.
"Search and Filter Rails Models Without Bloating Your Controller":
http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/

Rails form that redirects to show page

I'm using Rails 4 and have a Policy model with a field policy_number.
I'd like to create a (search-like) form where you input a policy_number and it redirects you to that Policy's show page. I'm not sure how to go about this, should the form's action be policy_path or something?
Thanks!
The biggest problem here is that the user is inputting the policy number in the search form, so you don't have access to it at the time the form is rendered. Without using JavaScript, you won't be able to go directly to the policy by policy number entered.
Here's a possible starting point, though. Create a PolicySearchController with an index method, add a route for it, and create a simple form.
app/controllers/policy_search_controller.rb
class PolicySearchController < ApplicationController
def index
policy = Policy.where(policy_number: params[:policy_number]).first
if policy.present?
redirect_to policy
else
redirect_to :policies, alert: "No matching policy found."
end
end
end
config/routes.rb
resources :policy_search, only: :index
app/views/policies/index.html.erb
<%= form_tag policy_search_index_path, method: :get do -%>
<%= text_field_tag :policy_number -%>
<% end -%>
Now you can iterate on this to add JavaScript, fuzzy matching, etc. if desired.

Add new route example.com/username while preserving existing routes

Is there a way to have example.com/username while preserving example.com/users/1 route?
To put simply, I just need to link to users's profile via username.
You can add an arbitrary route like this using:
get "/:username" => "users#show", as: :username
This will pass the username as a parameter to the show action.
Then in your views:
<%= link_to user.name, username_path(username: user.username) %>
You'll also need a controller action that knows how to handle the username parameter. If you want to use your existing users controller and preserve your /users/:id URLs too, you could do something like:
def show
#user = if params[:username].present?
User.find_by_username(params[:username])
else
User.find(params[:id])
end
end

Rails redirect_to post method?

redirect_to :controller=>'groups',:action=>'invite'
but I got error because redirect_to send GET method I want to change this method to 'POST' there is no :method option in redirect_to what will I do ? Can I do this without redirect_to.
Edit:
I have this in groups/invite.html.erb
<%= link_to "Send invite", group_members_path(:group_member=>{:user_id=>friendship.friend.id, :group_id=>#group.id,:sender_id=>current_user.id,:status=>"requested"}), :method => :post %>
This link call create action in group_members controller,and after create action performed I want to show groups/invite.html.erb with group_id(I mean after click 'send invite' group_members will be created and then the current page will be shown) like this:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
After redirect_to request this with GET method, it calls show action in group and take invite as id and give this error
Couldn't find Group with ID=invite
My invite action in group
def invite
#friendships = current_user.friendships.find(:all,:conditions=>"status='accepted'")
#requested_friendships=current_user.requested_friendships.find(:all,:conditions=>"status='accepted'")
#group=Group.find(params[:group_id])
end
The solution is I have to redirect this with POST method but I couldn't find a way.
Ugly solution: I solved this problem which I don't prefer. I still wait if you have solution in fair way.
My solution is add route for invite to get rid of 'Couldn't find Group with ID=invite' error.
in routes.rb
map.connect "/invite",:controller=>'groups',:action=>'invite'
in create action
redirect_to "/invite?group_id=#{#group_member.group_id}"
I call this solution in may language 'amele yontemi' in english 'manual worker method' (I think).
The answer is that you cannot do a POST using a redirect_to.
This is because what redirect_to does is just send an HTTP 30x redirect header to the browser which in turn GETs the destination URL, and browsers do only GETs on redirects
It sounds like you are getting tripped up by how Rails routing works. This code:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
creates a URL that looks something like /groups/invite?group_id=1.
Without the mapping in your routes.rb, the Rails router maps this to the show action, not invite. The invite part of the URL is mapped to params[:id] and when it tries to find that record in the database, it fails and you get the message you found.
If you are using RESTful routes, you already have a map.resources line that looks like this:
map.resources :groups
You need to add a custom action for invite:
map.resources :groups, :member => { :invite => :get }
Then change your reference to params[:group_id] in the #invite method to use just params[:id].
I found a semi-workaround that I needed to make this happen in Rails 3. I made a route that would call the method in that controller that requires a post call. A line in "route.rb", such as:
match '/create', :to => "content#create"
It's probably ugly but desperate times call for desperate measures. Just thought I'd share.
The idea is to make a 'redirect' while under the hood you generate a form with method :post.
I was facing the same problem and extracted the solution into the gem repost, so it is doing all that work for you, so no need to create a separate view with the form, just use the provided by gem function redirect_post() on your controller.
class MyController < ActionController::Base
...
def some_action
redirect_post('url', params: {}, options: {})
end
...
end
Should be available on rubygems.

Resources