Version 1:
Simple method that I've actually used in production: iframes.
Most of the time you don't actually care if the page renders all at once and direct from the server, and indeed it's better for it to load staggered.
If you just drop an iframe src'd to the controller's show action, you have a very simple solution that doesn't require direct cross-controller interactions.
Pro:
- dead easy
- works with existing show actions etc
- might even be faster, depending on savings w/ parallel vs sequential requests and memory load etc
Cons:
- you can't always easily save the page together with whatever-it-is
- iframes will break out of the host page's javascript namespace, so if they require that, you may need to give them their own minimalist layout; they also won't be able to affect the surrounding page outside their iframe
- might be slower, depending on ping time etc
- potential n+1 efficiency bug if you have many such modules on a page
Version 2:
Do the same thing using JS calls to replace a div with a partial, à la:
<div id="placeholder">
<%= update_page {|page| page['placeholder'].replace with some partial call here } %>
Same as above, except:
Pro:
- doesn't lock it into an iframe, thus shares JS context etc
- allows better handling of failure cases
Con:
- requires JS and placeholder divs; a bit more complex
Version 3:
Call a whole bunch of partials. It gets complicated to do that once you're talking about things like dashboards where the individual modules have significant amounts of setup logic, however.
There are various ways to get around this by making those things into 'mixins' or the like, but IMO they're kinda kludgy.
ETA: The way to do it via mixins is to create what is essentially a library file that implements your module controllers' setup functions, include that wherever something that calls 'em is used, and call 'em.
However, this has drawbacks:
- you have to know what top level controller actions will result in pages that include those modules (which you might not easily, if these are really widgety things that might appear all over, e.g. user preference dependent)
- it doesn't really act as a full fledged controller
- it still intermixes a lot of logic where your holding thing needs to know about the things it's holding
- you can't easily have it be segregated into its own controller, 'cause it needs to be in a library-type file/mixin
It IS possible to call methods in one controller from another controller. However, it's a major pain in the ass, and a major kludge. The only time you should consider doing so is if a) they're both independently necessary controllers in their own rights, and b) it has to function entirely on the back end.
I've had to do this once - primarily because refactoring the reason for it was even MORE of a pain - and I promise you don't want to go there unless you have to.
Summary
The best method IMHO is the first if you have complex enough things that they require significant setup - a simple iframe which displays the module, passing a parameter to tell it to use an ultraminimalist layout (just CSS+JS headers) because it's not being displayed as its own page.
This allows you to keep the things totally independent, function more or less as if they were perfectly normal controllers of their own (other than the layout setting), preserve normal routes, etc.
If you DON'T need significant setup, then just use partials, and pass in whatever they need as a local variable. This will start to get fragile if you run into things like n+1 efficiency bugs, though...