"Redo" suddenly failing. - ruby-on-rails

Awhile ago, I implemented an "undo" and "redo" button like in this railscasts on the paper_trail gem. Everything worked great until today when the "redo" stopped working. I haven't been able to figure out why.
Here is the relevant controller action:
def revert
#version = PaperTrail::Version.find(params[:id])
if #version.reify
#version.reify.save!(:validate => false)
view_context.reify_associations(#version)
else
#version.item.destroy
end
link_name = params[:redo] == "true" ? "undo" : "redo"
link = view_context.link_to(link_name, revert_version_path(#version.next, :redo => !params[:redo]), :method => :post)
if #version.event == "create"
flash[:success] = "Undid #{#version.event}. #{link}"
redirect_to :controller => session[:controller], action: :index
else
flash[:success] = "Undid #{#version.event}. #{link}"
redirect_to :back
end
end
It fails on the line: link = view_context.link_to(link_name, revert_version_path(#version.next, :redo => !params[:redo]), :method => :post)
With an error: ActionController:UrlGenerationError ... No route matches {:action=>"revert", :controller=>"versions", :format=>nil, :id=>nil, :redo=>false} missing required keys: [:id].
So, obviously, I followed the error to see why the :id wasn't being passed in, but it is.
In the parameters:
{"_method"=>"post",
"authenticity_token"=>"6/B1YDXqZ62+DqMMdcYTfCTIJGaVvc1RjKmxWW2GkeQ=",
"redo"=>"true",
"id"=>"29"}
The id is clearly defined and the first line of this controller finds the corresponding version in the table based on that id.
I cannot figure out what is wrong here so any help would be greatly appreciated.

For anyone finding this later on, the issue I was having was the first statement of my conditional (#version.reify) never returned nil. This is because I had overwritten the object attribute to add additional information to create events. To solve the problem, I simply changed the conditional to check the type of event, i.e.
if #version.event == "create"
...
end
PaperTrail's reify method looks at the object attribute of #version and recreates the object from the serialization. It returns nil whenever the object field is nil, otherwise it will return the object it unserialized.

Related

rails shoppe gem: can't add product to order

I am developing a site that will use the shoppe gem as the e-commerce engine, and I am having trouble adding or deleting an item to an existing order. A user should be able to view their order, and then next to each order item, there will be a plus sign and a minus sign. When clicked, the plus sign should add one item of the same kind to the order, and likewise, the minus sign should delete an item when clicked. I have two links with the following code:
link_to "+", adjust_order_item_quantity_path(item.id), :method => :post
link_to "-", adjust_order_item_quantity_path(item.id), :method => :delete
My routes look like this:
post 'orders/change_item_quantity/:order_item_id', to: 'orders#change_item_quantity', :as => 'adjust_order_item_quantity'
delete 'orders/change_item_quantity/:order_item_id', to: 'orders#change_item_quantity'
The code for my orders controller is:
def change_item_quantity
item = current_order.order_items.find(params[:order_item_id])
request.delete? ? item.decrease! : item.increase!
if item.quantity == 0
redirect_to "#", :notice => "#"
else
redirect_to request.referer
end
rescue Shoppe::Errors::NotEnoughStock => e
respond_to do |wants|
wants.html { redirect_to request.referer }
end
end
Much of this code is the sample code given on the shoppe tutorial site, but I am just having trouble getting it to work correctly. When I click the '+', I get an error that says:
Couldn't find Shoppe::OrderItem with 'id'=[nil] [WHERE `shoppe_order_items`.`order_id` = ?]
When I click the '-', one item is deleted as it should be, but then I get the same error message as above when I click the '-' when there is only one item left. I would really appreciate the help!
Thanks in advance!
EDIT
Here is my application trace:
1 def change_item_quantity
2 item = current_order.order_items.find(params[:order_item_id])
3 request.delete? ? item.decrease! : item.increase!
4 if item.quantity == 0
5 redirect_to root_path
The problem is on this line:
item = current_order.order_items.find(params[:order_item_id])
It looks like your order_item_id param is getting lost at some point. In general you should
include the full error trace with your question because it will tell you which line of code
caused the error.
Having some experience with reading errors, though, I can tell you that the
Couldn't find <ModelName> error is generally caused by using find with a bad id.
So the following line is what's failing:
item = current_order.order_items.find(params[:order_item_id])
Your view code looks good to me and I'm not sure what you'd need to change to
fix this error. What I'd do it put a breakpoint at the beginning of your change_item_quantity
method and make sure that params[:order_item_id] is set. If it's not, it's probably
an issue with your view code (the link_to).
Perhaps the item.id is nil in your link. Check your HTML and make sure ERB has entered the
ID correctly.

On re-submit of form with errors, a PATCH request is called when it's supposed to be a POST

Something very funny is happening... my controller has two checks that are made separately before it allows the form to be submitted (the code below is simplistic, there are other things happening). If either checkpoint fails, the new page is re-rendered.
Here's the code:
def create
if checkpointone == fail
render 'new'
else
if checkpointtwo == fail
render 'new'
else
redirect_to action: 'success'
end
end
end
Here's the funny flow problem I'm running into:
user enters data that fails checkpointone and passes checkpointtwo
user submits
new page successfully re-rendered with original parameters and error message
user still enters incorrect data (either checkpointone fails again, or it passes but checkpointtwo fails)
user submits
application fails with an error No route matches [PATCH] "/requests"
But why on earth is it looking for a PATCH all of the sudden? Who told it to? The weird thing is that if I start with a fail in checkpointtwo and a pass in checkpointone, from then on, I can make any number of fails and resubmissions in various combinations, and I'll always get the correct action: new page re-rendered with original parameters and error message.
Snippet of view code:
<%= form_for #requestrecord, :url => { action: 'create' }, :html=> {:id => 'form'} do |f| %>
Snippet of routes code:
get 'requests/new', to: 'requests#new', as: 'new_request'
post 'requests', to: 'requests#create'
In Rails 4, PATCH is used for updates. Could it be that your record is being created even when a checkpoint fails. This would cause Rails to think that the subsequent request is an update request, rather than create request.

Update not working with id attribute Rails 4

I've got below URL with get attributes sending request to the controller:
http://localhost:3000/videos/new/save_video?video_id=16&status=200&id=PhTHHldZJJQ
Constoller:
def save_video
#video = Video.find(params[:video_id])
if params[:status].to_i == 200
#video.update(:yt_youtube_id => params[:id].to_s, :is_complete => true)
Video.delete_incomplete_videos
else
Video.delete_video(#video)
end
redirect_to videos_path, :notice => "video successfully uploaded"
end
However I'm getting this error somehow:
ActiveRecord::RecordNotFound at /videos/new/save_video
Couldn't find Video with id=PhTHHldZJJQ
Is there a reason why it's not finding from :video_id?
I cannot figure this out...
ActiveRecord ids are integers. You've passed in a string to find which won't convert to an integer, so it will always fail.
It seems that it is trying to search the video using :id and not :video_id. It is possibly another gem (like CanCan), which might be trying to load the video before the action starts, in a filter. Or it might be something else in the controller, can you post the full code of the controller?
If you just want this to work, use :id as the variable to send the video id, instead of :video_id.

Rails form trouble

So, I have something of an odd controller/view setup, editing a Product model object occurs in the listings controller/view. There is a long-winded explanation for why this is, but I digress. However, when I submit the form, I get the error Couldn't find Product without an ID . What gives? Strangely, when I look at the params sent with the request, the ID attribute is assigned to the 'format' key. ?!.
The controller code is very simple. Edit Action:
def edit
#edit = Product.find(params[:id])
end
Update Action:
def update
#edit = Product.find(params[:id])
if #edit.save
redirect_to :url => listings_display_path
end
end
Here is my form_for code:
<% form_for #edit, :url => (listings_update_path(#edit)) do |f| %>
Edit, the trace:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"st17LW0S9uENaV8UBcxKUfRH67oo+r3TuFAxiPKMCEc=",
"product"=>{"brand"=>"test_brand",
"productname"=>"test_product",
"category"=>"test_category",
"regprice"=>"4",
"saleprice"=>"2",
"description"=>"description"},
"commit"=>"Submit",
"format"=>"21"}
Edit: routes.rb
resources :product do
resources :promotions
collection do
get "replace"
end
end
#listings
match 'listings/index' => 'listings#index'
match 'listings/display' => 'listings#display'
match 'listings/edit' => 'listings#edit'
match 'listings/update' => 'listings#update'
Edit: create action
def create
#product = Product.new(params[:product])
#product.user = current_user
if #product.save
redirect_to :action => 'index'
end
end
First, for an alternate approach to editing multiple records on a single view, try this railscast: http://railscasts.com/episodes/198-edit-multiple-individually
Second, this is unconventional but your whole approach is unconventional...
You can stick a hidden field in the form with the ID in it. Something like:
<%= f.hidden_field, :product, :id %>
Then check your params hash and the id will be in there. You should be able to access it in the controller using something similar to:
# untested
#edit = Product.find(params[:product][:id])
Off the top of my head I'm not sure how it will be stored in your params hash but it will be there and you'll be able to access it like any other hash attribute.
Good luck!
--EDIT--
Also, regarding your comment about lack of flexibility in Rails -- one thing I've learned is that Rails isn't inflexible, but it is highly optimized for particular conventions. The developers refer to this as being "highly opinionated" software, which means:
It can do just about anything you care to do it any way you want to do it, but...
There's almost always a "preferred" way which is better, faster (harder stronger lol).
You would save yourself tons of time and energy -- and probably have a lot of fun -- grabbing the Beginning Rails 3 book. You could work through it in a weekend, and when you were done you would have a great primer on the "Rails Way" which would hopefully help you go from "I don't get why Rails doesn't do this or that" to "I get how Rails works and how easy it is to do what I want to do by following X pattern". That's what happened for me anyhow.
Again good luck!
Try this
<%= form_for #edit, :url => listings_path(#edit), :html => {:method => :put} do |f| %>
You need to fix the routes that are for a single product instance to have the id:
match 'listings/index' => 'listings#index'
match 'listings/:id/display' => 'listings#display'
match 'listings/:id/edit' => 'listings#edit'
match 'listings/:id/update' => 'listings#update'

how do I create a custom route in rails where I pass the id of an existing Model?

I created the following route:
map.todo "todo/today",
:controller => "todo",
:action => "show_date"
Originally, the 'show_date' action and associated view would display all the activities associated for that day for all the Campaigns.
This ended up being very slow on the database...it would generate roughly 30 records but was still slow.
So, I'm thinking of creating a partial that would first list the campaigns separately.
If someone clicked on a link associated with campaign_id = 1, I want it to go to the following route:
todo/today/campaign/1
Then I would like to know how to know that the '1' is the campaign_id in the controller and then just do its thing.
The reason I want a distinct URL is so that I can cache this list. I have to keep going back to this and it's slow.
NOTE: It's possibly the problem actually is that I've written the queries in a slow way and sqlite isn't representative of how it will be in production, in which case this work-around is unnecessary, but right now, I need a way to get back to the whole list quickly.
The code above by #Damien is correct but incomplete. It should be:
map.todo "todo/today/campaign/:id", :controller => "todo", :action => "show_date"
in your views all you have to do is:
<%= link_to "Campaign 1", todo_path(:id => 1) %>
or simply
<%= link_to "Campaign 1", todo_path(1) %>
and the particular campaign id can be fetched using params[:id] in your action.
And yeah, sqlite is not production ready.
EDIT: The latter part is quite easy to implement:
However, you have to change the route slightly,
The route will now become,
map.todo "todo/today/:campaign/:id", :controller => "todo", :action => "show_date"
in your views:
<%= link_to "Campaign 1", todo_path(:campaign => "campaign", :id => 1) %>
In your todo controller, show_date action:
def show_date
#IF YOU ARE USING THIS REPEATEDLY IN LOTS OF DIFFERENT ACTIONS THEN A BETTER PLACE FOR THIS WOULD BE AS A HELPER IN application_controller.rb
if params[:id].nil? && params[:campaign].nil?
#DO SOMETHING WHEN BOTH ARE NIL,
elsif params[:campaign]!="campaign"
#DO SOMETHING WITH CAMPAIGN BEING SOMETHING OTHER THAN "CAMPAIGN"
elsif params[:campain]=="campaign" && params[:id].nil?
#DO SOMETHING WITH ID BEING NIL.
else
#FIND YOUR CAMPAIGN HERE.
end
end
Hope this helps. :)
Just with the following :
map.todo "todo/today/:id",
:controller => "todo",
:action => "show_date"
This will create the /todo/today/:id url where id is whatever you set in the url.
You can then access it in your controller with params[:id].
You might be interested in reading Rails Routing from the Outside In, particularly the section about resources.

Resources