Commit
…to efficiently fetch many models
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
* Added support for Locator.locate_many and Locator.locate_many_signed to efficiently fetch many models. | ||
|
||
*Tom Ward, DHH* | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
dhh
via email
Author
Member
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
require 'active_support' | ||
require 'active_support/core_ext/enumerable' # For Enumerable#index_by | ||
|
||
class GlobalID | ||
module Locator | ||
class << self | ||
|
@@ -15,6 +18,31 @@ def locate(gid, options = {}) | |
end | ||
end | ||
|
||
# Takes an array of GlobalIDs or strings that can be turned into a GlobalIDs. | ||
# The GlobalIDs are located using Model.find(:all, ids), so the models must respond to | ||
# that finder signature. | ||
# | ||
# This approach will efficiently call only one #find per model class, but still interpolate | ||
# the results to match the order in which the gids were passed. | ||
# | ||
# Options: | ||
# * <tt>:only</tt> - A class, module or Array of classes and/or modules that are | ||
# allowed to be located. Passing one or more classes limits instances of returned | ||
# classes to those classes or their subclasses. Passing one or more modules in limits | ||
# instances of returned classes to those including that module. If no classes or | ||
# modules match, +nil+ is returned. | ||
def locate_many(gids, options = {}) | ||
if (allowed_gids = parse_allowed(gids, options[:only])).any? | ||
models_and_ids = allowed_gids.collect { |gid| [ gid.model_name.constantize, gid.model_id ] } | ||
ids_by_model = models_and_ids.group_by(&:first) | ||
loaded_by_model = Hash[ids_by_model.map { |model, ids| [ model, model.find(:all, ids.map(&:last)).index_by(&:id) ] }] | ||
This comment has been minimized.
Sorry, something went wrong.
rafaelfranca
Member
|
||
|
||
models_and_ids.collect { |(model, id)| loaded_by_model[model][id] }.compact | ||
else | ||
[] | ||
end | ||
end | ||
|
||
# Takes either a SignedGlobalID or a string that can be turned into a SignedGlobalID | ||
# | ||
# Options: | ||
|
@@ -27,6 +55,23 @@ def locate_signed(sgid, options = {}) | |
SignedGlobalID.find sgid, options | ||
end | ||
|
||
# Takes an array of SignedGlobalIDs or strings that can be turned into a SignedGlobalIDs. | ||
# The SignedGlobalIDs are located using Model.find(:all, ids), so the models must respond to | ||
# that finder signature. | ||
# | ||
# This approach will efficiently call only one #find per model class, but still interpolate | ||
# the results to match the order in which the gids were passed. | ||
# | ||
# Options: | ||
# * <tt>:only</tt> - A class, module or Array of classes and/or modules that are | ||
# allowed to be located. Passing one or more classes limits instances of returned | ||
# classes to those classes or their subclasses. Passing one or more modules in limits | ||
# instances of returned classes to those including that module. If no classes or | ||
# modules match, +nil+ is returned. | ||
def locate_many_signed(sgids, options = {}) | ||
locate_many sgids.collect { |sgid| SignedGlobalID.parse(sgid) }, options | ||
end | ||
|
||
# Tie a locator to an app. | ||
# Useful when different apps collaborate and reference each others' Global IDs. | ||
# | ||
|
@@ -64,6 +109,10 @@ def find_allowed?(model_class, only = nil) | |
only ? Array(only).any? { |c| model_class <= c } : true | ||
end | ||
|
||
def parse_allowed(gids, only = nil) | ||
gids.collect { |gid| GlobalID.parse(gid) }.compact.select { |gid| find_allowed?(gid.model_class, only) } | ||
end | ||
|
||
def normalize_app(app) | ||
app.to_s.downcase | ||
end | ||
|
2 comments
on commit ab5f975
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merged pending deploy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're saying it's unreleased?
I thought we were switching to the releases feature on GitHub? @rafaelfranca @dhh