Class: ROM::Schema
- Inherits:
-
Object
- Object
- ROM::Schema
- Extended by:
- Initializer
- Includes:
- Enumerable, Memoizable
- Defined in:
- lib/rom/schema.rb,
lib/rom/schema/inferrer.rb,
lib/rom/compat/schema/dsl.rb,
lib/rom/schema/associations_dsl.rb
Overview
Relation schema
Schemas hold detailed information about relation tuples, including their primitive types (String, Integer, Hash, etc. or custom classes), as well as various meta information like primary/foreign key and literally any other information that a given database adapter may need.
Adapters can extend this class and it can be used in adapter-specific relations. In example rom-sql extends schema with Association DSL and many additional SQL-specific APIs in schema types.
Schemas are used for projecting canonical relations into other relations and every relation object maintains its schema. This means that we always have all information about relation tuples, even when a relation was projected and diverged from its canonical form.
Furthermore schema attributes know their source relations, which makes it possible to merge schemas from multiple relations and maintain information about the source relations. In example when two relations are joined, their schemas are merged, and we know which attributes belong to which relation.
Direct Known Subclasses
Defined Under Namespace
Classes: AssociationsDSL, DSL, Inferrer
Constant Summary collapse
- EMPTY_ASSOCIATION_SET =
EMPTY_HASH.freeze
- DEFAULT_INFERRER =
Inferrer.new(enabled: false).freeze
- HASH_SCHEMA =
Types::Coercible::Hash .schema(EMPTY_HASH) .with_type_transform(type_transformation)
Instance Attribute Summary collapse
- #__memoized__ ⇒ Object included from Memoizable readonly private
-
#associations ⇒ Setup::Registry::Root
readonly
Optional association set (this is adapter-specific).
-
#attributes ⇒ Array
readonly
Array with schema attributes.
-
#canonical ⇒ Symbol
readonly
The canonical schema which is carried in all schema instances.
-
#inferrer ⇒ #call
readonly
An optional inferrer object used in
finalize!
. -
#name ⇒ Symbol
readonly
The name of this schema.
-
#primary_key_name ⇒ Symbol
readonly
The name of the primary key.
-
#primary_key_names ⇒ Array<Symbol>
readonly
A list of all pk names.
-
#registry ⇒ registry
readonly
Registry::Root with runtime dependency resolving.
-
#relations ⇒ registry
readonly
Setup relation registry.
Class Method Summary collapse
- .attributes(attributes, attr_class) ⇒ Object private
-
.build_attribute_info(type, **options) ⇒ Hash
private
Builds a representation of the information needed to create an attribute.
-
.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options) ⇒ Schema
Define a relation schema from plain rom types and optional options.
Instance Method Summary collapse
-
#[](key, src = nil) ⇒ Object
Return attribute.
-
#append(*new_attributes) ⇒ Schema
Append more attributes to the schema.
-
#call(relation) ⇒ Relation
Abstract method for creating a new relation based on schema definition.
-
#canonical? ⇒ Boolean
Return if a schema is canonical.
-
#each {|Attribute| ... } ⇒ Object
Iterate over schema's attributes.
-
#empty? ⇒ TrueClass, FalseClass
Check if schema has any attributes.
-
#exclude(*names) ⇒ Schema
Exclude provided attributes from a schema.
-
#finalize! ⇒ self
private
Finalize a schema.
-
#finalize_attributes!(gateway: nil) ⇒ self
private
This hook is called when relation is being build during container finalization.
-
#foreign_key(relation) ⇒ Attribute
Return FK attribute for a given relation name.
-
#initialize {|_self| ... } ⇒ Schema
constructor
private
A new instance of Schema.
-
#key?(name) ⇒ Boolean
Return if a schema includes an attribute with the given name.
-
#merge(other) ⇒ Schema
(also: #+)
Merge with another schema.
-
#prefix(prefix) ⇒ Schema
Project a schema with renamed attributes using provided prefix.
-
#primary_key ⇒ Array<Attribute>
Return primary key attributes.
-
#project(*names) ⇒ Schema
Project a schema to include only specified attributes.
-
#rename(mapping) ⇒ Schema
Project a schema with renamed attributes.
- #set!(key, value) ⇒ Object private
-
#to_ary ⇒ Array
Array with schema attributes.
-
#to_ast ⇒ Array
Return AST for the schema.
-
#to_h ⇒ Hash
Coerce schema into a
Attribute> Hash. -
#to_input_hash ⇒ Dry::Types::Hash
private
Return coercion function using attribute types.
-
#to_output_hash ⇒ Dry::Types::Hash
private
Return coercion function using attribute read types.
-
#uniq(&block) ⇒ Schema
Return a new schema with uniq attributes.
-
#wrap(prefix = name.dataset) ⇒ Schema
Return new schema with all attributes marked as prefixed and wrapped.
Constructor Details
#initialize {|_self| ... } ⇒ Schema
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 a new instance of Schema.
153 154 155 156 157 |
# File 'lib/rom/schema.rb', line 153 def initialize(*) super yield(self) if block_given? end |
Instance Attribute Details
#__memoized__ ⇒ Object (readonly) Originally defined in module Memoizable
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.
#associations ⇒ Setup::Registry::Root (readonly)
Returns Optional association set (this is adapter-specific).
86 |
# File 'lib/rom/schema.rb', line 86 option :associations, default: -> { EMPTY_ASSOCIATION_SET } |
#attributes ⇒ Array (readonly)
Returns Array with schema attributes.
82 |
# File 'lib/rom/schema.rb', line 82 option :attributes, default: -> { EMPTY_ARRAY } |
#canonical ⇒ Symbol (readonly)
Returns The canonical schema which is carried in all schema instances.
94 |
# File 'lib/rom/schema.rb', line 94 option :canonical, default: -> { self } |
#inferrer ⇒ #call (readonly)
Returns An optional inferrer object used in finalize!
.
90 |
# File 'lib/rom/schema.rb', line 90 option :inferrer, default: -> { DEFAULT_INFERRER } |
#name ⇒ Symbol (readonly)
Returns The name of this schema.
70 |
# File 'lib/rom/schema.rb', line 70 param :name |
#primary_key_name ⇒ Symbol (readonly)
Returns The name of the primary key. This is set because in most of the cases relations don't have composite pks.
102 |
# File 'lib/rom/schema.rb', line 102 option :primary_key_name, optional: true |
#primary_key_names ⇒ Array<Symbol> (readonly)
Returns A list of all pk names.
106 |
# File 'lib/rom/schema.rb', line 106 option :primary_key_names, optional: true |
#registry ⇒ registry (readonly)
Returns Registry::Root with runtime dependency resolving.
74 |
# File 'lib/rom/schema.rb', line 74 option :registry, default: -> { self.class.registry } |
#relations ⇒ registry (readonly)
Returns Setup relation registry.
78 |
# File 'lib/rom/schema.rb', line 78 option :relations, default: -> { registry.relations } |
Class Method Details
.attributes(attributes, attr_class) ⇒ 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.
146 147 148 149 150 |
# File 'lib/rom/schema.rb', line 146 def self.attributes(attributes, attr_class) attributes.map do |attr| attr_class.new(attr[:type], **attr.fetch(:options)) end end |
.build_attribute_info(type, **options) ⇒ Hash
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.
Builds a representation of the information needed to create an attribute.
This representation is consumed by Schema.define
in order to create
the actual attributes.
138 139 140 141 142 143 |
# File 'lib/rom/schema.rb', line 138 def self.build_attribute_info(type, **) { type: type, options: } end |
.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options) ⇒ Schema
Define a relation schema from plain rom types and optional options
Resulting schema will decorate plain rom types with adapter-specific types
By default Attribute
will be used
120 121 122 123 124 125 126 127 |
# File 'lib/rom/schema.rb', line 120 def self.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **) new( name, attr_class: attr_class, attributes: attributes(attributes, attr_class), ** ) { |schema| yield(schema) if block_given? } end |
Instance Method Details
#[](key, src = nil) ⇒ Object
Return attribute
212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/rom/schema.rb', line 212 def [](key, src = nil) if count_index[key].zero? raise(KeyError, "#{key.inspect} attribute doesn't exist in #{src} schema") elsif count_index[key] > 1 && src.nil? raise(KeyError, "#{key.inspect} attribute is not unique") if count_index[key] > 1 elsif src source_index[src][key] else name_index[key] end end |
#append(*new_attributes) ⇒ Schema
Append more attributes to the schema
This returns a new schema instance
326 327 328 |
# File 'lib/rom/schema.rb', line 326 def append(*new_attributes) new(attributes + new_attributes) end |
#call(relation) ⇒ Relation
Abstract method for creating a new relation based on schema definition
This can be used by views to generate a new relation automatically. In example a schema can project a relation, join any additional relations if it includes attributes from other relations etc.
Default implementation is a no-op and it simply returns back untouched relation
173 174 175 |
# File 'lib/rom/schema.rb', line 173 def call(relation) relation end |
#canonical? ⇒ Boolean
Return if a schema is canonical
359 360 361 |
# File 'lib/rom/schema.rb', line 359 def canonical? equal?(canonical) end |
#each {|Attribute| ... } ⇒ Object
Iterate over schema's attributes
182 183 184 |
# File 'lib/rom/schema.rb', line 182 def each(&block) attributes.each(&block) end |
#empty? ⇒ TrueClass, FalseClass
Check if schema has any attributes
191 192 193 |
# File 'lib/rom/schema.rb', line 191 def empty? attributes.empty? end |
#exclude(*names) ⇒ Schema
Exclude provided attributes from a schema
242 243 244 |
# File 'lib/rom/schema.rb', line 242 def exclude(*names) project(*(map(&:name) - names)) end |
#finalize! ⇒ self
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.
Finalize a schema
368 369 370 371 372 |
# File 'lib/rom/schema.rb', line 368 def finalize!(*) return self if frozen? freeze end |
#finalize_attributes!(gateway: nil) ⇒ self
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 hook is called when relation is being build during container finalization
When block is provided it'll be called just before freezing the instance so that additional ivars can be set
382 383 384 385 386 387 388 389 390 |
# File 'lib/rom/schema.rb', line 382 def finalize_attributes!(gateway: nil) inferrer.(self, gateway).each { |key, value| set!(key, value) } yield if block_given? initialize_primary_key_names self end |
#foreign_key(relation) ⇒ Attribute
Return FK attribute for a given relation name
292 293 294 |
# File 'lib/rom/schema.rb', line 292 def foreign_key(relation) detect { |attr| attr.foreign_key? && attr.target == relation } end |
#key?(name) ⇒ Boolean
Return if a schema includes an attribute with the given name
350 351 352 |
# File 'lib/rom/schema.rb', line 350 def key?(name) !attributes.detect { |attr| attr.name == name }.nil? end |
#merge(other) ⇒ Schema Also known as: +
Merge with another schema
312 313 314 |
# File 'lib/rom/schema.rb', line 312 def merge(other) append(*other) end |
#prefix(prefix) ⇒ Schema
Project a schema with renamed attributes using provided prefix
269 270 271 |
# File 'lib/rom/schema.rb', line 269 def prefix(prefix) new(map { |attr| attr.prefixed(prefix) }) end |
#primary_key ⇒ Array<Attribute>
Return primary key attributes
301 302 303 |
# File 'lib/rom/schema.rb', line 301 def primary_key select(&:primary_key?) end |
#project(*names) ⇒ Schema
Project a schema to include only specified attributes
231 232 233 |
# File 'lib/rom/schema.rb', line 231 def project(*names) new(names.map { |name| name.is_a?(Symbol) ? self[name] : name }) end |
#rename(mapping) ⇒ Schema
Project a schema with renamed attributes
253 254 255 256 257 258 259 260 |
# File 'lib/rom/schema.rb', line 253 def rename(mapping) new_attributes = map do |attr| alias_name = mapping[attr.name] alias_name ? attr.aliased(alias_name) : attr end new(new_attributes) end |
#set!(key, value) ⇒ 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.
429 430 431 432 |
# File 'lib/rom/schema.rb', line 429 def set!(key, value) instance_variable_set("@#{key}", value) [key] = value end |
#to_ary ⇒ Array
Returns Array with schema attributes.
108 |
# File 'lib/rom/schema.rb', line 108 option :attributes, default: -> { EMPTY_ARRAY } |
#to_ast ⇒ Array
Return AST for the schema
424 425 426 |
# File 'lib/rom/schema.rb', line 424 def to_ast [:schema, [name, attributes.map(&:to_ast)]] end |
#to_h ⇒ Hash
Coerce schema into a
200 201 202 |
# File 'lib/rom/schema.rb', line 200 def to_h each_with_object({}) { |attr, h| h[attr.name] = attr } end |
#to_input_hash ⇒ Dry::Types::Hash
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 coercion function using attribute types
This is used for input_schema
in relations, typically commands use it
for processing input
413 414 415 416 417 |
# File 'lib/rom/schema.rb', line 413 def to_input_hash HASH_SCHEMA.schema( map { |attr| [attr.name, attr.to_write_type] }.to_h ) end |
#to_output_hash ⇒ Dry::Types::Hash
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 coercion function using attribute read types
This is used for output_schema
in relations
399 400 401 402 403 |
# File 'lib/rom/schema.rb', line 399 def to_output_hash HASH_SCHEMA.schema( map { |attr| [attr.key, attr.to_read_type] }.to_h ) end |
#uniq(&block) ⇒ Schema
Return a new schema with uniq attributes
335 336 337 338 339 340 341 |
# File 'lib/rom/schema.rb', line 335 def uniq(&block) if block new(attributes.uniq(&block)) else new(attributes.uniq(&:name)) end end |
#wrap(prefix = name.dataset) ⇒ Schema
Return new schema with all attributes marked as prefixed and wrapped
This is useful when relations are joined and the right side should be marked as wrapped
283 284 285 |
# File 'lib/rom/schema.rb', line 283 def wrap(prefix = name.dataset) new(map { |attr| attr.wrapped? ? attr : attr.wrapped(prefix) }) end |