4 ms·
I want to be able to write: cache(key_name, :ttl => 30.seconds, :dependent => ModelClass) do model.expensive_operation end and have that cache expir
by emmett 17y ago
I want to be able to write:
cache(key_name, :ttl => 30.seconds, :dependent => ModelClass) do
model.expensive_operation
end
and have that cache expire whenever any instance of ModelClass is updated.
With macros, every call to cache() can be expanded into:
ModelClass.register_expiration_callback(keyname)
do_cache(...)
Without macros, there's no way to catch deeply nested cache calls. I fake this by scanning the source for calls to cache() on startup right now.
- avibryant 17y agoI'm not following. Why does "ModelClass.register_expiration_callback(keyname)" have to be in a macro expansion, rather than just the first statement of the cache() method? It doesn't appear to use local scope at all. Can you flesh out the example a bit more? FWIW, I believe that Ruby or (especially) Smalltalk-style syntax for method calls and closures covers such a large set of the things macros are used for that their complexity cost (esp when building tools) outweighs their benefit.
- emmett 17y agoSorry, re-reading that it wasn't totally clear. The ModelClass.register_expiration_callback(keyname) has to be run at parse time, because there's no guarantee that the cache statement will ever be executed before a ModelClass instance is updated. You can't run things at parse time in Ruby (without exotic extensions); that's the whole point of macros. I completely agree that there are a large number of uses for macros that "SmallTalk style" syntax completely replace. It turns out that a lot of things don't really require you to run code at parse time. Take anaphoric-if for example, which is classically implemented as a macro; all you really need is the ability to manipulate the local variables of a closure from the outside. Parse-time execution is not required.