Implement Exporting feature - #372
Conversation
|
Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @arthurnn (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
|
If we're going to return a Concurrent::Future, we might as well pass an Obligation as the We currently do some early work outside the future / before the original write is completed, but at a glance, I see no special reason we must do things that way. In which case, we could put the whole |
7ca65a3 to
5978e30
Compare
|
I managed to implement what you said, and I also added a few more changes. An exporter now inherits from the |
|
That seems rather unduly complicated for the set of exporters we've currently seen -- especially the introduction of a FileExporter. (Which of my suggested approaches were you following? You moved the creation of the future up to the main method per #2, but kept a wait parameter along the lines of #1. 😕) |
|
That's true, but it is pretty flexible. But you're right, it might be quite useless for exporters to depend on eachother. I will see what I can do. |
|
An exporter now only waits for |
4bd8594 to
8223167
Compare
|
I am now using this code in production for exporting Brotli files. I created the gem |
d0cbf16 to
41317ac
Compare
|
I managed to get a thread pool working. |
|
89e2c73 seemed approachably small, and wanting only modifications. This now feels disproportionately complex, and I'm not particularly interested in trying to walk that back point by point; I'll defer to Team Sprockets if someone else still sees a path forward. |
|
Ok, I will wait for a comment by others. This whole thing was inspired by this comment by @schneems. Though I have some questions:
|
Generally it doesn't matter while you're making the PR. We usually ask a feature PR to be squashed at the end into 1 commit. It helps with readability and if we have to revert a feature we can just revert that one commit. I can help you out there if you have questions/problems.
Ordered arguments are really hard to use as an API. You can't easily add or remove arguments in the future. That's one reason why hash based APIs like rack are popular because they're easily extensible. The downside is that they can become a rats-nest and become very difficult to work with, even harder to deprecate. On the overall direction. I'm not sure. The whole concept of archivers is very sprockets-ish in design, i'm not sure if that's good or bad. It does mean we need more thought towards the API as it will outlive anything else we do today. I'll need to bash around some things another day. Maybe I can dig into this soon. Can you rebase this? I merged a change to the concurrent futures into master and it's conflicting with this PR. |
666e4c3 to
294fd31
Compare
|
I agree that the APIs need some thought. As I learned Rails a few years ago, I remember assets management and precompilation being one of the most confusing things, so now I'm happy to help working on it. As for the ordered arguments, I can certainly replace that with a hash but |
| unless mime_types.is_a? Array | ||
| if mime_types.is_a? String | ||
| mime_types = [mime_types] | ||
| elsif mime_types < Exporter |
There was a problem hiding this comment.
What does mime_types < Exporter do?
There was a problem hiding this comment.
It evaluates to true if mime_types is a class that inherits from Exporter (which is different from is_a? because mime_types is a class, not an instance)
http://stackoverflow.com/questions/4545518/test-whether-a-ruby-class-is-a-subclass-of-another-class
I started with #372 and made some style and behavior changes. The idea remains the same, that all writing to disk will happen via an exporter which gives us a place we can modify behavior from a gem. The interface for an exporter is as follows: An exporter is a class, a new instance will be initialized for each asset. It has access to: - asset - environment - directory - target There is a `setup` method that can be used to create mutable state before the exporter is called. A `skip?` method is run synchronously to determine if the exporter should execute or not. Since this is the only method guaranteed to run synchronously and since our logger is not threadsafe, we can do notifications in this method. The logger is passed into `skip?` to prevent people from accidentally storing a reference and trying to log when things are being called in different threads. Finally a `call` method does the work of writing a file to disk. It takes no arguments. Other changes? The top level lib/sprockets dir is really large so I moved the built in exporters to their own directory. Added documentation. Only allow registering of classes for exporters instead of procs. Added a zopfli exporter by default if you have the gem loaded before sprockets gets required. Switch back to using `compile` for the manifest interface. Use promises to chain asset compiles, this also handles #381. Fix tests to not use `rm -rf` with a system call in a few places
|
Zopfli and an archiver interface (called exporters) are merged into master. Check out the docs and source if you need to write one. Or ping me with questions on this thread. Thanks for your input. The final solution was a result of everyone's inputs and help. Also I pulled in this commit and built on it. Thanks again for all your help! |
I created an exporting feature, which replaces
manifest.compile. There is now an api for exporters, like there is an api for processors. Gzip is an example of an exporter. You can register an exporter like this:There are no breaking changes. I created this api so I could implement a Brotli exporter, which I will use with
ngx_brotli. That will look like this:There is room for improvement:
(env, asset, target, dir, logger, wait)is quite long'text/html'matches'text/*'? If not, I can certainly implement that, it can also be used for processors then.I will write a guide when this (or something like this) gets merged.