Saw your comment on railscasts.
Thought about it a bit and may be able to offer a solution.
Not sure if it will work with 2.3 or not, but in this article (http://pullmonkey.com/2009/2/11/blog-pl
y-on-rails) I show how to use a plugin's app/controllers and app/models, etc without engines. I think this provides a little more flexibility, seemingly perfect for your problem.
From what I can tell about your situation, each client would probably have a plugin of their own which would be packed with all the files they would want to overwrite.
For example, RAILS_ROOT/vendor/plugins/client1 would have app/controllers/.... and app/models and app/views of client1's own customizations.
Ok, so looking at my post, find the init.rb file and take a close look at it.
There is a way to tell rails about your plugin's models, controllers and views.
Normally, the plugins are meant simply to extend and to be overwritten by anything in the core applications code. But in your situation, the rules are reversed.
So for views, add this to init.rb (note the use of prepend_view_path instead of the normally used append_view_path):
ApplicationController.prepend_view_path(File.join(File.dirname(__FILE__), 'app', 'views'))
For models, again, instead of appending, we need to load it's path first:
# This is what you normally have
#model_path = File.join(directory, 'app', 'models')
#$LOAD_PATH << model_path
#ActiveSupport::Dependencies.load_paths << model_path
# do this instead to put the plugin's file paths first:
model_path = File.join(directory, 'app', 'models')
For controllers, same thing as models:
# instead of this:
#controller_path = File.join(directory, 'app', 'controllers')
#$LOAD_PATH << controller_path
#ActiveSupport::Dependencies.load_paths << controller_path
#config.controller_paths << controller_path
# do this:
controller_path = File.join(directory, 'app', 'controllers')
So everything in the client1 plugin (MVC-wise) would be considered first when posts controller is called or when the post model is called or when a particular view is requested.
Seems like it should work - let me know.