I have a helper method that has the following bit of code. It's part of an AJAX cart that's displayed on multiple pages on the site
module CartsHelper
def switch_buttons
unless URI(request.referer).path==new_order_path && !current_page?(store_path) \
|| current_page?(new_order_path)
#checkout = true
else
#checkout = false
end
end
end
Here's the cart partial view
<h2> Your Cart</h2>
<table>
<%= render(cart.line_items)%>
<tr class ="total_line">
<td colspan="2">Total</td>
<td class="total_cell"><%= number_to_currency(cart.total_price)%></td>
</tr>
</table>
<% if switch_buttons %>
<%= button_to 'Checkout', new_order_path, method: :get %>
<%= button_to 'Empty cart', cart, method: :delete,
confirm: 'Are you sure?', remote: true %>
<% else %>
<%= button_to 'Cancel Order', store_path, method: :get %>
<% end %>
The URI(request.referer).path is giving a bad argument (expected URI object or URI string) error in all of my tests. It works in the actual browser. I'm guessing it's because the tests don't actual go through the urls so request.referer is nil? Is there some way to set up the test to be able to get through this code?
Two things:
First, this answers your main question:
How do I set HTTP_REFERER when testing in Rails?
Second, it's not a given that request.referer will be set. Most browsers supply the header when you navigate from a previous page; most don't when you hand-enter a URL. HTTP clients can't be assumed to do so overall, and you have to be prepared to get nil from that attribute.
Related
I’m following this tutorial to implement a feature where user can submit Trivia/Interesting Facts.
I want to restrict (edit/delete) functionality to the admin or author of each item.
I’ve created a helper class in .application_controller
def author_of?(resource)
user_signed_in? && current_user.id == resource.user_id
end
But when I'm using this in Turbo-frame with Hotwire I’m getting this error
| ActionView::Template::Error (Devise could not find the Warden::Proxy instance on your request environment.
Here's my code for reference
index.html
<%= turbo_stream_from #stadium, :trivia %>
<%= tag.div id: "#{dom_id(#stadium)}_trivia" do %>
<%= render partial: "trivia/trivium", collection: #stadium.trivia %>
<% end %>
<% end %>
_trivium.html.erb
<%= turbo_frame_tag trivium do %>
<%= trivium.body %>
<% if author_of?(trivium) || admin? %>
<%= button_to "Delete", trivium_path(trivium), class: "btn btn-small btn-danger btn-link mr-2", method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
<% end %>
How can I access the current_user helper in the comment partial to check if the current_user is the author or admin (and should be allow to delete/edit)?
The reason this errors is, as mentioned here, partials with Turbo frames are rendered without any of your global helpers available.
That SO question points to a Hotwire forum here where they discuss possible solutions. The best one in my view is described here.
The gist is:
Pass in current_user as an argument to your partial, rather than accessing it from within your partial
Do your permission check within the partial, rather than the helper
<%= render partial: "trivia/trivium", collection: #stadium.trivia, user: current_user %>
You might consider turning your author_of method into a model method as well, and then you'll have the option to use it within your partial.
I have a AtpRank model containing the first 100 Atp tennis players.
My goal is to create in the view a table listing all tennis players and their attributes, along with a button for each player useful for the user to choose a list of tennis players. The home.html.erb code is below:
<% #atp_ranks.each do |tennis_player| %>
<tr id="tennist-<%= tennis_player.ranking %>">
<td class="atpranking"> <%= tennis_player.ranking %> </td>
<td class="atpname"> <%= tennis_player.name %> </td>
<td class="atppoints"> <%= tennis_player.points %> </td>
<% unless Time.now.month == 12 %>
<td>
<div id="atpenlist_form">
<% if current_user.atpenlisted?(tennis_player) %>
<%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id),
html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
<% end %>
<% else %>
<%= form_for(current_user.atp_selections.build, remote: true) do |f| %>
<div><%= hidden_field_tag :atp_id, tennis_player.id %></div>
<%= f.submit "Choose", class: "btn btn-primary btn-sm" %>
<% end %>
<% end %>
</div>
</td>
<% end %>
</tr>
<% end %>
As you can see, the form uses Ajax having set remote: true in the form_for helper.
Requests are handled by the atp_selections controller. Below is an extract of the create action of this controller:
current_user.atpenlist(tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
The destroy action uses the atpdiscard method instead of the atpenlist method.
In app/views/atp_selections I created the create.js.erb and destroy.js.erb files.
Below is the app/views/atp_selections/create.js.erb file:
$("#atpenlist_form").html("<%= escape_javascript(render('users/atpdiscard')) %>");
$("#atp_count").html('<%= current_user.atp_ranks.count %>');
Each of the app/view/users/_atpenlist.html.erb and app/view/users/_atpdiscard.html.erb partials contain the respective form (the same exact part of the code above starting with form_for).
I have to say that in the original code for the home page I did not explicitly included the entire code for the forms, but I just rendered the partials. This did not work: rails warned me that it could not find the variable or method tennis_player used in the iteration, for some reason to me unknown. So I had to renounce to render the partials and decided to include the entire code.
The issue is now that Ajax does not work: I have to refresh the page to see the results of submitting the form. I checked my code and could not find errors or explanation for this.
tennis_player is a local variable for you home.html.erb .
So you need to pass it in js.erb like
$("#atpenlist_form").html("<%= escape_javascript(render('users/atpdiscard'), locals: {tennis_player: your_tennis_player_object}) %>");
Set the tennis player object in your controller & use that in above js.erb
I am very new to Ruby and Rails and I am trying to make a modification to a page that is generating the Show, Edit and Delete links for the records using activescaffold. The issue I have is that the page that I am looking at has the links being generated automatically by activescaffold and I need to change only the Delete link to go to another page where I can display a message that will say, You are deleting "option blah blah", Are you sure you want to do that? and give them a cancel and ok button.
My issue at this time is I am not sure where to look for where the links are being generated and hence I am not able to target the new page.
Here is the code that is creating the links
<table cellpadding="0" cellspacing="0">
<tr>
<td class="indicator-container">
<%= loading_indicator_tag(:action => :record, :id => record.id) %>
</td>
<% active_scaffold_config.action_links.each :record do |link| -%>
<% next if controller.respond_to? link.security_method and !controller.send(link.security_method) -%>
<td>
<% if record.class.statused? and record.disabled? -%>
<%= link.action.to_sym == :enable ? render_action_link(link, url_options) : "" -%>
<% else -%>
<%= (record.authorized_for?(:action => link.crud_type) and link.action.to_sym != :enable) ? render_action_link(link, url_options) : "" -%>
<% end -%>
</td>
<% end -%>
Thank you for any help on this matter.
This answer is not specific to ActiveScaffold, but in Rails 3.2, the destroy link is generated as
<%= link_to 'Destroy', foo_instance, method: :delete, data: { confirm: 'Are you sure?' } %>
which results in the link having a data-confirm attribute, which does what it sounds like you want. Can you add this to your ActiveScaffold view?
UPDATE (Rails 2):
The data-confirm attribute is supported by jQuery (instead of, or in addition to prototype), so if you're using jQuery in a Rails 2 app, it might "just work". Otherwise, it's possible that you could just add javascript directly on the link you want to confirm. The url_options parameter for render_action_link looks juicy -- maybe it will accept arbitrary html attributes (as Rails link_to does), e.g.
url_options = { :onclick => 'return confirm("Are you sure?")` }
render_action_link(link, url_options)
This is my first question so I hope I can be as specific as possible, but don't be harsh.
I'm going through Agile Web Development with Rails and am very new to programming.
I want to hide a 'Checkout' button while I am on the order/new page so that it can't do anything nasty to the users purchase.
At the moment, I don't really understand how instance variables work, since it seems like no matter where I declare my instance variable, in a view or in orderscontroller#new, it always validates to true.
This seems to be the case because when I use the instance variable in the view to hide a div ( with hidden_div_if(condition == true) ) the buttons ALWAYS get hidden!
Not only that, but when I do:
<%= hidden_div_if( #hide_checkout_button == false ) do %>
<td><%= button_to 'Empty cart', cart, :method => :delete,
:confirm => 'Are you sure?' %></td>
<% end %>
<%= hidden_div_if( #hide_checkout_button == true ) do %>
<td><%= button_to "Checkout", new_order_path, :method => :get %></td>
<% end %>
BOTH buttons get hidden! How can that be!?!
In this example, I have placed the variable declaration in views\orders_form.html.erb:
<%= #hide_checkout_button = true %>
<%= form_for(#order) do |f| %>
<% if #order.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#order.errors.count, "error") %>
prohibited this order from being saved:</h2>
<ul>
<% #order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
...
Here is a discussion-forum with solutions for this problem, but they don't work for me and I don't know why: http://pragprog.com/wikis/wiki/Pt-G-2/version/35
And here is the question itself:
What happens if you click the Checkout button in the sidebar while the
checkout screen is already displayed? Can you find a way to disable
the button in this circumstance? (Hint: variables set in the controller
are available in layouts and partials as well as in the directly rendered
template.)
If you need any more information to help please ask, I'm not quite sure how much detail to provide or what information is important.
Thanks in advance :-)
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
puts 34
#hide_checkout_button = true
#order = Order.new
puts 37
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
Started GET "/assets/logo.png" for 127.0.0.1 at 2012-11-13 20:33:40 +0000
Served asset /logo.png - 304 Not Modified (1ms)
[2012-11-13 20:33:40] WARN Could not determine content-length of response body.
Set content-length of the response or set Response#chunked = true
*
34
37
*
/orders/new
Started GET "/orders/new" for 127.0.0.1 at 2012-11-13 20:33:49 +0000
Processing by OrdersController#new as HTML
←[1m←[36mCart Load (1.0ms)←[0m ←[1mSELECT "carts".* FROM "carts" WHERE "carts
"."id" = ? LIMIT 1←[0m [["id", 63]]
←[1m←[35m (1.0ms)←[0m SELECT COUNT(*) FROM "line_items" WHERE "line_items"."c
art_id" = 63
The problem is a missing =. do <%= %> instead.
<%= hidden_div_if( #hide_checkout_button == true ) do %>
<td><%= button_to "Checkout", new_order_path, :method => :get %></td>
<% end %>
Also, for boolean checks you can just do if(boolean) which will evaluate the same as if you put == true/false
<%= hidden_div_if( #hide_checkout_button) do %>
#...
<% end %>
You want to set that variable on the method that calls the view. In this case it is the new method in OrdersController
def new
#hide_checkout_button = true
end
Edit:
add this to the hidden div just to see if it helps finding the element to set the attribute to
<%= hidden_div_if(#hide_checkout_button, id: 'cart') do %>
with (in the application helper)
def hidden_div_if(condition, attributes = {}, &block
if condition
attributes["style"] = "display: none"
end
content_tag("div", attributes, &block)
end
if that doesn't do it, then just do it this way
<% if #hide_checkout_button %>
<td><%= button_to "Checkout", new_order_path, :method => :get %></td>
<% else %>
<td><%= button_to 'Empty cart', cart, :method => :delete, :confirm => 'Are you sure?' %></td>
<% end %>
used
<div class="actions">
<% if not #hide_checkout_button %>
<%= button_to 'Checkout', new_order_path, method: :get, class: "checkout" %>
<% end %>
<%= button_to 'Empty cart', cart, method: :delete, data: { confirm: "Are you sure?" }, remote: true %>
I've set up a Rails form roughly following the instructions in this Railscast.
Here's the code for the form:
<% form_tag complete_todos_path, :method => :put do %>
<ul>
<div id="incomplete_todos">
<% #incomplete_todos.each do |todo| %>
<%= render :partial => todo %>
<% end %>
</div>
</ul>
<%= submit_tag "Mark as completed" %>
<% end %>
And here's the code for the todo partial:
<div class="todo">
<li>
<%= check_box_tag "todo_ids[]", todo.id %>
<%=h todo.name %>
<%= link_to 'edit', edit_todo_path(todo) %>
<%= link_to 'delete', todo, :confirm => 'Are you sure?', :method => :delete %>
</li>
</div>
It's working great, but I'm looking to start implementing AJAX and I need each checkbox to have a unique id. Right now, the input tags generated look something like this:
<input id="todo_ids_" name="todo_ids[]" type="checkbox" value="7" />
Every check box has the same id ("todo_ids_"), which is a problem. I suspect the solution is embarrassingly simple, but I'm not seeing it. Any tips?
<%= check_box_tag "todo_ids[]", todo.id, false, :id => "todo_id_#{todo.id}" -%> or whatever you want the id to be.
I consider this a bug with check_box_tag caused by the seemingly hackish nature of manually giving it the name todo_ids[] and the method code calling sanitize_to_id(name). I just ran into this yesterday and I'm contemplating a patch.
I ended up using a solution similar to Ryan's, but as I wrote in the comment I had to make a further change. In the form:
<%= check_box_tag "todo_ids[#{todo.id}]", todo.id %>
In the action called by the form:
Todo.update_all(["completed_at = ?", Time.now], :id => params[:todo_ids].keys)
Note the "params[:todo_ids].keys" at the end, which was a workaround to deal with the odd way the parameters were formatted:
"todo_ids" => {"5"=>"5"}
Can you try this and let us know if it works:
check_box_tag "todo_ids[#{todo.id}]", todo.id %>
This is the expected behaviour of check_box_tag, as this comment on a rejected fix explains.
You can use collection_check_boxes like this (haml syntax, sorry):
# Accumulate todos in a params hash like { todos: { to_complete: [] } }
= collection_check_boxes(:todos, :to_complete, #incomplete_todos, :id, :name) do |todo_builder|
= todo_builder.label do
# This is the result of calling :name on the todo, as specified
# calling the helper
= todo_builder.text
= todo_builder.check_box
Of course you can use partials inside the block, just pass and use the builder inside.
Check more options in the API docs.