{"id":387,"date":"2007-11-15T08:41:17","date_gmt":"2007-11-15T12:41:17","guid":{"rendered":"http:\/\/www.dr-chuck.com\/wordpress\/?p=387"},"modified":"2011-12-17T12:24:27","modified_gmt":"2011-12-17T16:24:27","slug":"my-first-monkey-patch-for-a-rails-routes-rb-bug","status":"publish","type":"post","link":"https:\/\/www.dr-chuck.com\/csev-blog\/2007\/11\/my-first-monkey-patch-for-a-rails-routes-rb-bug\/","title":{"rendered":"My First Monkey Patch for a Rails routes.rb bug"},"content":{"rendered":"<p>For the past five days I have been struggling with a bug in the route code in rails 1.2.3.  I finally monkey patched url_for to essentially force it to do the obvious.<br \/>\nIn this blog post, I describe the bug and how I reproduced it and then the patch to work around the bug.<br \/>\nThere is a 10% chance that the routes is actually working and I just do not understand it.  I read in the Agile book something about routes trying to use the shortest URL where possible &#8211; but the problem is that I have these long URLs to pass bits of session-like information around between my ajax calls &#8211; particularly when I want to go cross domain with my Ajax (i.e. I want to use a Rails tool from one server included into a web page from another server where cookies and session information will not be usable).<br \/>\nThe bug comes down to when I call something with a long url with lots of stuff defined and then I generate a new url adding an id which is always at the end of the routes, url_for decides to &#8220;forget&#8221; the stuff that was to the left of it in the incoming request URL.  Changing action only generates the *right* url while changing the action and id generates the wrong URL.<br \/>\nMy Urls have three basic forms<br \/>\n\/imsti.fe828-12345-bfea.1.right-content\/comments\/ajaxstart<br \/>\n\/ajax.1.right-content\/comments\/ajaxstart<br \/>\n\/comments\/ajaxstart<br \/>\nThe first two are for use in divs and never shown to the user. The imsti one both establishes a session and sets up the context and div name when the tool is being used cross-domain.  The second is used when you are not going cross-domain and cookies are available to establish session because the ajax and the enclosing container are coming from the same server &#8211; so incoming ajax requests get the cookies.<\/p>\n<p><!--more--><br \/>\nHere is my routes.rb &#8211; the three routes in question are the ones that freak Rails url generation out &#8211; even though I never use not generate Urls with the imsti prefix &#8211; other URLs are incorrectly generated as you will see in this short sad story.<br \/>\nmap.connect &#8221;, :controller => &#8220;portal&#8221;<br \/>\n# Problem causing routes<br \/>\nmap.connect &#8216;imsti.:imssession.:context.:div\/:controller\/:action\/:id.:format&#8217;<br \/>\nmap.connect &#8216;imsti.:imssession.:context.:div\/:controller\/:action\/:id&#8217;<br \/>\nmap.connect &#8216;imsti.:imssession.:context.:div\/:controller\/:action&#8217;<br \/>\n#<br \/>\nmap.connect &#8216;ajax.:context.:div\/:controller\/:action\/:id.:format&#8217;<br \/>\nmap.connect &#8216;ajax.:context.:div\/:controller\/:action\/:id&#8217;<br \/>\nmap.connect &#8216;ajax.:context.:div\/:controller\/:action&#8217;<br \/>\nmap.connect &#8216;:controller\/:action\/:id.:format&#8217;<br \/>\nmap.connect &#8216;:controller\/:action\/:id&#8217;<br \/>\nmap.connect &#8216;:controller\/:action&#8217;<br \/>\nThe bug happens when I go to a page with a Url like this<br \/>\n\/ajax.1.right-content\/comments\/ajaxstart<br \/>\nAnd then in the view for this page I do this:<br \/>\n&lt;%= link_to_remote &#8216;view&#8217;, :update=> portal_get_div,<br \/>\n:url => { :action => &#8216;view&#8217;, :id => comment.id } %&gt;<br \/>\nSimple enough &#8211; I am changing the action and adding an ID &#8211; it should inherit the rest since in all my routes id and action are at the end.<br \/>\nHere is the problem &#8211; when I do not have the three imsti routes in my routes.rb all works well &#8211; the link_to_remote generates this url:<br \/>\n\/ajax.1.right-content\/comments\/view\/1<br \/>\nIf I add the three imsti prefixed routes to my routes.rb the same code when called from the same url:<br \/>\n\/ajax.1.right-content\/comments\/ajaxstart<br \/>\nMakes this URL:<br \/>\n\/comments\/view\/1<br \/>\nAargh!  And on the same page in the same view, this code:<br \/>\n&lt;%= link_to_remote &#8216;Manage&#8217;, :update=> portal_get_div,<br \/>\n:url => { :action => &#8216;manage&#8217; } %&gt;<br \/>\nMakes beautiful URLs like this regardless of what version of the routes.rb I use:<br \/>\n\/ajax.1.right-content\/comments\/manage<br \/>\nSo what the heck is happening &#8211; it works for action and fails when you do action+id &#8211; clearly the addition of an :id is triggering some deep stupid behavior in the url_for.<br \/>\nA key here is that I am never calling nor using a route that starts with imsti &#8211; the mere adding of the routes causes things to break.<br \/>\nI have played for hours and at one point removed my three routes and had a single route that would cause the evil behavior.  I lost the exact single route that broke it &#8211; but it looked something like this:<br \/>\nmap.connect &#8216;routesbiteme\/:controller\/:action&#8217;<br \/>\nIt had an action but no id &#8211; and yet it caused url generation to blow its brains out.  I need to look at the Rails source code.  One day I will download my own copy and start hacking on Rails &#8211; but I will save that for when I have more time.<br \/>\nSo here is the monkey patch to fix it:<\/p>\n<pre><font size=-1>\nmodule ApplicationHelper\ninclude Osids\ninclude Portal\nprotected\n# This is inspired by another monkey patch for support in browsers\n# without cookies - but since cookie_only >= false seems broken\n# that patch while useful to read is not much help\n#\n# http:\/\/www.edgesoft.ca\/blog\/read\/2\n#\n# Funny - sometimes options is a string - in this view code\n#\n# &lt;% form_remote_for :umap, @user, :update=> portal_get_div,\n#            :url =&gt; { :action =&gt; \"add\" } do |f| %&gt;\n#\n# options comes in as a string like this:\n#\n#   \/ajax.1.main-content\/users\/add\n#\n# I am sure this is yet another bug.  But we need to simply\n# not patch the Hash unless it is really a hash\n#\ndef url_for(options = {}, *parameters_for_method_reference)\nif options.class == Hash\nif options[:div] == nil && params[:div] != nil\noptions[:div] = params[:div]\nend\nif options[:context] == nil && params[:context] != nil\noptions[:context] = params[:context]\nend\nif options[:imssession] == nil && params[:imssession] != nil\noptions[:imssession] = params[:imssession]\nend\nend\nsuper\nend\nend\n<\/pre>\n<p><\/font><br \/>\nEffectively if the parameters are on the request, and not in options, I force them into options.  Somehow this convinces the recalcitrant url generation to really see the parameters.<br \/>\nI wasted about 15 hours on this one &#8211; I guess I learned something from it.  I cannot wait to see the ugly code in url generation or secret undocumented back-door feature that broke this.<br \/>\nP.S. I am still struggling with how to set the Rails session id from a URL &#8211; my other reason to be peeved at the Rails folks &#8211; don&#8217;t get me started on cookie_only and Rails developers rushing to put in security fixes without thinking about real impact.  For now I have a hack to do IMS TI sessions in my own framework without using Rails sessions &#8211; to get on to cross-domain Ajax testing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For the past five days I have been struggling with a bug in the route code in rails 1.2.3. I finally monkey patched url_for to essentially force it to do the obvious. In this blog post, I describe the bug and how I reproduced it and then the patch to work around the bug. There [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-387","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/387","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/comments?post=387"}],"version-history":[{"count":1,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/387\/revisions"}],"predecessor-version":[{"id":2490,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/387\/revisions\/2490"}],"wp:attachment":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/media?parent=387"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/categories?post=387"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/tags?post=387"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}