Rails 3 ajax response is not executed - ruby-on-rails-3

I am creating a simple ajax feature into my application where a user can increase a "like count" through a link.
I have managed to get the link to run my controller code and the browser is getting a response back, but the response is not executed.
The like link is in partial:
.likes
.like_up
= link_to( image_tag('/images/thumb_up.png'), '/like/' + question.id.to_s, :remote => true)
#like_count
= 22# question.likecount
.like_down
= link_to( image_tag('/images/thumb_down.png'), '/dislike/' + question.id.to_s, :remote => true)
And it goes into likes_controller:
def up
#like = current_user.likes.create( :question_id => params[:question_id], :like => true )
respond_to do |format|
format.js
end
end
Then in my up.js.haml I simply have a debug javascript:
alert('2');
and nothing else.
When I press the like link in browser the rails log tels me that it has rendered the js.haml
...
Rendered likes/up.js.haml within layouts/application (104.9ms)
Completed 200 OK in 923ms (Views: 116.3ms | ActiveRecord: 20.1ms)
And when I look at the response the browser gets it certainly looks like the up.js.haml rendered into my application layout where the yield tag is placed.
<div class='content'>
alert('2');
</div>
I suspect that this is not what the browser wants to get back. Also when looking at the firebug console during the ajax request, nothing happens after the request is sent.
Could someone find out why this is not working? Every tutorial and guide I have found does this the same way I'm doing here.

First, make sure the content-type is correct:
curl --head -X POST http://localhost:8080/....
I believe the Content-Type should be text/javascript
Second, I think you want to disable the layout so that the DIV doesn't appear in the output.
def up
#like = current_user.likes.create( :question_id => params[:question_id], :like => true )
respond_to do |format|
format.js { render :partial => 'up', :layout => false }
end
end
Or maybe disable it in the whole controller with
layout false

It happened to me in a simple remote form.
Worked after restarting the server even it was the development environment.
Hope I save someone the hour I wasted.
Best regards.

Related

How to update the database when users download an ActiveStorage blob attachment?

Currently users can download an ActiveStorage blob in my app using the following link:
link_to 'download', rails_blob_path(pj.document.file, disposition: 'attachment')
However, I would like to update an attribute in the database for the associated model to register when the file was first downloaded. This field is called the downloaded_at field.
I have made the following attempt:
Changed the link_to > button_to as I'm updating the model.
Added the appropriate route
Added the following code in the database:
def download
#proofreading_job = ProofreadingJob.find(params[:id])
#proofreading_job.update(downloaded_at: Time.current) if current_user == #proofreading_job.proofreader.user
response.headers["Content-Type"] = #proofreading_job.document.file.content_type
response.headers["Content-Disposition"] = "attachment; #{#proofreading_job.document.file.filename.parameters}"
#proofreading_job.document.file.download do |chunk|
response.stream.write(chunk)
end
ensure
response.stream.close
end
However, this does not do anything except redirect to the #proofreading_job page which is not what I want.
Has anyone done this before and if so how can I accomplish this task.
I think you can also try using your action controller as a proxy, the concept is this:
download the file in your action
check if it is downloaded successfully and other validations
perform clean up operations (in your case the added code in your #3)
send the file back to user using the send_data/send_file rendering method
E.g. in your controller:
def download
file = open(params[:uri])
validate!
cleanup!
send_file file.path
end
Then in your view:
link_to 'download', your_controller_path
Above is just concept and I apologize for only providing pseudo code in advance.
In the end I just used some javascript to capture the click of the button as follows:
td = link_to rails_blob_path(pj.document.file, disposition: 'attachment'),
id: pj.document.id,
download: pj.document.file_name,
class: "btn btn-outline-secondary btn-sm btn-download" do
=pj.document.file_name
i.fa.fa-download.ml-3 aria-hidden="true"
coffee script:
$('.btn-download').on 'click', (e) ->
id = $(this).attr('id')
$.ajax {url: Routes.document_path(id), type: 'PUT'}
routes.rb
resources :documents, only: [:show, :update]
documents_controller.rb:
def update
document = Document.find(params[:id])
authorize([:proofreaders, document])
document.update(downloaded_at: Time.current) if document.downloaded_at.nil?
head :ok
end
This seems to work very well. It updates the database and the user gets the file downloaded to their computer.

Getting controller name from a request.referer in Rails

I know I can use request.referrer to get the full referrer URL in Rails, but is there a way to just get the controller name from the URL?
I want to see if the URL of http://myurl.com/profiles/2 includes "profiles"
I know I can use a regex to do it but I wondered if there was a better way.
Keep in mind that request.referrer gives you the url of the request before the current one. That said, here is how you can convert request.referrer to controller/actionn information:
Rails.application.routes.recognize_path(request.referrer)
it should give you something like
{:subdomain => "", :controller => "x", :action => "y"}
Here is my try which works with Rails 3 & 4. This code extracts one parameter on logout and redirects user to customized login page otherwise it redirects to general login page.
You can easily extract :controller this way. Controller part:
def logout
auth_logout_user
path = login_path
begin
refroute = Rails.application.routes.recognize_path(request.referer)
path = subscriber_path(refroute[:sub_id]) if refroute && refroute[:sub_id]
rescue ActionController::RoutingError
#ignore
end
redirect_to path
end
And tests are important as well:
test "logout to subscriber entry page" do
session[:uid] = users(:user1).id
#request.env['HTTP_REFERER'] = "http://host/s/client1/p/xyzabc"
get :logout
assert_redirected_to subscriber_path('client1')
end
test "logout other referer" do
session[:uid] = users(:user1).id
#request.env['HTTP_REFERER'] = "http://anyhost/path/other"
get :logout
assert_redirected_to login_path
end
test "logout with bad referer" do
session[:uid] = users(:user1).id
#request.env['HTTP_REFERER'] = "badhost/path/other"
get :logout
assert_redirected_to login_path
end
Inside the controller, you have the method controller_name which returns you only the name. In your case, it would return "profiles".
You may also use params[:controller] which returns the same string.

Rendering and re-raising exceptions in Rails controllers

I'm creating an API in Rails and I've run into a situation where I'd like to alert the user that something bad happened by passing them some JSON with an error message. However, I'd also like to re-raise the exception so that Airbrake (formerly Hoptoad) will still catch the error and notify us so that we can look into the problem a bit more.
I'm currently catching the error like so:
begin
if #myobject.update_attributes(:foo => "bar")
render :json => #myobject, :status => :ok
else
render :json => #myobject.errors, :status => :unprocessable_entity
end
rescue Exception => e
render :json => { :errors => { :message => "Uh oh! Something went wrong." } }
raise e
end
The problem is that my client never gets the JSON message since the raise e stops it from rendering and sends it a generic 500 error.
How should I fix this?
[My Solution]
As suggested by Jordan below, I simply call notify_airbrake(ex) in my code any time that I catch the exception. However, I abstracted it slightly by adding the following to my ApplicationController so that I can easily change from Airbrake to something else in the future:
class ApplicationController < ActionController::Base
...
def notify_exception_service(ex)
notify_airbrake(ex)
end
...
end
So, instead of notify_airbrake(ex) I just call notify_exception_service(ex).
From the Airbrake gem documentation:
If you want to log arbitrary things which you've rescued yourself from a controller, you can do something like this:
rescue => ex
notify_airbrake(ex)
flash[:failure] = 'Encryptions could not be rerouted, try again.'
end
The #notify_airbrake call will send the notice over to Airbrake for later analysis. While in your controllers you use the notify_airbrake method, anywhere else in your code, use Airbrake.notify.
You don't have to re-raise the exception in order to log it with in Airbrake.
As I've said on chat, I think you can't becouse of how rails render things.
When you call render, a #_something_render_variable is set, but the page is not directly render, there is still additional call. Raising an exception obviusly block this flow, actually breaking the render of the webpage.
Changing this behaviour is really hard, you must alias the render method and work on it, I had a similar problem.

No request params in constraint when testing route

When I run this in practice it works, but I can't seem to write a working test for my route constraint with rspec.
When the test runs the constraint is triggered, but the request params are empty, thus it does not validate and the test fails.
I am running Rails 3.0.9, rspec-rails 2.6.1 and rspec 2.6.0.
config/routes.rb
match ":param1-unique-:param2" => "controller#index",
:constraints => ParamConstraint.new
lib/param_constraint.rb
class ParamConstraint
def matches?(request)
#request ||= request
valid_param1? && valid_param2?
end
def valid_param1?
#request.params[:param1] == "lorem"
end
def valid_param2?
#request.params[:param2] == "ipsum"
end
end
spec/routing/param_constraint_spec.rb
require 'spec_helper'
describe "param constraint routing" do
it "recognizes route for param1 and param2" do
{ :get => "/lorem-unique-ipsum" }.
should route_to(
:controller => "controller",
:action => "index",
:param1 => "lorem",
:param2 => "ipsum"
)
end
end
Update
If I inspect the request in the constraint I get the following output:
#<ActionDispatch::Request:0x007fee140ff910 #env={
"rack.version"=>[1, 1],
"rack.input"=>#<StringIO:0x007fee1446da48>,
"rack.errors"=>#<StringIO:0x007fee1446e768>,
"rack.multithread"=>true,
"rack.multiprocess"=>true,
"rack.run_once"=>false,
"REQUEST_METHOD"=>"GET",
"SERVER_NAME"=>"example.org",
"SERVER_PORT"=>"80",
"QUERY_STRING"=>"",
"PATH_INFO"=>"/lorem-unique-ipsum",
"rack.url_scheme"=>"http",
"HTTPS"=>"off",
"SCRIPT_NAME"=>"",
"CONTENT_LENGTH"=>"0"
}>
I ran into this same issue today, searching for an answer brought me to this page's question. For what it's worth, I had to resort to writing a request spec instead.
context "passing params that satisfy ParamConstraint" do
before do
visit "/lorem-unique-ipsum"
end
it "should serve up a page with content" do
# replace this with some assertion that gets satisfied by
# pages served up when ParamConstraint.new.matches? returns true
page.should have_selector("html body div#foo")
page.should_not have_selector("html body div#bar")
end
end
context "passing params that DO NOT satisfy ParamConstraint" do
before do
visit "/other-unique-other"
end
it "should serve up a page with different content" do
# replace this with some assertion that gets satisfied by
# pages served up when ParamConstraint.new.matches? returns false
page.should_not have_selector("html body div#foo")
page.should have_selector("html body div#bar")
end
end
This doesn't answer your question, which I take to be "how to test routing constraint", as the proper way would be via a routing spec. But given this gap in how request.params works when you use "should route_to", this is a workaround. A request spec, as opposed to a routing spec, will fill request.params correctly.
Same issue exists years later, with rspec-core 3.4.4, rspec-rails 3.4.2, rails 4.2.6. Don't have time to dig into exactly why...
You can use a request spec as suggested above, but don't use it to test the page contents. Instead, replicate a routing test (route_to) by checking the conversion of URL paths to request params:
RSpec.describe 'routes', type: :request do
describe '/:slug' do
it 'routes correctly' do
get '/test-product-slug'
expect(request.params).to eq(
'controller' => 'product',
'action' => :index,
'slug' => 'test-product-slug'
)
end
end
end

How do I stop my rails application from returning a 304

I have a feeds controller that should NEVER return a 304.
My render call looks like this ( where json_feed_content is a jsonp string );
render :text => jsonp_feed_content, :content_type => "application/json"
I have set caching to false in my production environment:
config.action_controller.perform_caching = false
I have a client script that constantly polls content from this feed, but most of the requests return 304 [Content Not Changed]. Is there a way to prevent my rails application from doing this?
Add this to your controller:
#update last-modified so content is always fresh
headers['Last-Modified'] = Time.now.httpdate
There might be better ways, but this worked for me.