Netzke internals: nested widgets
I wanted it from the very beginning: nesting widgets easily. Say, I need a widget which has a border layout and 2 grids in it. I need each grid to communicate to its own instance on the server, e.g. getting the data from the respective models. I also want the layout to do its own thing - e.g., communicate to its own server-side instance that the user has changed the region sizes (to store them in the preferences for this user). But I also want it all to behave like one widget: e.g. one grid displaying the one-to-many association data for the other (say, one grid shows bosses, and the other - the clerks working under the selected boss). How is this achieved in Netzke? By using the double underscore convention to address nested widgets, and the blessing of Ruby's method\_missing method.
Let’s stick to our example of a widget made of a layout and 2 grids. Let’s call this top-level compound widget “staff”: it should allow us to CRUD (create/read/update/delete) bosses and their respective clerks. Each grid uses AJAX to do those CRUD operations. In Rails it means that it sends a request to some action on a controller. How should we let the controller know, which widget instance should it redirect the request to? This is where the double underscore convention plays its part. Say, the method to get the grid’s data is called “get_data”. Say, the grid displaying bosses is internally called “bosses”. Then the action name to get the data for this grid will be: staff__bosses__get_data. A Netzke-enabled controller, with the help of method_missing, parses this action and by seeing the double underscores it knows that it has to instantiate the “staff” widget (declared in the controller class), and simply pass it the rest of the action: bosses__get_data, along with the request parameters that it has received. Each widget knows to do exactly the same: when seeing a double underscore, it instantiates its aggregatee (a nested widget), and passes it the rest of the method, with the aggregatee’s name stripped off. If no double underscore is given, the widget thinks that it’s a “normal” method, i.e. addressed to itself. So, to complete the example, the same action for the other grid would be called, of course, staff__clerks__get_data.
It might have been done differently. There might have been a common “dispatcher” action declared in the controller, and a widget would send its name along with the request, so the dispatcher would take the job to reach the right widget instance and call the method on it. But the way it’s done in Netzke has one, somewhat hidden, advantage: any widget, or even the controller itself, may implement any of those double underscore methods, to interfere with the way its aggregatee handles the request. In our example it comes handy if we want the “clerks” grid to display the clerks belonging to the boss selected in the “bosses” grid, but limit this functionality to only some specific bosses (e.g. the bosses from our own department). Namely, the “staff” widget implements the method staff__clerks__get_data, where it extracts the boss’ id from the request, checks if this boss is on the “white list”, and if so, instantiates the “clerks” widget with boss’ id being specified as the filter, and finally calls its “get_data” method. If the user is not supposed to see the clerks for the requested boss, it would simply return a flash message informing the user about that, along with zero data. Easy? Flexible? For many tasks out there - quite so.
