Hi I am total Newbie learning RoR, Web and OOP. Its fascinating.(I have just managed to get email working for example). Anyways, while deleting a few DB records from a List using Destroy I noticed that I could cause the system to crash by hitting the same destroy link again before the first Destroy had completed. What appears on the browser is:- ActiveRecord::RecordNotFound in Projects#destroy Couldn''t find Project with ID=58 /app/controllers/projects_controller.rb:54:in `destroy'' script/server:49 Obviously this is not acceptable in a production environment. Is there a technique to prevent this. Peter _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Hi Peter, On 8.2.2005, at 18:44, Cutting Peter wrote:> > Obviously this is not acceptable in a production environment. Is there > a technique to prevent this.Yes, you can rescue the exception and redirect to a page that tells the user not to double-click anymore :-). The other way is to use javascript to prevent double-clicking altogether, so that when the user clicks a link, an onClick handler disables it. Here''s a not-so-sophisticated example of the method: <script type="text/javascript"> function disableLink( linkID ) { if (document.getElementById) { target = document.getElementById( linkID ); target.href = "#" } } </script> <a href="destroy" id="myDestroy" onClick="disableLink(''myDestroy''); return true;">Destroy</a> The safest bet is of course to use both of these techniques. //jarkko> > > > Peter > > > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Jarkko Laine http://jlaine.net http://odesign.fi _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
If you''re passing in the id of the record you want to do the delete on simply do something like this: item_i_want_to_delete = SomeRecord.find_first(["id =?",@params["id"]) item_i_want_to_delete.destroy unless item_i_want_to_detroy.nil? Hope this helps J Cutting Peter wrote:> > > Hi > > I am total Newbie learning RoR, Web and OOP. Its fascinating.(I have > just managed to get email working for example). > > > > Anyways, while deleting a few DB records from a List using Destroy I > noticed that I could cause the system to crash by hitting the same > destroy link again before the first Destroy had completed. > > > > What appears on the browser is:- > > > > ActiveRecord::RecordNotFound in Projects#destroy > > Couldn''t find Project with ID=58 > > /app/controllers/projects_controller.rb:54:in `destroy'' > > script/server:49 > > > > Obviously this is not acceptable in a production environment. Is there a > technique to prevent this. > > > > Peter > > > > > > > ------------------------------------------------------------------------ > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails
Cutting Peter wrote:> I am total Newbie learning RoR, Web and OOP. Its fascinating.(I have > just managed to get email working for example).Welcome to Rails!> Anyways, while deleting a few DB records from a List using Destroy I > noticed that I could cause the system to crash by hitting the same > destroy link again before the first Destroy had completed. > > Obviously this is not acceptable in a production environment. Is there a > technique to prevent this.Read up on exception handling in Ruby. Here''s an example. Anything in the begin ... rescue block that raises ActiveRecord::RecordNotFound will be handled by the rescue ... end block. class ProjectController < ActionController::Base def destroy begin project = Project.find(Integer(@params[''id''])) project.destroy flash[:notice] = "I hammered number #{project.name} for you." rescue ArgumentError flash[:error] = "That''s a nasty looking id you handed me." rescue ActiveRecord::RecordNotFound flash[:error] = "Hold your horses, that project doesn''t exist." rescue Exception => e flash[:error] = "I''m rescuing anything that hasn''t been caught: #{e}" raise e # You can raise the same exception again. ensure logger.info "I''m an ensure block: my code is *always* run at the end, even if an exception is raised." end redirect_to :action => ''list'' end end To generically handle exceptions raised in any controller action, define the rescue_action_in_public method: class ProjectController < ActionController::Base # Note the access control here. We''re defining a method that''s only # visible to this class and its subclasses. The public can''t see it. protected def rescue_action_in_public(exception) render_text ''<html><body><h1>Project Error</h1></body></html>'' end end You may define rescue_action_in_public in your ApplicationController to provide default rescue behavior for all your controllers. See http://rails.rubyonrails.com/classes/ActionController/Rescue.html for more info. jeremy
* Jarkko Laine (jarkko-k1O+Gnc6WpmsTnJN9+BGXg@public.gmane.org) [050208 12:12]:> The other way is to use javascript to prevent double-clicking > altogether, so that when the user clicks a link, an onClick handler > disables it. > > Here''s a not-so-sophisticated example of the method: > > <script type="text/javascript"> > > function disableLink( linkID ) { > if (document.getElementById) { > target = document.getElementById( linkID ); > target.href = "#" > } > } > > </script> > > <a href="destroy" id="myDestroy" onClick="disableLink(''myDestroy''); > return true;">Destroy</a>Another method is to keep a sequence counter in the session, which gets incremented on change operations (create, edit, delete, etc.). Push this sequence counter via a hidden field in forms. If a form submission comes in with a less-than-current sequence number then it''s a re-submit of an old transaction, so don''t change the database. In Rails this can be easily implemented with a before filter, and a view helper. This method is quite common in some environments, and, IIRC, I think was documented in ArsDigita''s ACS system. Again, like the JS method, it''s not fool-proof (as you''re still trusting the client to provide proper information), but it doesn''t rely on client-side Javascript to work properly. If you''re really anal you can /require/ the sequence number to be passed back in on the form, but that''s going beyond simple double-click protection and into something else. Rick -- http://www.rickbradley.com MUPRN: 215 | it to backup my random email haiku | Win98 machines just after | a new install.
On 8.2.2005, at 19:49, Rick Bradley wrote:>> > > Another method is to keep a sequence counter in the session, which gets > incremented on change operations (create, edit, delete, etc.). Push > this sequence counter via a hidden field in forms. If a form > submission > comes in with a less-than-current sequence number then it''s a re-submit > of an old transaction, so don''t change the database. In Rails this can > be easily implemented with a before filter, and a view helper. This > method is quite common in some environments, and, IIRC, I think was > documented in ArsDigita''s ACS system.The current approach to this in OpenACS (the successor of old ACS) is to get the next object id beforehand and use that (signed) in the form to prevent double-clicking. Using this approach would need a bit extended form widgets (like ad_form [1] in OpenACS). However, as I said, I think the the JavaScript approach and the server side approach (whatever that then is) complement each other. You can never completely prevent users from double-clicking with a server-side approach, you can only treat the symptoms. To complete prevention you need javascript. However, the JS way doesn''t obviously work for people who have JavaScript disabled in their browser. That''s where the server-side prevention should kick in. //jarkko [1] http://openacs.org/api-doc/proc-view?proc=ad%5fform> > Again, like the JS method, it''s not fool-proof (as you''re still > trusting > the client to provide proper information), but it doesn''t rely on > client-side Javascript to work properly. If you''re really anal you can > /require/ the sequence number to be passed back in on the form, but > that''s going beyond simple double-click protection and into something > else. > > Rick > -- > http://www.rickbradley.com MUPRN: 215 > | it to backup my > random email haiku | Win98 machines just after > | a new install. > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Jarkko Laine http://jlaine.net http://odesign.fi _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails