Hybrid View

  1. #1
    Ext JS Premium Member christocracy's Avatar
    Join Date
    Oct 2006
    Location
    Montreal
    Posts
    381
    Vote Rating
    0
    christocracy is on a distinguished road

      0  

    Default [3.0] Ext.Direct Routers for Rails and Merb

    [3.0] Ext.Direct Routers for Rails and Merb


    I released a beta version of the Ext.Direct router for Rails yesterday.
    http://rubyforge.org/projects/rails-extjs/

    See README.rdoc where I wrote a 3-step setup process.
    Code:
    >sudo gem install rails-extjs-direct
    This gem is very new and I have yet to handle file-uploads from multipart forms but that won't take more than 2 hours. The Rails router is implemented using Rack Middle Ware so you need Rails 2.3.2+. The Rails gem will route transactions to multiple ApplicationControllers during a single Ajax request.

    The Merb gem has been around a little longer.
    http://rubyforge.org/projects/merb-extjs/

    Code:
    >sudo gem install merb-extjs-direct
    The Merb gem is implemented with merb-parts so Ext.Direct requests will be routed to actions within your Merb::PartControllers. I'm looking forward to something like merb-parts in Rails3.

    Both the Rails and Merb gem have a very small code footprint, there's really not much to them. If anyone wishes to contribute to either of these projects, let me know and I'll provide access to the repos on Rubyforge.

    I foresee adding more gems to these namespaces "merb-extjs" and "rails-extjs" to assist with Rails/Merb development with Ext JS.

    Please let me know of any issues or suggestions.
    /**
    * @author Chris Scott
    * @business www.transistorsoft.com
    * @rate $150USD / hr; training $500USD / day / developer (5 dev min)
    *
    * @SenchaDevs http://senchadevs.com/developers/transistor-software
    * @twitter http://twitter.com/#!/christocracy
    * @github https://github.com/christocracy
    */

  2. #2
    Sencha - Community Support Team edspencer's Avatar
    Join Date
    Jan 2009
    Location
    Palo Alto, California
    Posts
    1,939
    Vote Rating
    7
    edspencer is a jewel in the rough edspencer is a jewel in the rough edspencer is a jewel in the rough

      0  

    Default


    Thanks for putting this up.

    I'm a Rails guy too and have been looking through this - what I'm finding difficult at the moment is getting any 'editorial' kind of info about what Writers and Direct actually do... is there anything I can look at about this?

    Poking around at the gem code I can see how it's looping through a bunch of 'requests' inside the single request received, but how do you provide error feedback this way? If I want to execute a few actions and they're bundled into a single Direct request, how do I regain control if one of them fails? What do I do if my save doesn't pass validation, or my load hits a 404?

    It looks like REST is out of the window with this approach too - do you think that makes it harder for other applications to use the same server API?

    I hope I don't sound negative... I just don't understand how it's meant to work at the moment!
    Ext JS Senior Software Architect
    Personal Blog: http://edspencer.net
    Twitter: http://twitter.com/edspencer
    Github: http://github.com/edspencer

  3. #3
    Ext JS Premium Member christocracy's Avatar
    Join Date
    Oct 2006
    Location
    Montreal
    Posts
    381
    Vote Rating
    0
    christocracy is on a distinguished road

      0  

    Default


    Poking around at the gem code I can see how it's looping through a bunch of 'requests' inside the single request received, but how do you provide error feedback this way?
    In SVN for that gem, I've added a rescue_action to the controller mixin Rails::ExtJS:irect::Controller. The rescue_action is looking for exceptions of class XException. XException is provided by the rails-extjs-direct gem. Notice how it simply returns an Ajax response of type XExceptionResponse. Ext.Direct is prepared to receive this type of response (simply a json response having "type":"exception")

    Code:
    def rescue_action(e)
          if (e.kind_of?(XException))
            render :json => XExceptionResponse.new(@xrequest, e)
          else
            raise e
          end
        end
    Raising an XException in a controller action:
    Code:
    class UsersController < ApplicationController
        include Rails::ExtJS::Direct::Controller
    
      def create
          raise XException.new("A create exception!!!")
      end
    end
    /**
    * @author Chris Scott
    * @business www.transistorsoft.com
    * @rate $150USD / hr; training $500USD / day / developer (5 dev min)
    *
    * @SenchaDevs http://senchadevs.com/developers/transistor-software
    * @twitter http://twitter.com/#!/christocracy
    * @github https://github.com/christocracy
    */

  4. #4
    Ext User
    Join Date
    Mar 2009
    Posts
    3
    Vote Rating
    0
    ttsuchi is on a distinguished road

      0  

    Default


    Thanks for the gem! I'm starting to take a look at the gem today. One thing I noticed it is that it doesn't support namespaced controllers yet... (Well, I'm sure there're lots of things that's not supported yet, but this was the first roadblock I encountered.) I want to use it under my "/admin/*" URL, and so want to invoke Admin::SomeController instead.

    So looking at the gem source, I made the following changes:

    Code:
    def initialize(app, rpath)
    ...
        @ns = rpath[0...rpath.rindex('/')]
    end
    
    def call(env)
    ...
      request_env["PATH_INFO"] = "#{@ns}/#{controller}/#{action}"
      request_env["REQUEST_URI"] = "#{@ns}/#{controller}/#{action}"
    ...
    end
    and in my environment.rb

    Code:
    config.middleware.use Rails::ExtJS::Direct::RemotingProvider, "/admin/direct"
    So basically, when "/admin/direct" router is defined, this would try to forward reqs to "/admin/*" URL. This is not a great solution - this way, the app can handle only one namespace. Maybe a better approach is to pass the namespace param from Ext.Direct (like baseParams: {ns: '/admin'}) and get that value from there, but I haven't looked into the Ext.Direct.addProvider yet to know if that's possible

  5. #5
    Ext User
    Join Date
    Mar 2009
    Posts
    3
    Vote Rating
    0
    ttsuchi is on a distinguished road

      0  

    Default


    Also, maybe the default of the @params in XRequest should be a Hash ({}) as opposed to array ([])?

  6. #6
    Sencha User
    Join Date
    Jan 2008
    Location
    Romania
    Posts
    23
    Vote Rating
    0
    chriss is on a distinguished road

      0  

    Default


    You can solve this problem by modifying the remoting_provider.rb file from the gem( ruby\gems\1.8\gems\rails-extjs-direct-0.0.3\lib\rails-extjs-direct\rack).
    Replace
    Code:
    controller = req.delete("action")
    with
    Code:
    controller = req.delete("action").gsub("::","/")
    The only disadvantage is that you'll have to call your provider by
    window[Namespace::Controller]

  7. #7
    Ext User
    Join Date
    Sep 2009
    Posts
    1
    Vote Rating
    0
    quorak is on a distinguished road

      0  

    Default


    Quote Originally Posted by ttsuchi View Post
    Thanks for the gem! I'm starting to take a look at the gem today. One thing I noticed it is that it doesn't support namespaced controllers yet... (Well, I'm sure there're lots of things that's not supported yet, but this was the first roadblock I encountered.) I want to use it under my "/admin/*" URL, and so want to invoke Admin::SomeController instead.
    Any chance to make this a feature?
    I think this setup is quite likely as extjs is great for admin interfaces!

    best

  8. #8
    Ext User
    Join Date
    Mar 2008
    Posts
    2
    Vote Rating
    0
    kampnerj is on a distinguished road

      0  

    Default formHandler?

    formHandler?


    Is this going to support the formHandler option at some point soon?

  9. #9
    Sencha User
    Join Date
    Jan 2008
    Location
    Romania
    Posts
    23
    Vote Rating
    0
    chriss is on a distinguished road

      0  

    Default


    Quote Originally Posted by quorak View Post
    Any chance to make this a feature?
    I think this setup is quite likely as extjs is great for admin interfaces!

    best
    I had the same problem, and here is what i did

    override some ext.direct methods

    Code:
    Ext.override(Ext.direct.RemotingProvider,{
    
    
         doCall : function(c, m, args){
            var data = null, hs = args[m.len], scope = args[m.len+1];
    
            if(m.len !== 0){
                data = args.slice(0, m.len);
            }
    
            var t = new Ext.Direct.Transaction({
                provider: this,
                args: args,
                action: c,
                method: m.name,
                namespace: m.namespace,
                data: data,
                cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
            });
    
            if(this.fireEvent('beforecall', this, t) !== false){
                Ext.Direct.addTransaction(t);
                this.queueTransaction(t);
                this.fireEvent('call', this, t);
            }
        },
    
        doForm : function(c, m, form, callback, scope){
            var t = new Ext.Direct.Transaction({
                provider: this,
                action: c,
                method: m.name,
                namespace: m.namespace,
                args:[form, callback, scope],
                cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
                isForm: true
            });
    
            if(this.fireEvent('beforecall', this, t) !== false){
                Ext.Direct.addTransaction(t);
                var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
                    params = {
                        extTID: t.tid,
                        extAction: c,
                        extMethod: m.name,
                        extType: 'rpc',
                        extUpload: String(isUpload)
                    };
                
                // change made from typeof callback check to callback.params
                // to support addl param passing in DirectSubmit EAC 6/2
                Ext.apply(t, {
                    form: Ext.getDom(form),
                    isUpload: isUpload,
                    params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
                });
                this.fireEvent('call', this, t);
                this.processForm(t);
            }
        },
    
        
        getCallData: function(t){
    
            console.log(t)
            return {
                action: t.action,
                method: t.method,
                data: t.data,
                type: 'rpc',
                tid: t.tid,
                namespace: t.namespace || ''
            };
        }
        
    })
    modify Rails::ExtJS:irect::RemotingProvider call method, so now contains this

    Code:
    controller = req.delete("action").strip.downcase
    action =  req.delete("method").strip.downcase
    ns = req.delete("namespace").strip.downcase
    controller = [ns, controller].join("/") if !ns.blank?
    And now i can define my provider like this

    Code:
    Ext.Direct.addProvider({
        type:"remoting",
        url:"/direct",
        actions:{
            "main":[{
                "name":"load", 
                "len":1,
                'namespace':'ruby_namespace'
            }]
        },
        "namespace":"js_namespace"
    })

  10. #10
    Ext User
    Join Date
    Oct 2008
    Posts
    18
    Vote Rating
    0
    somebee is on a distinguished road

      0  

    Default


    I have created a merb-router myself, which does not use Parts at all. It exposes all the public controllers and actions automatically (has a helper for generating the direct-api javascript), and maps to the exact same controllers as your public api etc.

    This is done with merb-action-args, so that that I can do this:

    Code:
    class Topics < Application
      direct :expose => :all 
    	
      def show(id)
    	@topic = Topic.get(id)
    	display @topic
      end
    
    end
    
    When you include the js-helper, you can automagically call:
    
    Topics.show(10, function(topic){ /*callback*/}) in your client-code.
    
    But the same action can also be accessed via regular http, like:
    http://yourapp/topics/10.json => {:id => 10, :title => "Hello", ...}
    Or without resource-routes:
    http://yourapp/topics/show.json?id=10 => {:id => 10, :title => "Hello", ...}
     
    So, with merb-action-args there is no need to have duplicate code.
    The only thing you need to set it up is to add this to router.rb:
    
    Merb::Router.prepare do
    
      direct("/rpc.json") # or the url you want to use
    
      ...
    
    end
    I'm planning on releasing it on github, but the code itself is still a little messy, so need to do some cleaning first :-)