Route skips view and calls method straight away - ruby-on-rails

I am still new to rails and running into a rather weird problem (at least from my perspective):
There is a view with a form and a route and a method in a controller. Somehow the method gets called straight away instead of rendering the view, waiting for input and the passing that on to the method.
This is what it kinda looks like:
Controller
class Some::ThisController < ApplicationController
def method_a
variable_a = params[:variable_a].to_time
variable_b = #other stuff
#variable_c = # do stuff with the variable_a & variable_b
end
View (method_a.rb)
= form_tag this_method_a_path do
= text_field_tag :variable_a
= text_field_tag :variable_a
= submit_tag 'Apply'
Routes (some.rb)
The::Application.routes.draw do
namespace :some do
# leave all the unimportant stuff
match this/method_a => this#method_a, :as => :method_a
So what is my issue?
The view is not being rendered - I only get:
undefined method `to_time' for nil:NilClass
When I rename the method the view renders fine.
What do I want?
The view to render, so I can fill out the form and submit it and then have the method return whatever is in #variable_c.
I cant figure out what goes wrong. Maybe it is too late today...

You need two separate controller methods, one to render the view and one to accept the submission of the form. params[:variable_a] will be unavailable when you're in method_a because the form hasn't been submitted yet, it's just being rendered!
Try this:
class Some::ThisController < ApplicationController
def method_a
# nothing, just let Rails render the method_a view
end
# this will accept the submission of the form
def method_b
variable_a = params[:variable_a].to_time # this will now be available because the user has submitted the form
variable_b = #other stuff
#variable_c = # do stuff with the variable_a & variable_b
Add the new method to the routes:
The::Application.routes.draw do
namespace :some do
# leave all the unimportant stuff
get 'some/method_a' => 'some#method_a', :as => :method_a
post 'some/method_b' => 'some#method_b'. :as => :method_b
And now your view will be:
= form_tag method_b_path do
= text_field_tag :variable_a
= text_field_tag :variable_b
= submit_tag 'Apply'
When the user hits the submit button the params :variable_a and :variable_b will be POSTed to the method_b action in your controller and you will be able to call params[:variable_a] or b.
If you don't understand how this works maybe this will help:
User visits the path GET /some/method_a and your app receives the request at the method_a action in your controller and responds by rendering the method_a.html.erb view.
The form is rendered and the user fill out the form and clicks submit sending a new request to the method_b action in your controller. Along with this request the params from the text_fields are included and you can then use the for your computations.
Hope that's clear enough.

Well, this much depends on how you call your route.
Is it a get request? A url-defined "variable_a"? In this case, you are not defining it in you route, so it goes to null...
Or is it a post/patch request? In which case you would most probably have a form posting to a route and hence to a controller method... But you need first to render the form.
So, what you should have is:
A method in your controller to call your form view as 'get'
In your case, as simple as adding in your routes:
match "this/method_a", to: "controller#draw_form", via: 'get'
In your controller
def draw_form
render "method_a"
end
and then, when your form posts to the same url ("this/method_a" in your routes), there will be parameters to be processed by your method_a action in your controller.

Related

Get the actual id of current user without deleting the session? rails will_paginate dilema

I want to get the real id of a User due to a problem in the url for will_paginate in that I need to set it manually, so I want to get the target link for will paginate by a custom renderer method in the view like this,
<%= will_paginate #friends, :renderer => WillPaginateHelper::MyLinkRenderer %>
And the helper is something like,
module WillPaginateHelper
class MyLinkRenderer < WillPaginate::ActionView::LinkRenderer
include SessionsHelper
protected
def link(text, target, attributes = {})
if target.is_a?(Integer)
attributes[:rel] = rel_value(target)
target = "/users" + current_user.id + "/friends?page=#{target}"
end
attributes[:href] = target
tag(:a, text, attributes)
end
end
end
The line target = "/users" + current_user.id + "/friends?page=#{target}" is the important one where I need to set the url for will_paginate anchor links with the current user id.
Problem is that when the helper I am using is ran in the view to set the url, I get a error undefined local variable or method session..., you cannot use sessions hash in helpers so how to get the real id of the current_user to insert into variable. Do I delete/destroy the session get the id and create a new one? The problem is once I delete the session how to get the id once I delete the session and the user reference is deleted.
Reason
I have a friends#index action that is rendered in a div and upon initial call to url the pagination attaches correctly being the url users/:id/friends so each pagination request goes to the correct user and action. But this index view has "Unfriend" form attached to each displayed friend so you can destroy the friendship which goes to the destroy action of the friends controller so markup is eg <form... action="friends/177"...> and on full view reload of the index action from destroy action will paginate attaches to the last known link unless overriden. So when index action is fully rendered again pagination links are friends/177 which give a server error anyway and makes no sense as that record just got destroyed.
I have the current_user variables and id in my destroy action but I can not find a way to get them to my helper method, or simply get the current user id from the session.
Ok, so thanks to max and everyone for helping me. found this post and this answer here which you can do something like,
In your routes
get 'users/:cu_id/friends', to: 'users#index', as: :my_friends
And for the view
<%= will_paginate #friends, params: { controller: :users, :cu_id => current_user.id } %>
This will produce users/:cu_id/friends?page=2... of course :cu_id will be the actual id number, for pagination.
EDIT: Order is important here to if you have any other routes going to the same url eg, users/:user_id/friends when you nest resources, you need to put get 'users/:cu_id/friends',... below this, as rails will build the correct route for you for will paginate but come back in through the friends#index action when paginated and match eg users/1/friends through users/:user_id/friends as it is first in order in routes.rb. here in rails guide it explains how it works.

How to prevent redirect after submitting data through a form

New to rails (programming as a whole), and I'm a little confused about routing.
I have a form in my view that takes someone's email, and then emails them once they hit submit. It works, but after I hit submit I'm being redirected to the view for the method I'm calling in order to mail the form. I don't want to be redirected, I want to stay on the same page. I have attempted using POST and PUT, but both redirect me. Am I using my controller incorrectly?
My controller has this method:
def mail
recipient_email = params[:email]
itinerary_body = params[:body]
x = ItineraryMailer.itinerary("#{recipient_email}", "#{itinerary_body}")
x.deliver
end
And here is the form from my view:
<%= form_tag({controller: "bookings", action: "mail"}, method: "post") do %>
<%= text_field_tag(:email) %>
<%= text_field_tag(:body) %>
<%= submit_tag("Email Me!") %>
My route:
match '/bookings', to: 'bookings#mail', via: 'post'
Thank you!
Since you don't have a redirect_to in your mail action Rails isn't redirecting you anywhere. Instead, it's just trying to render that action's view directly. However, I'm guessing that isn't the view you want rendered.
So, instead of trying to prevent a redirection (which isn't happening anyway) I would suggest that you take the opposite approach: use a redirection to get to the correct view. At the end of your mail action do this:
redirect_to action: :original_action_name
Where :original_action_name is the name of the action that gave you the view you want.
You could also use the render 'action' suggested in a comment, but that can cause problems in some cases where the current action may not have loaded or set up everything needed by the view you want to have rendered. Of course, you could just do that loading/setup in the current action but then you are doing the same thing in two places which isn't DRY.

Render view from application_controller

So, I have search form, and search is avaliable obviously from any page.
I thought that it makes sense, that such action from application controller is placed in layouts/views folder.
But I just don't get- Rails doesn't see it. So I can't do this? How then should I provide action, avaliable from any page?
Code:
def tests_search
#tests=Test.test_search(params[:query])
respond_to do |format|
format.html
end
end
Route:
search_tests GET /search_tests(.:format) application#tests_search
Form:
<%= form_tag search_tests_path, {:id=>'test_search',:method => :get} do%>
Error:
Unknown action
The action 'tests_search' could not be found for ApplicationControllerr
You should create a new search controller. Use rails g controller search index which will create a search controller with a index action (you could also call the action something like result). Then add a search/_form.html.erb file in your search view folder, with the form:
<%= form_tag search_path, {:id=>'test_search',:method => :get} do |f| %>
and render this in your layout/application.html.erb where you want it to be:
<%= render "search/form" %>
This way you have a search form on any pages, that uses the SearchController to handle the search requests.
I would recommend using other controller to do this. It can be for example SearchController even if there will be only one method.
Notice that ApplicationController is controller that every other controller in application inherits from by default. So if it wouldn't be the case, it could make sense, but now every controller will inherit your test_search action, which is not desired.
If your search form will be a partial, then there is no difference whether this is in ApplicationController or in any other controller. You just have to point to right route.
Initially you have to explain yourself the flow. What you need is some partial which is rendered on all pages, and if a user adds some input to it and submits, he gets some output. Right? Good. So you start by creating a new partial somewhere in
app/views/shared/_search.html.erb
Then, you create your route in routes.rb to point to a controller's action. You don't have to place this in application_controller. Instead, create your search_controller.rb and create some action which responds to the form submission there.
Whenever you want to render your search form on other pages, you simply call render partial (more on that here) with something like
<%= render "shared/search" %>
This is good if you created the file above. Make sure your action exists and the name is correct, in your case it should be:
def test_search
...
end
Good luck.

Manage Single Rails form to other controller with create and update action

i'm new of rails. I use rails 3.0.5 .
I have an EMPLOYEE resource, but I would like to manage it with another extern controller (emp_profile_controller).
This extern controller (emp_profile_controller) manages some actions (index, new_employee, create_employee, edit_employee, update_employee ecc.. ) .
My routes for this controller are :
controller :emp_profile do
get 'emp_profile' => :index
get 'emp_edit_profile' => :edit_employee
put 'emp_edit_profile' => :update_employee
get 'new_employee' => :new_employee
post 'new_employee' => :create_employee
get 'emp_list' => :emp_list
end
How can i use one form to handle both Create and Update actions in this controller ?
I tried with :
form_for(#employee, :url => { :controller => "emp_profile"}) do |f|
but it doesn't work.
If i manage only one action at time (create OR update), url_for works, for example :
form_for(#employee, :url => { :controller => "emp_profile", :action => "update_employee" }
but how can i handle both actions with one form ?
Thanks for your availability and I apologize if I asked a stupid question.
EDIT
For now, i solved checking if object exist in the form file, if exist i set a variable with the UPDATE action path, else, i set a variable with the CREATE action path. So in the form_for statement i use url_for with the above variable.
<% if #employee.new_record?
action = "create_employee"
method = "post"
else
action = "update_employee"
method = "put"
end
%>
form_for(#employee, :url => { :controller => "emp_profile", :action => action }, :method => method
I don't think it is the best way but it works and i can use only one form file.
As your model name and controller name are different, you can add this line to your routes
resources :employees,:controller=>"emp_profile",:path=>"emp_profile"
Change the method names of create_employee,update_employee to create and update respectively.
And change your form_for as given below
<%= form_for #employee do |f| %>
....
<% end %>
First of all, if you want to update something, this object should exist.
How do plan to find it out, I don't know (cause there different ways, depends on background).
There are 2 ways of solving this issue.
You can just check if object exist in view file, and if exists, renfer form for update, else for create.
Other way is to do it in controller.
For example:
def create
#employee=Employee.find_by_name('Jack Black') #for example
if #employee!=nil
render :action=> 'update'
else
#employee=Employee.new(:employee)
#employee.save
end
as i understand you want to execute two different actions on the same controller using a form submitting, this is not possible, you can only execute one action using a form submitting,
because the form is reaching to an action controller that action is suppose to render some view at the end of it's execution code, if it was possible to use to actions on form submitting how rails will know which view to render??? (that's why it's not possible).
if you want to do some more code execution at the controller, the right way to it is to call a method with some code in it that you want to execute, that method should be in the model,
because it is a good practice to write all massive chunks of code in the model and leave the controller as light from code as possible :-)
hope this helps.

How to call a controller's method from a view?

I'm developing a small application in Ruby-On-Rails. I want to call a controller's method from a view. This method will only perform some inserts in the database tables. What's the correct way to do this? I've tried something like this but apparently the method code is not executed:
<%= link_to 'Join', method: join_event %>
The method option in a link_to method call is actually the HTTP method, not the name of the action. It's useful for when passing the HTTP Delete option, since the RESTful routing uses the DELETE method to hit the destroy action.
What you need to do here, is setup a route for your action. Assuming it's called join_event, add the following to your routes.rb:
match '/join_event' => 'controllername#join_event', :as => 'join_event'
Be sure to change controllername to the name of the controller you are using. Then update your view as follows:
<%= link_to 'Join', join_event_path %>
The _path method is generated based on the as value in the routes file.
To organize your code, you might want to encapsulate the inserts into a static model method. So if you have a model called MyModel with a name column, you could do
class MyModel
# ...
def self.insert_examples
MyModel.create(:name => "test")
MyModel.create(:name => "test2")
MyModel.create(:name => "test3")
end
end
Then just execute it in your action via:
MyModel.insert_examples
In addition to agmcleod's answer, you can expose controller methods to the view with ActionController::Base::helper_method:
class EventsController < ApplicationController
helper_method :join_event
def join_event(event)
# ...
end
end
But in this case, I think you're best off following his advice and moving this method to the model layer, since it's interacting with the database.

Resources