How do you send a request with the "DELETE" HTTP verb? - ruby-on-rails

I'd like to create a link in a view within a Rails application that does this...
DELETE /sessions
How would I do that.
Added complication:
The "session" resource has no model because it represents a user login session. CREATE means the user logs in, DESTROY means logs out.
That's why there's no ID param in the URI.
I'm trying to implement a "log out" link in the UI.

Correct, browsers don't actually support sending delete requests. The accepted convention of many web frameworks is to send a _method parameter set to 'DELETE', and use a POST request.
Here's an example in Rails:
<%= link_to 'log out', session_path, :method => :delete %>
You may want to have a look at Restful Authentication.

I don't know about Rails specifically, but I frequently build web pages which send DELETE (and PUT) requests, using Javascript. I just use XmlHttpRequest objects to send the request.
For example, if you use jQuery:
have a link that looks like this:
<a class="delete" href="/path/to/my/resource">delete</a>
And run this Javascript:
$(function(){
$('a.delete').click(function(){
$.ajax(
{
url: this.getAttribute('href'),
type: 'DELETE',
async: false,
complete: function(response, status) {
if (status == 'success')
alert('success!')
else
alert('Error: the service responded with: ' + response.status + '\n' + response.responseText)
}
}
)
return false
})
})
I wrote this example mostly from memory, but I'm pretty sure it'll work....

Correct me if I'm wrong, but I believe you can only send POST and GET requests with a browser (in HTML).

Rails' built in method for links will generate something like this:
Logout
If you don't want to use the Rails' built in method (i.e. don't want the rel="nofollow", which prevents search engine crawlers from following the link), you can also manually write the link and add the data-method attribute, like so:
Logout
Browsers can only send GET/POST requests, so this will send a normal GET request to your Rails server. Rails will interpret and route this as a DESTROY/DELETE request, and calls the appropriate action.

Related

Ruby on Rails: Using user input for calculations on the same page

So, I'm having issues with what I think is a really simple problem. I don't know how to access user input...or perhaps I don't know how to declare a temporary variable in rails.
Anyway, what is the most straight forward way of accomplishing this:
<div>
Enter Group Size: <%= number_field_tag(:group_size)%>
Select Study Site: <%= number_field_tag(:site) %>
</div>
<% if :site > 4 %>
Hello!
<% else %>
Nope!
<% end %>
I suppose I'll need javascript to actually make it work, but for now I just need to know how to use these variables.
Any help would be greatly appreciated. Thanks.
To use them dynamically with ERB, they need to be wrapped in a form and submitted to the server. You can then access them easily with params[:variable_name]. It would probably be cleaner to prepare the message in the controller, but if you don't need to interact with models, it would be more straightforward to use some basic JS to do everything.
As with most things, the solution is a lot more involved:
Ajax
Unlike native apps, Rails relies on the HTTP protocol
HTTP works on requests. You send requests to a server to render a web page; the server responds to the requests. The problem with this is you cannot use Rails with "live" functionality without sending requests to-and-from the server (even "live" applications just keep a perpetual connection open, acting as a single request)
This means if you want to process "live" data (without refresh), you'll have to use a technology to send a request on your behalf. As you noted, this will be ajax:
$(".element").on("action", function(){
$.ajax({
url: "your/end/point"
data: {your: data}
success: function(data) {
alert(data)
}
});
});
Rails
To handle an ajax request in Rails, it's basically the same as handling an HTTP request:
#config/routes.rb
resources :controller do
get :your_endpoint
end
#app/controllers/controllers_controller.rb
def your_endpoint
# perform actions here
respond_to do |format|
format.html
format.js { # handles JS / Ajax request }
end
end
Return
You can then handle the returned data with your JS (Ajax) function. This gives the image that it's working in "real time", but will actually be sending & receiving requests from the server every time

Is it possible to send javascript request using wiselinks?

By default rails (I am using rails 4) sends data-remote requests with the following header:
Accept:*/*;q=0.5, text/javascript, application/javascript, application/ecmascript, application/x-ecmascript
Later in rails code I respond to it using format.js.
I want to migrate to wiselinks, but all data-push requests try to render html.
Is it possible to have default rails behaviour with wiselinks?
I think that you don't unserstand correctly how Wiselinks work. If you use :data => { :remote => true }, then you simply don't need :data => { :push => true }.
Pushing should be used only if you need to update your URL (follow a link).
Ok, I rewritten my controllers to respond to html AJAX requsets instead of using JS requests.
This way I can use wiselinks and it works. Main reason I was using JS response is to set page title with javascript and some other details. But with wiselinks I can use special helper for that.
I think there may be a need in the future to implement some complex behaviour after AJAX request, which will require some JS execution on client side after the response. However this will be possible to implement with page:done event
Thank you all for the help!

Rails 3 - is link_to with parameters secure?

As a general rule of thumb you aren't supposed to trust any input of data from users. If you had a simple link_to with a parameter:
link_to "Click me", test_path(:my_param => "test")
The route might look like: example.com/test?my_param=test
How do I know if the param, or any injected data for that matter, is being filtered properly? The Rails 3 API doesn't specify that it filters data that is passed to the controller, but I want to make sure that the params[:my_param] is filtered securely in the controller before I utilize the params data.
Any thoughts?
Rails framework doesn't secure things by default for GET request. link_to tag is sending a http get request.
If it is a POST/PUT/DELETE request then the Rails uses protect_from_forgery for verify the data sending url
However in your case, its not hard to write a simple method to verify your data for get requests ,
you could write a before_filter to check the sending parameters for a GET request
HTH

ajaxSetup not working with Rails / jquery-ujs

I'm working on a project that makes heavy usage of XHR, and there is some data that needs to be appended to each ajax request in order for the server side to properly keep track of what is going on in the browser.
I have a generic class called xhr_response_handler than I use to handle the callbacks for all ajax requests that is added to all forms, links etc that is making ajax requests. (i.e. where ":remote => true" in rails)
$('.xhr_response_handler')
.on('ajax:success', function(event, data, status, xhr) { xhr_success(data); })
.on('ajax:failure', function(xhr, status, error) { xhr_fail(error); });
Then I try to append the default data sent with each request using:
$.ajaxSetup({
data: {
Foo: <%= #Bar %>
}
});
This works for some of the elements where the ajax settings are configured directly with jquery, but this does not work for some elements created using rails methods like link_to and form_for with :remote => true.
The odd thing is that if I also add
$('.xhr_response_handler').data( 'params', { Foo2: <%= #Bar2 %> } );
Then that works for adding data to the rails generated ajax requests using link_to. What makes it odd is that now all of a sudden the ajaxSettings also works, and in the ajax requests I get both Foo and Foo2 as parameters. But I get no parameters at all when using ajaxSettings by itself.
Furthermore, none of the data to be appended gets serialized into the form data sent in ajax requests generated from the rails form_for method.
Any help would be appreciated.
Rails 3.2.3
Ruby 1.9.3p194
jQuery 1.7.2
I think this behaviour appears because the data hash of $.ajaxSetup is overridden by the ajax calls you do later on. The data has to be merged manually.

Figuring out facebook iFrame clients

I am in the process of building a facebook app that works through iFrame with Ruby On Rails.
My App does serve multiple clients, web, mobile, and facebook. And depending the type of client the UI renders different kind of views.
When the user connects to my app using the facebook page tab, I do get enough information (in params collection) to identify the user came from facebook. Based on that I can customize the views to fit into the iFrame.
But for subsequent requests, because they happens through iframe, there is nothing that tells this is a facebook request (as far as I can tell unless there is something in the headers which I dont know of).
I tried setting a cookie during the first request and that worked great. But the problem is when the user requested my app directly from another browser tab (not through facebook) the cookie was still present and the user ended up seeing the facebook(ised) UI, instead of Normal UI.
Anyone has a solution to this?
I recommend using a different route for Facebook tabs. So if your regular URLs look like this:
/foobar
Then you may want to use something like this for Facebook, by adding the "/fb" prefix (or similar) to your Facebook app's tab or canvas URL:
/fb/foobar
In your routes.rb, you can then pass a parameter to indicate that the user is viewing the page on Facebook, such as "channel=facebook" or "facebook=true".
Your routes.rb might look something like this:
scope 'fb', :channel => 'facebook' do
# ... your routes ...
end
scope :channel => 'web' do
# ... your routes ...
end
With these routes, each request originating from Facebook will automatically have a "channel=facebook" parameter. Of course, you'd still be responsible for making sure to generate the appropriate URLs within your app. You could add :as => 'facebook' and :as => web to the scopes above and use this to generate URLs using dedicated helpers (e.g. facebook_foobar_url, web_foobar_url). But the best way to do this depends a lot on the complexity of your app.
Alternatively, you could also use default_url_options in your controller to add a "channel=facebook" or "facebook=true" parameter to every generated URL if you detect that the current request originated from Facebook (either from the existence of a signed_request or from channel/facebook param you added). Note that this method is deprecated in Rails 3, and I'm not quite sure what (if any) the official replacement is in Rails 3.1 or 3.2.
I needed to solve this with the least amount of intrusion into the existing code.
And am posting my solution here for the benefit of anyone looking to solve similar problem.
I have a unique layout for facebook and this was invoked during the first request (like I mentioned above, the initial request posted from facebook tab has facebook params).
To make sure there was facebook param for subsequent requests, inside the facebook layout, I bound all the form submission and anchor click events to a method that would add a hidden form element and query string param respectively.
this is how the client side code looks like:
<script type="text/javascript">
$(document).ready(function() {
$('form').submit(function(e) {
$('<input>').attr({
type: 'hidden',
id: 'fb_param',
name: 'fb_param',
value: 'true'
}).appendTo($(this));
return true;
});
$('a').click(function(e) {
var newUrl = $(this).attr("href");
if (newUrl.indexOf("?") != -1) {
newUrl = newUrl + '&fb_param=true';
} else {
newUrl = newUrl + '?fb_param=true';
}
$(this).attr("href", newUrl);
return true;
});
});
</script>
Now to handle the server side redirects (typical when you update a resource etc), needed to extend the redirect_to method like so:
/app/conntrollers/application_controller.rb
class ApplicationController < ActionController::Base
def redirect_to(options = {}, response_status = {})
if params[:fb_param] == 'true'
if options
if options.is_a?(Hash)
options["fb_param"] = "true"
else
if options.include? "?"
options = "#{options}&fb_param=true"
else
options = "#{options}?fb_param=true"
end
end
end
end
super
end
end

Resources