Class: ROM::Relation
- Inherits:
-
Object
- Object
- ROM::Relation
- Extended by:
- AutoCurry, Initializer, Notifications::Listener, Plugins::ClassMethods, ClassInterface, SettingProxy
- Includes:
- Dry::Core::Memoizable, Pipeline, Commands, Materializable
- Defined in:
- lib/rom/relation.rb,
lib/rom/relation/name.rb,
lib/rom/relation/wrap.rb,
lib/rom/relation/graph.rb,
lib/rom/compat/relation.rb,
lib/rom/relation/loaded.rb,
lib/rom/relation/curried.rb,
lib/rom/relation/combined.rb,
lib/rom/relation/commands.rb,
lib/rom/relation/composite.rb,
lib/rom/relation/materializable.rb,
lib/rom/relation/class_interface.rb
Overview
Base relation class
Relation is a proxy for the dataset object provided by the gateway. It can forward methods to the dataset, which is why the "native" interface of the underlying gateway is available in the relation
Individual adapters sets up their relation classes and provide different APIs depending on their persistence backend.
Direct Known Subclasses
Defined Under Namespace
Modules: ClassInterface, Commands, Materializable Classes: Combined, Composite, Curried, Graph, Loaded, Name, Wrap
Constant Summary collapse
- NOOP_OUTPUT_SCHEMA =
Default no-op output schema which is called in
Relation#each
-> tuple { tuple }.freeze
Instance Attribute Summary collapse
-
#associations ⇒ Setup::registry
readonly
Relation associations.
-
#auto_map ⇒ TrueClass, FalseClass
readonly
private
Whether or not a relation and its compositions should be auto-mapped.
-
#auto_struct ⇒ TrueClass, FalseClass
readonly
private
Whether or not tuples should be auto-mapped to structs.
-
#commands ⇒ Commandregistry
readonly
private
Command registry.
- #config ⇒ ROM::Configurable::Config readonly private
-
#dataset ⇒ Object
readonly
Dataset used by the relation provided by relation's gateway.
-
#datasets ⇒ registry
readonly
Relation associations.
-
#inflector ⇒ Dry::Inflector
readonly
The default inflector.
-
#input_schema ⇒ Object#[]
readonly
private
Tuple processing function, uses schema or defaults to Hash[].
-
#mappers ⇒ registry
readonly
An optional mapper registry (empty by default).
-
#meta ⇒ Hash
readonly
private
Meta data stored in a hash.
-
#name ⇒ Name
readonly
The relation name.
-
#output_schema ⇒ Object#[]
readonly
private
Tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA.
-
#registry ⇒ registry
readonly
Registry::Root with runtime dependency resolving.
-
#schema ⇒ Setup::registry
readonly
The canonical schema.
-
#schemas ⇒ Setup::registry
readonly
Relation schemas.
-
#struct_namespace(ns) ⇒ Relation
readonly
Return a new relation configured with the provided struct namespace.
Class Method Summary collapse
-
.[](adapter) ⇒ Class
extended
from ClassInterface
Return adapter-specific relation subclass.
- .auto_curried_methods ⇒ Object extended from AutoCurry private
-
.auto_curry(name, &block) ⇒ Object
extended
from AutoCurry
private
Auto-curry a method.
- .auto_curry_busy? ⇒ Boolean extended from AutoCurry private
- .auto_curry_guard ⇒ Object extended from AutoCurry private
- .curried ⇒ Object extended from ClassInterface private
-
.forward(*methods) ⇒ Object
extended
from ClassInterface
Dynamically define a method that will forward to the dataset and wrap response in the relation itself.
- .inherited(klass) ⇒ Object private
- .new(dataset = nil, **opts) ⇒ Object
-
.plugins ⇒ Object
extended
from Plugins::ClassMethods
Return all available plugins for the component type.
- .setting_mapping ⇒ Object
-
.subscribe(event_id, query = EMPTY_HASH, &block) ⇒ Object
extended
from Notifications::Listener
Subscribe to events.
-
.use(name, **options) ⇒ Object
extended
from Plugins::ClassMethods
Include a registered plugin in this relation class.
-
.view_methods ⇒ Object
private
This is used by the deprecated command => relation view delegation syntax.
Instance Method Summary collapse
-
#>>(other) ⇒ Relation::Composite
included
from Pipeline::Operator
Compose two relation with a left-to-right composition.
-
#[](name) ⇒ Attribute
Return schema attribute.
-
#adapter ⇒ Symbol
private
The wrapped relation's adapter identifier ie :sql or :http.
-
#as(aliaz) ⇒ Relation
Return a new relation with an aliased name.
- #attr_ast ⇒ Object private
- #auto_map? ⇒ Boolean private
- #auto_struct? ⇒ Boolean private
-
#call ⇒ Relation::Loaded
Loads a relation.
-
#combine(*args) ⇒ Relation
Combine with other relations using configured associations.
-
#combine_with(*others) ⇒ Relation::Graph
Composes with other relations.
-
#command(type, mapper: nil, use: EMPTY_ARRAY, plugins_options: EMPTY_HASH, **opts) ⇒ ROM::Command
included
from Commands
Return a command for the relation.
-
#curried? ⇒ false
private
Returns if this relation is curried.
-
#each {|Hash| ... } ⇒ Enumerator
Yields relation tuples.
-
#eager_load(assoc) ⇒ Relation
Return a graph node prepared by the given association.
-
#first ⇒ Object
included
from Materializable
Return first tuple from a relation coerced to an array.
-
#foreign_key(name) ⇒ Symbol
private
Return a foreign key name for the provided relation name.
-
#gateway ⇒ Symbol
private
Return name of the source gateway of this relation.
-
#graph? ⇒ false
private
Returns if this relation is a graph.
-
#map_to(klass, **opts) ⇒ Relation
Return a new relation that will map its tuples to instances of the provided class.
-
#map_with(*names, **opts) ⇒ Relation::Composite
Maps relation with custom mappers available via registry.
- #mapper ⇒ Object private
- #meta_ast ⇒ Object private
-
#new(dataset, **new_opts) ⇒ Object
Return a new relation with provided dataset and additional options.
-
#node(name) ⇒ Relation
Create a graph node for a given association identifier.
- #nodes(*args) ⇒ Object private
-
#one ⇒ Object
included
from Materializable
Delegate to loaded relation and return one object.
-
#one! ⇒ Object
included
from Materializable
Delegate to loaded relation and return one object.
-
#preload_assoc(assoc, other) ⇒ Relation::Curried
private
Preload other relation via association.
-
#schema? ⇒ TrueClass, FalseClass
private
Returns true if a relation has schema defined.
-
#to_a ⇒ Array<Hash>
Materializes a relation into an array.
-
#to_ast ⇒ Array
Returns AST for the wrapped relation.
-
#with(opts) ⇒ Relation
Returns a new instance with the same dataset but new options.
-
#wrap(*names) ⇒ Wrap
Wrap other relations using association names.
-
#wrap? ⇒ false
private
Return if this is a wrap relation.
-
#wrap_around(*others) ⇒ Relation::Wrap
Wrap around other relations.
Instance Attribute Details
#associations ⇒ Setup::registry (readonly)
Returns Relation associations.
128 |
# File 'lib/rom/relation.rb', line 128 option :associations, default: -> { registry.associations.scoped(config.component.id) } |
#auto_map ⇒ TrueClass, FalseClass (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns Whether or not a relation and its compositions should be auto-mapped.
145 |
# File 'lib/rom/relation.rb', line 145 option :auto_map, default: -> { config.auto_map } |
#auto_struct ⇒ TrueClass, FalseClass (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns Whether or not tuples should be auto-mapped to structs.
150 |
# File 'lib/rom/relation.rb', line 150 option :auto_struct, default: -> { config.auto_struct } |
#commands ⇒ Commandregistry (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns Command registry.
176 177 178 |
# File 'lib/rom/relation.rb', line 176 option :commands, default: -> do registry.commands.scoped(config.component.id, opts: {adapter: adapter}) end |
#config ⇒ ROM::Configurable::Config (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
93 |
# File 'lib/rom/relation.rb', line 93 option :config, default: -> { self.class.config } |
#dataset ⇒ Object (readonly)
Returns dataset used by the relation provided by relation's gateway.
124 |
# File 'lib/rom/relation.rb', line 124 option :dataset, default: -> { datasets.infer(config.component.id) } |
#datasets ⇒ registry (readonly)
Returns Relation associations.
119 |
# File 'lib/rom/relation.rb', line 119 option :datasets, default: -> { registry.datasets.scoped(config.component.id, config: config) } |
#inflector ⇒ Dry::Inflector (readonly)
Returns The default inflector.
107 |
# File 'lib/rom/relation.rb', line 107 option :inflector, default: -> { config.component.inflector } |
#input_schema ⇒ Object#[] (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns tuple processing function, uses schema or defaults to Hash[].
133 |
# File 'lib/rom/relation.rb', line 133 option :input_schema, default: -> { schema.to_input_hash } |
#mappers ⇒ registry (readonly)
Returns an optional mapper registry (empty by default).
159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/rom/relation.rb', line 159 option :mappers, -> mappers { if mappers.is_a?(Hash) registry.mappers .scoped(config.component.id, opts: {adapter: config.component.adapter}) .import(mappers) else mappers end }, default: -> { registry.mappers.scoped(config.component.id, opts: {adapter: adapter}) } |
#meta ⇒ Hash (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns Meta data stored in a hash.
183 |
# File 'lib/rom/relation.rb', line 183 option :meta, reader: true, default: -> { EMPTY_HASH } |
#name ⇒ Name (readonly)
Returns The relation name.
98 |
# File 'lib/rom/relation.rb', line 98 option :name, default: -> { Name[config.component.id, config.component.dataset] } |
#output_schema ⇒ Object#[] (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA.
138 139 140 |
# File 'lib/rom/relation.rb', line 138 option :output_schema, default: lambda { schema.any?(&:read?) ? schema.to_output_hash : NOOP_OUTPUT_SCHEMA } |
#registry ⇒ registry (readonly)
Returns Registry::Root with runtime dependency resolving.
102 |
# File 'lib/rom/relation.rb', line 102 option :registry, default: -> { self.class.registry(config: config) } |
#schema ⇒ Setup::registry (readonly)
Returns The canonical schema.
115 |
# File 'lib/rom/relation.rb', line 115 option :schema, default: -> { schemas.infer(config.component.id) } |
#schemas ⇒ Setup::registry (readonly)
Returns Relation schemas.
111 |
# File 'lib/rom/relation.rb', line 111 option :schemas, default: -> { registry.schemas.scoped(config.component.id, config: config) } |
#struct_namespace(ns) ⇒ Relation (readonly)
Return a new relation configured with the provided struct namespace
155 |
# File 'lib/rom/relation.rb', line 155 option :struct_namespace, reader: false, default: -> { config.struct_namespace } |
Class Method Details
.[](adapter) ⇒ Class Originally defined in module ClassInterface
Return adapter-specific relation subclass
.auto_curried_methods ⇒ Object Originally defined in module AutoCurry
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
.auto_curry(name, &block) ⇒ Object Originally defined in module AutoCurry
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Auto-curry a method
.auto_curry_busy? ⇒ Boolean Originally defined in module AutoCurry
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
.auto_curry_guard ⇒ Object Originally defined in module AutoCurry
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
.curried ⇒ Object Originally defined in module ClassInterface
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
.forward(*methods) ⇒ Object Originally defined in module ClassInterface
Dynamically define a method that will forward to the dataset and wrap response in the relation itself
.inherited(klass) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rom/relation.rb', line 59 def self.inherited(klass) super adapter = config.component.adapter klass.configure do |config| # Relations that inherit from an adapter subclass are not considered abstract anymore # You can override it later inside your class' config of course if adapter config.component.abstract = false # Use klass' name to set defaults # # ie `Relations::Users` assumes :users id and a corresponding dataset (table in case of SQL) # # TODO: make this behavior configurable? # if klass.name config.component.id = config.component.inflector.component_id(klass.name).to_sym config.component.dataset = config.component.id else config.component.id = :anonymous end end end end |
.new(dataset = nil, **opts) ⇒ Object
186 187 188 189 190 191 192 |
# File 'lib/rom/relation.rb', line 186 def self.new(dataset = nil, **opts) if dataset super(**opts, dataset: dataset) else super(**opts) end end |
.plugins ⇒ Object Originally defined in module Plugins::ClassMethods
Return all available plugins for the component type
.setting_mapping ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/rom/compat/relation.rb', line 11 def setting_mapping @setting_mapping ||= { auto_map: [], auto_struct: [], struct_namespace: [], wrap_class: [], adapter: [:component, :adapter], gateway: [:component, :gateway], schema_class: [:schema, :constant], schema_dsl: [:schema, :dsl_class], schema_attr_class: [:schema, :attr_class], schema_inferrer: [:schema, :inferrer] }.freeze end |
.subscribe(event_id, query = EMPTY_HASH, &block) ⇒ Object Originally defined in module Notifications::Listener
Subscribe to events
.use(name, **options) ⇒ Object Originally defined in module Plugins::ClassMethods
Include a registered plugin in this relation class
.view_methods ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This is used by the deprecated command => relation view delegation syntax
31 32 33 34 35 36 |
# File 'lib/rom/compat/relation.rb', line 31 def self.view_methods ancestor_methods = ancestors.reject { |klass| klass == self } .map(&:instance_methods).flatten(1) instance_methods - ancestor_methods + auto_curried_methods.to_a end |
Instance Method Details
#>>(other) ⇒ Relation::Composite Originally defined in module Pipeline::Operator
Compose two relation with a left-to-right composition
#[](name) ⇒ Attribute
Return schema attribute
208 209 210 |
# File 'lib/rom/relation.rb', line 208 def [](name) schema[name] end |
#adapter ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns The wrapped relation's adapter identifier ie :sql or :http.
556 557 558 |
# File 'lib/rom/relation.rb', line 556 def adapter config.component.adapter end |
#as(aliaz) ⇒ Relation
Return a new relation with an aliased name
549 550 551 |
# File 'lib/rom/relation.rb', line 549 def as(aliaz) with(name: name.as(aliaz)) end |
#attr_ast ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
468 469 470 |
# File 'lib/rom/relation.rb', line 468 def attr_ast schema.map(&:to_read_ast) end |
#auto_map? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
481 482 483 |
# File 'lib/rom/relation.rb', line 481 def auto_map? (auto_map || auto_struct) && ![:combine_type] end |
#auto_struct? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
486 487 488 |
# File 'lib/rom/relation.rb', line 486 def auto_struct? auto_struct && ![:combine_type] end |
#call ⇒ Relation::Loaded
Loads a relation
355 356 357 |
# File 'lib/rom/relation.rb', line 355 def call Loaded.new(self) end |
#combine(*associations) ⇒ Relation #combine(*associations, **nested_associations) ⇒ Relation #combine(associations) ⇒ Relation
Combine with other relations using configured associations
255 256 257 |
# File 'lib/rom/relation.rb', line 255 def combine(*args) combine_with(*nodes(*args)) end |
#combine_with(*others) ⇒ Relation::Graph
Composes with other relations
266 267 268 |
# File 'lib/rom/relation.rb', line 266 def combine_with(*others) Combined.new(self, others) end |
#command(type, mapper: nil, use: EMPTY_ARRAY, plugins_options: EMPTY_HASH, **opts) ⇒ ROM::Command Originally defined in module Commands
Return a command for the relation
This method can either return an existing custom command identified
by type
param, or generate a command dynamically based on relation
AST.
#curried? ⇒ false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns if this relation is curried
373 374 375 |
# File 'lib/rom/relation.rb', line 373 def curried? false end |
#each {|Hash| ... } ⇒ Enumerator
Yields relation tuples
Every tuple is processed through Relation#output_schema, it's a no-op by default
221 222 223 224 225 226 227 228 229 |
# File 'lib/rom/relation.rb', line 221 def each(&block) return to_enum unless block_given? if auto_map? mapper.(dataset.map { |tuple| output_schema[tuple] }).each(&block) else dataset.each { |tuple| yield(output_schema[tuple]) } end end |
#eager_load(assoc) ⇒ Relation
Return a graph node prepared by the given association
304 305 306 307 308 309 310 311 312 |
# File 'lib/rom/relation.rb', line 304 def eager_load(assoc) relation = assoc.prepare(self) if assoc.override? relation.(assoc) else relation.preload_assoc(assoc) end end |
#first ⇒ Object Originally defined in module Materializable
Return first tuple from a relation coerced to an array
#foreign_key(name) ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Return a foreign key name for the provided relation name
576 577 578 579 580 581 582 583 584 |
# File 'lib/rom/relation.rb', line 576 def foreign_key(name) attr = schema.foreign_key(name.dataset) if attr attr.name else :"#{inflector.singularize(name.dataset)}_id" end end |
#gateway ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Return name of the source gateway of this relation
565 566 567 |
# File 'lib/rom/relation.rb', line 565 def gateway config.component.gateway end |
#graph? ⇒ false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns if this relation is a graph
382 383 384 |
# File 'lib/rom/relation.rb', line 382 def graph? false end |
#map_to(klass, **opts) ⇒ Relation
Return a new relation that will map its tuples to instances of the provided class
535 536 537 |
# File 'lib/rom/relation.rb', line 535 def map_to(klass, **opts) with(opts.merge(auto_map: false, auto_struct: true, meta: {model: klass})) end |
#map_with(*mappers) ⇒ Relation::Composite #map_with(*mappers, auto_map: true) ⇒ Relation::Composite
Maps relation with custom mappers available via registry
When auto_map
is enabled, your mappers will be applied after performing
default auto-mapping. This means that you can compose complex relations
and have them auto-mapped, and use much simpler custom mappers to adjust
resulting data according to your requirements.
521 522 523 |
# File 'lib/rom/relation.rb', line 521 def map_with(*names, **opts) super(*names).with(opts) end |
#mapper ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
491 492 493 |
# File 'lib/rom/relation.rb', line 491 def mapper mappers[to_ast] end |
#meta_ast ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
473 474 475 476 477 478 |
# File 'lib/rom/relation.rb', line 473 def = self..merge(dataset: name.dataset, alias: name.aliaz, struct_namespace: [:struct_namespace]) [:model] = false unless auto_struct? || [:model] end |
#new(dataset, **new_opts) ⇒ Object
Return a new relation with provided dataset and additional options
Use this method whenever you need to use dataset API to get a new dataset and you want to return a relation back. Typically relation API should be enough though. If you find yourself using this method, it might be worth to consider reporting an issue that some dataset functionality is not available through relation API.
422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/rom/relation.rb', line 422 def new(dataset, **new_opts) opts = if new_opts.empty? elsif new_opts.key?(:schema) .merge(new_opts).reject { |k, _| k == :input_schema || k == :output_schema } else .merge(new_opts) end self.class.new(**opts, dataset: dataset) end |
#node(name) ⇒ Relation
Create a graph node for a given association identifier
291 292 293 294 295 |
# File 'lib/rom/relation.rb', line 291 def node(name) assoc = associations[name] other = assoc.node other.eager_load(assoc) end |
#nodes(*args) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/rom/relation.rb', line 271 def nodes(*args) args.reduce([]) do |acc, arg| case arg when Symbol acc << node(arg) when Hash acc.concat(arg.map { |name, opts| node(name).combine(opts) }) when Array acc.concat(arg.map { |opts| nodes(opts) }.reduce(:concat)) end end end |
#one ⇒ Object Originally defined in module Materializable
Delegate to loaded relation and return one object
#one! ⇒ Object Originally defined in module Materializable
Delegate to loaded relation and return one object
#preload_assoc(assoc, other) ⇒ Relation::Curried
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Preload other relation via association
This is used internally when relations are composed
321 322 323 |
# File 'lib/rom/relation.rb', line 321 def preload_assoc(assoc, other) assoc.preload(self, other) end |
#schema? ⇒ TrueClass, FalseClass
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true if a relation has schema defined
400 401 402 |
# File 'lib/rom/relation.rb', line 400 def schema? !schema.empty? end |
#to_a ⇒ Array<Hash>
Materializes a relation into an array
364 365 366 |
# File 'lib/rom/relation.rb', line 364 def to_a to_enum.to_a end |
#to_ast ⇒ Array
Returns AST for the wrapped relation
463 464 465 |
# File 'lib/rom/relation.rb', line 463 def to_ast [:relation, [name.relation, attr_ast, ]] end |
#with(opts) ⇒ Relation
Returns a new instance with the same dataset but new options
447 448 449 450 451 452 453 454 455 456 |
# File 'lib/rom/relation.rb', line 447 def with(opts) = if opts.key?(:meta) opts.merge(meta: .merge(opts[:meta])) else opts end new(dataset, **, **) end |
#wrap(*names) ⇒ Wrap
Wrap other relations using association names
335 336 337 |
# File 'lib/rom/relation.rb', line 335 def wrap(*names) wrap_around(*names.map { |n| associations[n].wrap }) end |
#wrap? ⇒ false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Return if this is a wrap relation
391 392 393 |
# File 'lib/rom/relation.rb', line 391 def wrap? false end |
#wrap_around(*others) ⇒ Relation::Wrap
Wrap around other relations
346 347 348 |
# File 'lib/rom/relation.rb', line 346 def wrap_around(*others) wrap_class.new(self, others) end |