Download button for controller action being called twice in rails applicaton - ruby-on-rails

"Why is the 'download_file' method in my Rails controller being called twice even though I only have one 'link_to' button triggering it? I have tried disabling turbolinks and callbacks but the issue persists and two credits are removed each time the download occurs. Here is the relevant code for my 'download_file' method and the link that triggers it:
#dashboard_controller.rb
def download_file
puts "download_file method called"
if current_account.credit < 1
redirect_to root_path, notice: "You don't have enough credits to download this file."
else
puts current_account.credit
current_account.credit -= 1
current_account.save
file = File.open("#{Rails.root}/test_file.zip", "r")
self.response.headers["Content-Type"] = "application/zip"
self.response.headers["Content-Disposition"] = "attachment; filename='test_file.zip'"
self.response_body = file
end
end
#routes.rb
get 'download_file', to: 'dashboard#download_file', as: 'download_file'
#dashboard>show.html.erb
<%= link_to "Download", download_file_path, class: "download-button" %>

Related

Unable to save merged articles

So I'm trying to create a feature for Typo (blogging app) that merges two articles in one. For some reason, I can't manage to save the merged article. I have followed several threads here, read over and over Rails and Ruby docs... And Can't figure out why it doesn't work
Besides finding what's wrong with my code, I'd like to know best solutions to see what's going on 'under the hood', to debug the code. Eg: See when methods are called, what parameters are passed...
Here is my code:
View:
<% if #article.id && #user_is_admin %>
<h4>Merge Articles</h4>
<%=form_tag :action => 'merge_with', :id => #article.id do %>
<%= label_tag 'merge_with', 'Article ID' %>
<%= text_field_tag 'merge_with' %>
<%= submit_tag 'Merge' %>
<% end %>
<% end %>
Controller
def merge_with
unless Profile.find(current_user.profile_id).label == "admin"
flash[:error] = _("You are not allowed to perform a merge action")
redirect_to :action => index
end
article = Article.find_by_id(params[:id])
debugger
if article.merge_with(params[:merge_with])
flash[:notice] = _("Articles successfully merged!")
redirect_to :action => :index
else
flash[:notice] = _("Articles couldn't be merged")
redirect_to :action => :edit, :id => params[:id]
end
end
Model:
def merge_with(other_article_id)
other_article = Article.find_by_id(other_article_id)
if not self.id or not other_article.id
return false
end
self.body = self.body + other_article.body
self.comments << other_article.comments
self.save!
other_article = Article.find_by_id(other_article_id)
other_article.destroy
end
Thanks in advance, and sorry if this is a rookie question :)
You did not mentioned what problem you are facing while saving, you just said you could not manage to save so I can't help you with that unless you provide some stack trace.
I will mention a few things though:
first is in your controller method you have multiple redirection code like redirect_to :action => index without any return from method so I think you will get multiple redirect or render error at some point like when unless executes and redirects but code continues the execution and throws error so try to reduce these redirects or mention it like redirect_to :action => index and return.
Then in model merge_with you are assigning other_article twice, you don't need the second one.
about debugging, you can create some puts line inside code and check it in rails server console to verify that the condition is executed like in controller method after if article.merge_with you can put:
puts "merge sucess"
and check console when merge action is called, if you see "merge sucess" then if block executed.
OR
use byebug like you used debugger. It will stop the execution where it will find the byebug word and will give access to a live session in rails console.
if you put it where you have debugger you can access the console and do the operations manually like run:
article.merge_with(params[:merge_with])
then see what happens. or put before self.save! in model and save it manually in console and check errors like self.errors.messages.
Stack trace is also helpful to see line by line code execution and identify the error.
I will update this if you post any info about what error you are facing

Calling custom action without re-rendering page with Rails link_to

In _follow.html.slim I am trying to make a link to "Add Friend" with this:
= link_to "Add Friend", :controller => "relationships", :action => "req"
I want it to call the method req in the relationships controller while staying on the same page. It currently isn't even calling the method and is returning this error:
No route matches {:controller=>"relationships", :action=>"req", :name=>"Nathan Glass", :age=>"21"}
I'm following this tutorial http://francik.name/rails2010/week10.html and he doesn't define a route for this action. If this error is correct I guess my confusion is why I need a route for this. Otherwise, what is my problem here? Thanks!
class RelationshipsController < ApplicationController
def req
puts "req called"*10
# is setting #current_user since the current_user method already returns #current_user?
#current_user = current_user
#friend = User.find_by_name(params[:name])
unless #friend.nil?
if Relationship.request(#current_user, #friend)
flash[:notice] = "Friendship with #{#friend.name} requested"
else
flash[:error] = "Friendship with #{#friend.name} cannot be requested"
end
end
# render somewhere
end
end
First, you always need to define a route for an action. If you don't, rails doesn't know that your action exists (even if you specify the controller and the action names in your link_to).
For that, you can simply do, in your config/routes.rb file:
get 'relationships/req'
Now, your req action has a path, relationships_req_path (responding to HTTP GET requests).
Then, if you want to call a controller action while staying on the same page, you can do:
link_to "Add as friend", relationships_req_path, remote: true
The remote: true modifies the link behavior(it will works like an ajax call) and renders the relationships/req.js.erb file by default (which can contain nothing). This file allows use to dynamically add/modify content on the current page.

Simple Ruby on Rails Button Action

I have a method in a helper file that I want activated only when a button is pressed.
def add_f01
#count = Count.find_by_user_id(#user)
#car = Car.find_by_user_id(#user)
#car.toggle!(:f01)
#count.increment!(:v01)
end
How do I do it, please?
I've created a working app here: https://github.com/noahc/stackoverflow
Pull it down and play around with it so you can learn how it works.
Essentially you need the following:
#routes.rb
match 'f01', to: 'users#call_app_controller'
# Anywhere in your view. I have it in index.html.erb of users
<td><%= button_to 'change name', f01_path(user: user)%></td>
#Application controller
def add_f01(user)
user.first = "changed in Application Controller"
user.save
end
#users_controller
def call_app_controller
#user = User.find(params[:user])
add_f01(#user)
redirect_to users_path
end

How to destroy polymorphic model? Method destroy missing argument

Im using this gem to add private messages to my application.
https://github.com/LTe/acts-as-messageable/blob/master/lib/acts-as-messageable/message.rb
I`m trying to add remove link to message.
So in my controller i have destroy action:
def destroy
#message = current_user.messages.with_id(params[:id])
if #message.destroy
flash[:notice] = "All ok"
else
flash[:error] = "Fail"
end
end
And in my view i have link: = link_to "Delete", message_path(message.id), :method => :delete
But when im trying to click link i receive: wrong number of arguments (0 for 1)
This is related with this question: Why delete method gives me wrong path? with
The problem is that you're getting all messages, so #message is really multiple messages. You probably want to do:
#message = Message.find(params[:id])
But this may be different for the gem. The gem's documentation has a section on deleting at the bottom of the readme.

link_to_unless_current fails when processing forms with error messages in it with restfull routes

does anyone know how to prevent the failing mechanism of link_to_unless_current?
f.e.: I have my page navigation with
link_to_unless_current "new task", new_task_path
When I click on the link, i come to the new taks path form... And no link is created -> ok.
Then I put incorrect values in the form and submit.
The TasksController processes the "create" action, the validation for the ActiveRecord-model fails because of the incorrect data and the controller renders the "new" action (and includes the error messages for the model).
class TasksController < ApplicationController
def create
#task = Task.new(params[:task])
if #task.save
flash[:notice] = 'task was successfully created.'
redirect_to(tasks_url)
else
render :action => "new"
end
end
end
But here the link gets created!
-> Because of the difference between the urls:
link path = new_task_path
but
posted path = tasks_path with :method => :post
Does anybody know how to cleanly solve this problem?
Thanks
Having a quick look at the source for link_to_unless_current...
...it makes use of current_path? such that you should be able to do something like this:
In a helper...
def current_page_in?(*pages)
pages.select {|page| current_page?(page)}.compact.any?
end
... and then in your view, you can just supply an array of either named_routes or hashes like Shadwell's answer above.
<%= link_to_unless(current_page_in?(new_thing_path, things_path), "add a thing") %>
You get the idea...
UPDATED
Had a think about this... and it'd be great if you could just use it like you'd hoped that the original method worked. Here we compare the supplied named route (or controller + action hash) with the current page AND its referrer.
def current_page_or_referrer_in(options)
url_string = CGI.unescapeHTML(url_for(options))
request = #controller.request
# We ignore any extra parameters in the request_uri if the
# submitted url doesn't have any either. This lets the function
# work with things like ?order=asc
if url_string.index("?")
request_uri = request.request_uri
referrer_uri = request.referrer
else
request_uri = request.request_uri.split('?').first
referrer_uri = request.referrer.split('?').first
end
#referrer_uri always has full path (protocol, host, port) so we need to be sure to compare apples w apples
if url_string =~ /^\w+:\/\//
["#{request.protocol}#{request.host_with_port}#{request_uri}", referrer_uri].include?(url_string)
else
referrer_uri = referrer_uri.gsub(request.protocol, '').gsub(request.host_with_port, '')
[request_uri, referrer_uri].include?(url_string)
end
end
The beauty is that it now lets you just do this (from your example):
<%= link_to_unless(current_page_or_referrer_in(new_task_path), "Add a task") %>
It'll then display if you're on new_task_path OR a page to which it has been sent (such as the create page
You can do it with link_to_unless instead of link_to_unless_current:
link_to_unless(controller_name == 'tasks' &&
(action_name == 'new' || action_name == 'create'),
new_task_path)

Resources