New to rails. Was working on chapter 2 of railsTutorial.
I was pair programming with someone who only uses text based browser w3m. So once we created user resource, we created a new user and tried deleting it but couldn't. I could do it on my Chrome browser though, so thought this may be issue with JS or so.
How do we resolve this?
Rails relies on javascript for links to http DELETE actions.
link_to "Delete Thing", thing_path(#thing), method: :delete
Generates a link with a data-method attribute. The Rails UJS driver then catches the click and turns it into into an AJAX HTTP DELETE request.
method: symbol of HTTP verb - This modifier will dynamically create an
HTML form and immediately submit the form for processing using the
HTTP verb specified. Useful for having links perform a POST operation
in dangerous actions like deleting a record (which search bots can
follow while spidering your site). Supported verbs are :post, :delete,
:patch, and :put. Note that if the user has JavaScript disabled, the
request will fall back to using GET. If href: '#' is used and the user
has JavaScript disabled clicking the link will have no effect. If you
are relying on the POST behavior, you should check for it in your
controller's action by using the request object's methods for post?,
delete?, patch?, or put?.
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Added:
The whole reason we do this is that Rails is built around restful interfaces using the full range of HTTP methods; GET, POST, DELETE, PUT, PATCH. But some browsers can only send GET & POST requests and HTML does not define links which perform other actions than GET.
If no javascript is a requirement you can create a form to send requests other than GET:
<%= form_for #thing, method: :delete do |f| %>
<%= f.submit 'Delete thing' %>
<% end %>
Note that this form uses method="post" for compatability. Rails adds a special hidden input to the resulting form:
<input name="_method" type="hidden" value="delete" />
Which Rails uses to fake the extended set of HTTP methods (DELETE, PUT, PATCH, etc).
If you don't want to use Turbolinks with your Rails(4) application, it's easy! Just do this:
Remove the gem 'turbolinks' line from your Gemfile
Remove the //=require turbolinks from your app/assets/javascripts/application.js
Remove the two "data-turbolinks-track" => true hash key/value pairs
from your app/views/layouts/application.html.erb
Related
So, I have this form declaration:
<%= form_for 'students_list', {:url => update_students_list_stream_url(#stream), :method=>:patch} do |students_list_form| %>
Just as described in API docs, but this leads me to error:
No route matches [POST] "/streams/26/edit-students-list"
So it still tries to post, even though my HTML input has:
<input type="hidden" name="_method" value="patch" />
From Rails guide:
Rails works around this issue by emulating other methods over POST
with a hidden input named "_method", which is set to reflect the
desired method:
I'm quite confused
I was looking for an answer on why rails loaded method patch as post in the rendered form.
If you ended up here looking for that like I did, this is the answer you are looking for:
https://stackoverflow.com/a/46699512/5750078
From https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark:
Rails use Rack::MethodOverride middleware that tweaks the HTTP methods PUT/PATCH to POST for supporting old browsers.
This can happen on Rails API application to unload the middleware for performance. In some cases where you want to call a casual PUT/PATCH request using form_with tag in the views, simply add
# config/application.rb
config.middleware.insert_after Rack::Runtime, Rack::MethodOverride
You'll be better doing this:
<%= form_for #stream do |student_form_list| %>
If you've set up your routes using the standard resources directive, you'll have the following routes:
Of these routes, the update path should just be students_list_stream_path -- not the update_students_list_stream_path you have now.
If you've set up the form_for to use the correct object, it will automatically set the path & method for update.
I'm a Ruby user, trying to make a web service that receives user's active request. I made a button, of which class is a "btn-send-alert". Then after the html code, I put a script function.
<div class="page-title">
<button class="btn-send-alert" style="background-color: transparent;">Help Request</button>
<p>Hello</p><br>
</div>
........
<script>
$(".btn-send-alert").click(function(){
alert('hello!');
<% Smalier.class_alert(#lesson,current_user).deliver_now %>
});
</script>
The problem is, the ruby code just start on its own even before I click this button.
And if I click this button, no email is delivered any longer.
Maybe in some point, I think I'm seriously wrong but I can't find where it is. Is there way that I can make this function work correctly?
Looking forward to seeing the response!
Best
Thanks to Rich, I am now able to write a code that works fine! The below code is that code.
<%= content_tag :div, class: "page-title" do %>
<%= button_to "Help Request", support_path, method: :get, remote: true, class:"btn btn-danger", params: { lesson_id: #lesson.id, user_id: current_user.id} %>
<%= content_tag :i, "wow!" %>
////
def support
#lesson = Lesson.find_by(:id => params[:lesson_id])
current_user = User.find_by(:id => params[:user_id])
mailer.class_alert(#lesson,current_user).deliver_now
end
Above code runs well!
I'm a Ruby user
Welcome to Rails!!
Stateless
Firstly, you need to understand that Rails applications - by virtue of running through HTTP - are stateless, meaning that "state" such as User or Account have to be re-established with each new action.
In short, this means that invoking actions/commands on your system have to be done through ajax or another form of server-connectivity.
Many native developers (native apps are stateful) don't understand how Rails / web apps are able to retain "state", and thus make a bunch of mistakes with their code.
receives user's active request
Even if you understand how to set up authentication inside a Rails app, it's important to understand the virtues of it being stateless... EG the above line means you have to have a user signed in and authenticated before you can send the request.
This forms one part of your problem (I'll explain in a second)
ERB
Secondly, the other problem you have is with the ERB nature of Rails.
the ruby code just start on its own even before I click this button.
This happens because you're including pure Ruby code in your front-end scripts. This means that whenever these scripts are loaded (triggered), they will fire.
The bottom line here is you need to put this script on your server. Otherwise it will just run...
Fixes
1. ERB
<%= content_tag :div, class: "page-title" do %>
<%= button_tag "Help Request", class:"btn-send-alert" %>
<%= content_tag :p, "Hello %>
<% end %>
You'll thank me in 1+ months.
Convention over Configuration means you use as many of the Rails helpers as you can. You don't need to go stupid with it, but the more "conventional" your code is, the better it will be for future developers to improve it.
Another tip - only use HTML for formatting; CSS for styling. Don't use <br> unless you actually want to break a line.
Another tip - never use inline styling - Rails has an adequate asset pipeline into which you should put all your CSS
--
2. Ajax
Secondly, your use of Javascript is incorrect.
More specifically, you're calling a server-based function inside front-end views. To explain this a little more, I'll show you the famed MVC image I post on here a lot:
This is how Rails works (MVC - Model View Controller) - this means that whenever you deal with your application, you have to accommodate a layer of abstraction between the user & your app -- the browser.
By its nature, the browser is not stateful - it stores session cookies which you have to authenticate on the server. You cannot call "server" code in the front-end HTML/JS.
This is why your Ruby code is firing without any interaction, although I'm not sure how it's able to fire in the asset pipeline.
If you want to make it work properly, you'll need to create a controller action to invoke the mailer send function, which you'll be able to do using the following setup:
#config/routes.rb
get :support, to: "application#support", as: :support -> url.com/support
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
respond_to :js, only: :support
def support
Smalier.class_alert(#lesson.current_user).deliver_now
end
end
#app/views/controller/view.html.erb
<%= content_tag :div, class: "page-title" do %>
<%= button_to "Help Request", support_path, method: :get, class:"btn-send-alert" %>
<%= content_tag :p, "Hello" %>
<% end %>
#app/views/application/support.js.erb
alert ("Hello");
Each and every ruby code snippet embedded in ERB runs on server, in order to assemble a valid HTML or Javascript script for browsers to render.
Browsers don't understand ruby script at all, all they can understand is HTML and Javascript.
In your case (I'm supposing you're using rails since you tagged your question with ruby-on-rails), emails are delivered when rails engine is assembling HTML's.
If you want the emails being sent after the users click that button, the correct way is:
Define an action method in some controller, give it an URL (i.e. add a route in config/routes.rb), send email in that action.
When the button on the page is clicked, send an AJAX request to that URL.
I've successfully added ujs in one rails (3.2.6) app. Adding the :remote => true to my form tag allows me to make ajax calls to my js.erb files for dynamic loading of divs, ect..
But in another application on the same machine (Ubuntu 12.0.4), is seems the ujs engine is not working. I'm always getting a Template not Found because the form is sending format => html rather than js. If I force the form to use js format (format => 'js'), it then just renders the js.erb file, rather than calling it via ajax.
In the application.js, I've included the proper headers with the following:
//= require jquery
//= require jquery_ujs
//= require_tree .
The javascript files are included when I actually browse to the primary home page which is using the application layout, which includes the above mentioned javascirpt references. The form in the page is as follows:
<%= form_tag list_path, :remote => true, :id => 'frmBookResults', :method => :post do %>
But although it contains :remote => true, and there's a route established for list_path (the route works, because if I change the list.js.erb to list.html.erb, the view renders), and a method in the controller to handle the request (def list....end), the subsequent list.js.erb is ignored and I get a template not found error, because rails is processing the form request as html, which I can confirm in the log.
I've searched everywhere I could for a solution, but can't figure out why my ujs isn't working for this particular app, when it is nearly identical to my working app, gemset, versions, and configuration.
I've found a couple of other articles on stack overflow where people had the same problem, but no final, working answer was given.
Any help or direction would be greatly appreciated.
It was indeed an issue with the ajax being broken. The onkeyup trigger I was using to submit the form was as follows:
<%= form_tag list_path, :id => 'frmBookResults', :remote => true do %>
<input id='keyword' type='text' onkeyup='document.forms["frmBookResults"].submit();'/>
<% end %>
Note the following:
document.forms["frmBookResults"].submit();
Apparently, submitting the form via javascript was the issue, because when I updated the onkeyup to use a jquery submit as described below, rails ujs kicked in and the ajax calls to my list.js.erb worked:
onkeyup="jQuery('#frmBookResults').submit()"
Thanks for all the feedback mccanff! Your contribution along with other developers from the rails group at linkedIn helped me finally solve my issue.
I have a ruby-on-rails application and I'm now wondering why RoR uses Restful Requests:
eg. if you want delete an ressource it's a best practice to do it with such an HTTP Request:
DELETE /entry/13
you can also do it with with such an normal request:
GET /entry/delete/13 or GET /entry/13/delete
now my question:
when i make a link to such an restful delete operation with the link_to helper
link_to :controller =>:delete, :id => id, :method => :delete
it results in some cryptic javascript:
Delete
So whats the idea behind it?
In my opinion you just exclude non-javascript users.
All versions of Rails < 3 use very obtrusive javascript (and the result is pretty ugly, as you've demonstrated).
The doc suggests the method will fall-back to using GET if javascript is disabled:
:method => symbol of HTTP verb - This
modifier will dynamically create an
HTML form and immediately submit the
form for processing using the HTTP
verb specified. Useful for having
links perform a POST operation in
dangerous actions like deleting a
record (which search bots can follow
while spidering your site). Supported
verbs are :post, :delete and :put.
Note that if the user has JavaScript
disabled, the request will fall back
to using GET. If you are relying on
the POST behavior, you should check
for it in your controller‘s action by
using the request object‘s methods for
post?, delete? or put?.
Either way, I would suggest you create the "destroy" links like so:
# when you have an "entry" object
link_to "Destroy", entry, :method => :delete
# when you only have an "entry" object's id
link_to "Destroy", entry_path(:id => id), :method => :delete
I've been searching for hours now and haven't found anything that helps.
What I want to do:
I need to call the check_login-Method (as below), which needs parameters.
redirect_to check_login_users_url(
:user => {:name => input[1], :password => input [2] },
:stylesheet => 'scaffold',
:method => :get)
The point is that these params are sent in the method-call as in the "Redirected to"-line below.
Processing ApplicationController#execute(for 127.0.0.1 at 2009-12-19 00:28:40) [POST]
Parameters: {"command"=>{"line"=>"log dodo wg"}, "authenticity_token"=> <...token>}
Redirected to http://localhost:3000/benutzer/check_login?method=get&stylesheet=scaffold&user%5Bname%5D=dodo&user%5Bpassword%5D=wg
Completed in 9ms (DB: 0) | 302 Found [http://localhost/execute]
I want to prevent rails from putting the params into the url and pass them hidden instead.
When I send a form created with form_for, there's nothing in the url, so I assume it must be possible.
Please tell me how to do that.
Steps tried
I have tried different "html-verbs": get, put, post - no difference. Though the call of check_login is really short the url-with-params shows up in my Console
create an instance variable and pass it as param (strange, didn't work either)
watch form_for working – without results, got no clue
//edith:
Thanks for all your help so far. Perhaps I didn't specify my problem in enough detail.
I've got a text_field in which I enter short commands (experimentally). Its form calls execute in AppController, which in case of login-data performs redirect_to check_login. I don't need to access a webpage, I simply want to run the method. I liked the idea of putting it into :flash, but I'm wondering if there's a "neater" way to do pass the data hidden.
TL; DR Version: Use a form.
You're never going to be able to fully hide parameters, tools can be used to monitor requests and view the post data/parameters. You could however obfuscate it with an encrypted session. Also it appears that you're sending login info via a GET request, this is generally a bad practice.
That said...
What is going wrong for you is that you're not generating any post data with link_to :method => :post. link_to will use what ever parmas you give it to generate the url. Wheres forms will send all the params generated by the form as POST data to the url generated in the form_for call.
Upon receiving a POST request, Rails will merge parameters routing picks up from from the URL with the post data it receives into one params hash.
As in POST to
http://localhost:3000/benutzer/check_login?stylesheet=scaffold&user%5Bname%5D=dodo&user%5Bpassword%5D=wg
produces the same params hash in the receiving controller action as a POST to http://localhost:3000/benutzer/check_login with the following data:
stylesheet=scaffold&user[name]=dodo&user[pasword]=wg
There will be no distinction in the server log between the two requests.
If you look at what form_for is doing, it submits POST data built from the form inputs to the url generated by the arguments.
form_for #user, create_user_url(:stylesheet => "scaffold") do |f|
f.text_field :name
f.password_field, :password
end
This form will submit the form data to the url generated from the options. In this example the url is: http://localhost:3000/users/create?stylesheet=scaffold and the form data is:
user[name]=name_field_value_at_submit&user[password]=password_field_value_at_submit
link_to will not populate post data for you. You must either do it through a form or with javascript. The link_to documentation contains an example of doing this with javascript. Look for how the destroy with :onclick is handled.
If you really don't like buttons, you could use link_to_function to submit a form.
Replace
:method => :get)
with
:method => :post)
What's the difference between :get and :post? Read Methods GET and POST in HTML forms - what's the difference?
With form_for you create form which is then POSTed to server, that's why you don't see parameters in url - they're in http request body. But it is not possible to redirect user's browser from some action in controller to make another POST - if it would be possible, then I could redirect user to (for example) email change form of gmail or other forms. You can only redirect user to other site, which user's browser then GETs.
If you really don't want to show parameters in url, and both actions are in same application, then you can store those parameters in session or flash store, and retrieve in next request after redirect.
You can use Ajax request to send form data to action :
In some cases its not good to change :get into :post.
For instance in case of Controller's :index action its not good approach to use :post
So Use ajax call to submit form and update only dynamic content of the page.
In js.coffe script file
$ ->
$("#button-id").on "click", (ev) ->
$.ajax
type: "GET"
dataType: "html"
url: "/horoscope_dailies"
data:
date: date
success: (data) ->
$("#index_content").html data
error: (object, error) ->
console.log error
In your controller action
render partial: 'partial_name' if request.xhr?
In your view file:
%div{:id => 'partial_content'}
= render 'partial_name'