If any rails-ists are reading, interested in feedback if this makes sense.
In Rails2 “engine” hack, your plugin assets would be copied to the local app in a special directory on app startup, and then referred to with a special :plugin key in rails asset helpers.
This was hacky. Rails3 doesn’t do it anymore. So what do you do with gem/plugin provided assets in Rails3? Some people hack in middleware to make your Rails app serve the plugin assets directly from the plugin directory. There are various other ways to try and do the same thing, get the Rails app to serve assets directly from the plugin directory–rather than ‘middleware’, configuring or over-riding other parts of Rails3.
So another simple approach is to provide a generator for your gem/plugin/engine that copies over all the assets to the local app. That actually works pretty well for ‘end-users’. But it’s annoying for developement of the gem/plugin/engine itself — when you’re hacking on the assets, you have to be constantly re-running your install generator to see the changes? Bah.
So here’s what I figured. Yeah, stick with the generator approach, generating the plugin/gem/engine assets into the local app. DO make sure you put them all in a subdirectory named after your gem, to avoid confusion. DO put some comments on the top warning the end-user that they probably don’t want to edit these files, they were generated. DO provide a generator that only generates assets, so someone can refresh their assets whenever they want without needing to run the rest of your ‘install’ generator, if you have one. If you have a main “install” generator, it can call out to a secondary asset only generator no problem, “generate(‘my_gem:assets’)”.
But okay, it’s still annoying when you’re a developer hacking on those engine-provided assets to have to be constantly re-running that generator to see your changes.
So, okay, I thought, why can’t we do what Rails2 engines did here, in the sense of automatically copying those gem assets over to the local app on startup? We’d probably not want to do that in ‘production’ environment, but only in ‘development’. So we maybe want some line in environments/development.rb to do it. Can we invoke a Rails3 generator in development.rb? Turns out, yes, if you do a couple things.
First thing is your generator needs a few explicit ‘require’ lines in it, that it doesn’t ordinarily need when run from the command line with “rails generate…”. I guess “rails generate” implicitly ‘requires’ the infrastructure, but if you want to be able to call your generator programmatically, you need to be explicit, at the top of your generator file:
require 'rails/generators' require 'rails/generators/base'
No problem. Now in your development.rb (or presumably anywhere else you want to programmatically invoke), you do have to specifically require/load the generator file so it’ll be available. In my gem, I already had a constant defined with the current runtime location of the installed gem, although it still ends up being a kind of ugly line:
require File.join(Blacklight.root, "lib", "generators", "blacklight", "assets_generator.rb")
And then you just need to programmatically invoke the generator class you just loaded. Took me a bunch of poking around to do that, ultimately having to consult the Thor spec files themselves, wasn’t actually documented anywhere else, and trying to look at the code itself wasn’t getting me anywhere. Then I remembered, hey, the specs might provide an example, and they did, leading me here (with some arguments):
Okay, stick those in my environments/development.rb, and all my assets are silently copied over (over-writing what was there before) on startup. Matching Rails2 engine workflow for development better. You still need to restart the app to see the changes, but you don’t need to remember the generator name and call it.
Curious what anyone else thinks of this pattern.