Module: GlobalID::Locator
- Defined in:
- lib/global_id/locator.rb
Defined Under Namespace
Classes: BaseLocator, BlockLocator, Error, InvalidModelIdError, RecordNotFound, RecordUnavailable, UnscopedLocator
Class Attribute Summary collapse
-
.default_locator ⇒ Object
The default locator used when no app-specific locator is found.
Class Method Summary collapse
-
.fetch(gid, options = {}) ⇒ Object
Like .locate, but instead of returning
nilor leaking the backend’s own exceptions when the record can’t be returned, it raises one of two GlobalID-specific errors so callers can tell the cases apart:. -
.locate(gid, options = {}) ⇒ Object
Takes either a GlobalID or a string that can be turned into a GlobalID.
-
.locate_many(gids, options = {}) ⇒ Object
Takes an array of GlobalIDs or strings that can be turned into a GlobalIDs.
-
.locate_many_signed(sgids, options = {}) ⇒ Object
Takes an array of SignedGlobalIDs or strings that can be turned into a SignedGlobalIDs.
-
.locate_signed(sgid, options = {}) ⇒ Object
Takes either a SignedGlobalID or a string that can be turned into a SignedGlobalID.
- .locator_for(gid) ⇒ Object
-
.use(app, locator = nil, &locator_block) ⇒ Object
Tie a locator to an app.
Class Attribute Details
.default_locator ⇒ Object
The default locator used when no app-specific locator is found.
21 22 23 |
# File 'lib/global_id/locator.rb', line 21 def default_locator @default_locator end |
Class Method Details
.fetch(gid, options = {}) ⇒ Object
Like .locate, but instead of returning nil or leaking the backend’s own exceptions when the record can’t be returned, it raises one of two GlobalID-specific errors so callers can tell the cases apart:
-
GlobalID::Locator::RecordNotFound when the record no longer exists. Retrying won’t help.
-
GlobalID::Locator::RecordUnavailable when the record couldn’t be located due to any other failure in the backend, like a database connection error. The record may still exist, so retrying may succeed.
The distinction is drawn without knowing the backend’s exception classes: the record is looked up through a query that doesn’t raise on missing records (like .locate_many’s :ignore_missing), so an empty result means the record is gone, while an error from the query itself means the backend is unavailable.
Returns nil for a blank or unparseable GlobalID, or one disallowed by the :only option, just like .locate, and accepts the same options.
Note: custom locators registered with .use need to implement locate_many with support for the :ignore_missing option for this method to work.
71 72 73 74 75 76 77 |
# File 'lib/global_id/locator.rb', line 71 def fetch(gid, = {}) gid = GlobalID.parse(gid) return unless gid && find_allowed?(gid, [:only]) fetch_record(gid, .except(:only)) or raise RecordNotFound, "Couldn't find record for #{gid}" end |
.locate(gid, options = {}) ⇒ Object
Takes either a GlobalID or a string that can be turned into a GlobalID
Options:
-
:includes- A Symbol, Array, Hash or combination of them. The same structure you would pass into aincludesmethod of Active Record. If present, locate will load all the relationships specified here. See guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. -
:only- 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,nilis returned.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/global_id/locator.rb', line 35 def locate(gid, = {}) gid = GlobalID.parse(gid) return unless gid && find_allowed?(gid, [:only]) locator = locator_for(gid) if locator.method(:locate).arity == 1 GlobalID.deprecator.warn "It seems your locator is defining the `locate` method only with one argument. Please make sure your locator is receiving the options argument as well, like `locate(gid, options = {})`." locator.locate(gid) else locator.locate(gid, .except(:only)) end end |
.locate_many(gids, options = {}) ⇒ Object
Takes an array of GlobalIDs or strings that can be turned into a GlobalIDs. All GlobalIDs must belong to the same app, as they will be located using the same locator using its locate_many method.
By default the GlobalIDs will be located using Model.find(array_of_ids), so the models must respond to that finder signature.
This approach will efficiently call only one #find (or #where(id: id), when using ignore_missing) per model class, but still interpolate the results to match the order in which the gids were passed.
Options:
-
:includes- A Symbol, Array, Hash or combination of them The same structure you would pass into a includes method of Active Record. @see guides.rubyonrails.org/active_record_querying.html#eager-loading-associations If present, locate_many will load all the relationships specified here. Note: It only works if all the gids models have that relationships. -
:only- 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,nilis returned. -
:ignore_missing- By default, locate_many will call #find on the model to locate the ids extracted from the GIDs. In Active Record (and other data stores following the same pattern), #find will raise an exception if a named ID can’t be found. When you set this option to true, we will use #where(id: ids) instead, which does not raise on missing records.
104 105 106 107 108 109 110 111 |
# File 'lib/global_id/locator.rb', line 104 def locate_many(gids, = {}) if (allowed_gids = parse_allowed(gids, [:only])).any? locator = locator_for(allowed_gids.first) locator.locate_many(allowed_gids, ) else [] end end |
.locate_many_signed(sgids, options = {}) ⇒ Object
Takes an array of SignedGlobalIDs or strings that can be turned into a SignedGlobalIDs. The SignedGlobalIDs are located using Model.find(array_of_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:
-
:includes- A Symbol, Array, Hash or combination of them The same structure you would pass into a includes method of Active Record. @see guides.rubyonrails.org/active_record_querying.html#eager-loading-associations If present, locate_many_signed will load all the relationships specified here. Note: It only works if all the gids models have that relationships. -
:only- 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,nilis returned.
147 148 149 |
# File 'lib/global_id/locator.rb', line 147 def locate_many_signed(sgids, = {}) locate_many sgids.collect { |sgid| SignedGlobalID.parse(sgid, .slice(:for)) }.compact, end |
.locate_signed(sgid, options = {}) ⇒ Object
Takes either a SignedGlobalID or a string that can be turned into a SignedGlobalID
Options:
-
:includes- A Symbol, Array, Hash or combination of them The same structure you would pass into a includes method of Active Record. @see guides.rubyonrails.org/active_record_querying.html#eager-loading-associations If present, locate_signed will load all the relationships specified here. -
:only- 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,nilis returned.
125 126 127 |
# File 'lib/global_id/locator.rb', line 125 def locate_signed(sgid, = {}) SignedGlobalID.find sgid, end |
.locator_for(gid) ⇒ Object
179 180 181 |
# File 'lib/global_id/locator.rb', line 179 def locator_for(gid) @locators.fetch(normalize_app(gid.app)) { default_locator } end |
.use(app, locator = nil, &locator_block) ⇒ Object
Tie a locator to an app. Useful when different apps collaborate and reference each others’ Global IDs.
The locator can be either a block or a class.
Using a block:
GlobalID::Locator.use :foo do |gid, |
FooRemote.const_get(gid.model_name).find(gid.model_id)
end
Using a class:
GlobalID::Locator.use :bar, BarLocator.new
class BarLocator
def locate(gid, = {})
@search_client.search name: gid.model_name, id: gid.model_id
end
end
171 172 173 174 175 176 177 |
# File 'lib/global_id/locator.rb', line 171 def use(app, locator = nil, &locator_block) raise ArgumentError, 'No locator provided. Pass a block or an object that responds to #locate.' unless locator || block_given? URI::GID.validate_app(app) @locators[normalize_app(app)] = locator || BlockLocator.new(locator_block) end |