3 Replies Latest reply on Aug 5, 2014 10:31 AM by Jon Tara

    How to call url_for from outside of a controller?

    Benj Bouch



      I need to call from a plain ruby class (not a RhoController) the following code:


          Rho::WebView.navigate(url_for controller: 'Blah', action: 'index')


      which get me an `undefined method url_for` error.

      If I replace the `url_for` by its literal "/app/Blah/index" it works ok and navigates to the target page, but I'd like to keep using `url_for` because of conditional query parameters and such


      I even try using `Rho::RhoController.new.url_for ...` but, as expected, it won't work


      Any suggestion appreciated, thanks in advance.

        • Re: How to call url_for from outside of a controller?
          Jon Tara

          url_for is an instance method  defined in Rho::RhoController and so is only available in controllers. And, clearly, some features do not make sense if it were available outside of controllers. For example, the application and model default if not specified based in the request. Clearly, if there is no controller instance, there is no request that can be examined.


          It's a pretty small bit of code, and so you might want to extract it and rewrite it for the more general case. So, in your rewritten version, you should always require a model option.


          I think this would be a useful feature in Rhodes. I agree url_for should be usable outside of a controller (with obvious limitations), but the current code cannot be used in that context.


              # Examples of how to use url_for method:
              # url_for '/some_url'
              # ==> /some_url
              # When generating a new URL, missing values may be filled in from the current request's parameters.
              # For example, if application name or model are not specifyed in the call parameters, they would be filled from the request.
              # url_for :action => :index
              # ==> /application/model
              # url_for :action => :create
              # ==> /application/model
              # url_for :action => :new
              # ==> /application/model/new
              # url_for :action => :show, :id => '{12}'
              # ==> /application/model/{12}/show
              # url_for :model => :another_model, :action => :show, :id => '{12}'
              # ==> /application/another_model/{12}/show
              # url_for :controller => :another_controller, :action => :show, :id => '{12}'
              # ==> /application/another_controller/{12}/show
              # url_for :application => :another_app, :model => :another_model, :action => :show, :id => '{12}'
              # ==> /another_app/another_model/{12}/show
              # url_for :action => :create, :query => {:name => 'John Smith', 'address' => "http://john.smith.com"}
              # ==> /application/model?name=John%20Smith&address=http%3A%2F%2Fjohn.smith.com
              # url_for :action => :show, :id => '{12}', :fragment => "an-anchor"
              # ==> /application/model/{12}/show#an-anchor
              def url_for(params = {})
                return params.to_s if params.is_a?( String ) or params.is_a?( Symbol )
                return '/' if not params.is_a?( Hash ) or params.nil?
                params = params.symbolize_keys if params.is_a? Hash
                application = params[:application] || @request['application']
                model = params[:controller] || params[:model] || @request['model']
                action = params[:action].nil? ? nil : params[:action].to_s
                id = params[:id].nil? ? nil : params[:id].to_s
                id = "{#{id}}" if id and not( id =~ /^{/ and id =~ /}$/ )
                if params[:query_s]
                  query = params[:query_s]
                  query = query_to_s(params[:query])
                fragment = params[:fragment].nil? ? '' : '#' + params[:fragment]
                amurl = ''
                amurl << '/' << application.to_s << '/' if application
                amurl << model.to_s
                is_bb6 = System::get_property('platform') == 'Blackberry' && (System::get_property('os_version').split('.')[0].to_i >= 6)
                if action.nil? or ( !is_bb6 && action == 'create' ) or action == 'index'
                  amurl << query << fragment
                  amurl << '/' << (id.nil? ? action : id + '/' + action) << query << fragment         
                #amurl = '/' + application.to_s + '/' + model.to_s
                #return amurl + query + fragment if action.nil? or action == 'create' or action == 'index'
                #return amurl +'/'+ (id.nil? ? action : id + '/' + action) + query + fragment
            • Re: How to call url_for from outside of a controller?
              Benj Bouch

              Thanks Jon for your reply,


              It's very interesting, and also that confirm what I understood by reading the code before posting my question.

              Now to go a little further, the internal server spawned by a RhoMobile app is serving a single user right? So it there any internal state variables that keep track of the current/last controller? We could write `Rho::RhoController.current.url_for ...`


              I don't really like duplicating code, its not a good practice for long term maintenance.



                • Re: How to call url_for from outside of a controller?
                  Jon Tara

                  Each request to a controller create a new instance of the a controller object. Once the request is done, the object is gone. You can't call instance methods on a non-existent object!


                  There's no need, though, for duplicate code - this is an open-source project. You can make a feature request, and maybe they will implement it. Or, you can do it yourself and submit a pull request.


                  Outside of a controller, url_for can't do any more than create a URL from an options hash. So, take the existing code and write a function that creates the URL from only the options hash. (Default application.) Then rewrite the existing code to use that code, but default model if not supplied.