Closed mzikherman closed 10 years ago
This looks great. Could you please update CHANGELOG, too (add a Next Release). Thanks.
Thanks a ton for this! It looks great, with the small exception of the case of cache freshness on Mongoid.garnered_find(["m1", "m2"])
, per my comment. Can you verify that with a test?
So, the specific issue in https://github.com/artsy/garner/issues/62 failed as a spec here: https://github.com/artsy/garner/pull/63/files#diff-53b60d8911baf05f72df2717fd871d24R115 with my changes.
Digging in it seems that "m1" and ["m1"] was is binding to the same key, so explicitly adding a value when a singular object is passed as an array was the only way I saw to fix (w/o going into how they get generated- which I will do later today if I have time). This fix now seems kind of kludgy. What do you think?
Great work @mzikherman, and excellent spec reproducing the error.
I agree with you that any fix necessitates passing in different keys depending on whether garnered_find
was called with array arguments (e.g., ["m1"]
) versus single object arguments (e.g., "m1"
). One way to make the solution less kludgy is to just use the actual arguments passed in as the key. So, instead of keying both key({ :array => true })
and key({ :source => :garnered_find })
, we could just do a single key({ :garnered_find_args => args })
. Something like this:
def self.garnered_find(*args)
identity = Garner::Cache::Identity.new
args.flatten.each do |arg|
binding = identify(arg)
identity = identity.bind(binding)
end
identity.key({ :garnered_find_args => args }) do
find(*args)
end
end
Would that work here?
Great call on the keys @fancyremarker . Rather than including the semi-arbitrary :array => true or :source => :garnered_find , we have the args right there and we might as well include them. That also solves the 'special case' of an array of one item vs an item itself cleanly. I just pushed an update to the PR that incorporates this- I'd love your feedback. I can of course rebase/squash if and when this might be mergeable.
@mzikherman: This looks great. See my comment; I'd like to hear your thoughts as I may be mistaken. Also, no need to rebase/squash. Better to keep the context for our conversation intact, IMO.
I'm still a little bit confused as to the need for flatten. Could you clarify a bit more and I'll try to write a spec? Just pushed a spec that find only gets called once when garnered_find called on an array of one object - is that what you meant?
The need for flatten
comes when you have a single array argument with N members, instead of N arguments. Consider the following:
garnered_find("foo", "bar")
yields
# args = ["foo", "bar"]
# within args.each loop...
binding = identify("foo")
identity = identity.bind(binding)
binding = identify("bar")
identity = identity.bind(binding)
While garnered_find(["foo", "bar"])
yields
# args = [["foo", "bar"]]
# within args.each loop...
binding = identify(["foo", "bar"])
identity = identity.bind(binding)
identify(["foo", "bar"])
won't resolve to a valid proxy_binding
, since the array isn't a valid ID or slug, and thus won't have a garner_cache_key
, which means it can't be cached.
Does that help explain things at all?
Ah yes, it does. I just pushed a trivial spec that sure enough has find being called twice on foo
in your example- indicating not caching when called with an array, and correctly with flatten (which makes total sense now- thanks!).
Yea, that seems totally obvious now, dont know how I didn't get that.
Thanks @mzikherman, merging. Want to cut and release a new gem version?
AWESOME!! I've never done that before but would love to.
Modifies
garnered_find
to accept a splat to match the API of Mongoid'sfind
.You can now call
Artist.garnered_find('andy-warhol','pablo-picasso')
orArtist.garnered_find(['andy-warhol','pablo-picasso'])
I think this is a straightforward fix except for the following cases:
In these cases, the return type should match what was passed in. I couldn't figure out a great way to figure out from *args what the original arguments were, thus: https://github.com/artsy/garner/pull/63/files#diff-617bcfaf854a9b15e531702bca4eabafR64
That has the effect of properly returning an array of one (containing the object), vs the object itself (matching
find
).