exAspArk / batch-loader

:zap: Powerful tool for avoiding N+1 DB or HTTP queries
https://engineering.universe.com/batching-a-powerful-way-to-solve-n-1-queries-every-rubyist-should-know-24e20c6e7b94
MIT License
1.04k stars 52 forks source link

Add lazy eval #92

Open v2kovac opened 5 months ago

v2kovac commented 5 months ago

Follow up to this pr: https://github.com/exAspArk/batch-loader/pull/89

Added a lazy_eval method to chain methods together. I called it lazy_eval instead of lazy to avoid breaking changes. Follows Enumerator::Lazy with eager and force methods that only become available after lazy_eval is called. eager tells the loader to stop accumulating methods in the lazy chain and force a real evaluation on the next call. force tells the loader to sync immediately (kind of redundant, you shouldn't use lazy_eval if you're gonna force)

In graphql, a sync is implicitly done at the correct time so eager isn't required to break the lazy chain. I automatically disable lazy chaining after a sync. This allows a nice looking:

def user_name
  user.lazy_eval.name
end

But in non graphql contexts, you'd have to do:

def lazy_user_name
  user_lazy.lazy_eval.name.eager
end

The code is relatively easy to understand, and the api is fairly analogous to Enumerator::Lazy, but I understand if you think it's too complex. Let me know if you have any thoughts, was fun to work on regardless.

exAspArk commented 5 months ago

Thank you for submitting it! I'll try to find time to review it over the weekend

exAspArk commented 5 months ago

Sorry for the delay here, and thank you for submitting this PR! 🔥

What do you think about isolating this logic to GraphQL only? My current thinking:

Just as an idea, user.lazy.id returning a new BatchLoader::GraphQL somehow that can be synced by GraphQL automatically and point to the "same" @batch_loader? Maybe even creating a new "lazy" GraphQL class with method_missing to return BatchLoader::GraphQL?

I called it lazy_eval instead of lazy to avoid breaking changes.

To clarify, is it to avoid breaking changes if someone uses Enumerator::Lazy?