Class: ROM::Schema
- Inherits:
-
Object
- Object
- ROM::Schema
- Extended by:
- Initializer, Notifications::Listener
- Includes:
- Enumerable, Memoizable
- Defined in:
- lib/rom/schema.rb,
lib/rom/schema/dsl.rb,
lib/rom/schema/inferrer.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 =
AssociationSet.new(EMPTY_HASH).freeze
- DEFAULT_INFERRER =
Inferrer.new(enabled: false).freeze
Constants included from Memoizable
Instance Attribute Summary collapse
-
#associations ⇒ AssociationSet
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 ⇒ Object readonly
- #primary_key_names ⇒ Object readonly
Attributes included from Memoizable
Class Method Summary collapse
- .attributes(attributes, attr_class) ⇒ Object private
-
.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options) ⇒ Schema
Define a relation schema from plain rom types.
Instance Method Summary collapse
-
#[](key, src = name.to_sym) ⇒ 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!(**opts) ⇒ self
private
Finalize a schema.
-
#finalize_associations!(relations:) ⇒ self
private
Finalize associations defined in a schema.
-
#finalize_attributes!(gateway: nil, relations: 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.
-
#to_ary ⇒ Array
Array with schema attributes.
-
#to_ast ⇒ Array
Return AST for the schema.
-
#to_h ⇒ Hash
Coerce schema into a <AttributeName=>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.
Methods included from Notifications::Listener
Methods included from Initializer
Methods included from Memoizable
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.
130 131 132 133 134 |
# File 'lib/rom/schema.rb', line 130 def initialize(*) super yield(self) if block_given? end |
Instance Attribute Details
#associations ⇒ AssociationSet (readonly)
Returns Optional association set (this is adapter-specific).
79 |
# File 'lib/rom/schema.rb', line 79 option :associations, default: -> { EMPTY_ASSOCIATION_SET } |
#attributes ⇒ Array (readonly)
Returns Array with schema attributes.
75 |
# File 'lib/rom/schema.rb', line 75 option :attributes, default: -> { EMPTY_ARRAY } |
#canonical ⇒ Symbol (readonly)
Returns The canonical schema which is carried in all schema instances.
90 |
# File 'lib/rom/schema.rb', line 90 option :canonical, default: -> { self } |
#inferrer ⇒ #call (readonly)
Returns An optional inferrer object used in ‘finalize!`.
83 |
# File 'lib/rom/schema.rb', line 83 option :inferrer, default: -> { DEFAULT_INFERRER } |
#name ⇒ Symbol (readonly)
Returns The name of this schema.
71 |
# File 'lib/rom/schema.rb', line 71 param :name |
#primary_key_name ⇒ Object (readonly)
98 99 100 |
# File 'lib/rom/schema.rb', line 98 def primary_key_name @primary_key_name end |
#primary_key_names ⇒ Object (readonly)
102 103 104 |
# File 'lib/rom/schema.rb', line 102 def primary_key_names @primary_key_names end |
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.
125 126 127 |
# File 'lib/rom/schema.rb', line 125 def self.attributes(attributes, attr_class) attributes.map { |type| attr_class.new(type) } end |
.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options) ⇒ Schema
Define a relation schema from plain rom types
Resulting schema will decorate plain rom types with adapter-specific types By default ‘Attribute` will be used
116 117 118 119 120 121 122 |
# File 'lib/rom/schema.rb', line 116 def self.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **) new( name, attributes: attributes(attributes, attr_class), ** ) { |schema| yield(schema) if block_given? } end |
Instance Method Details
#[](key, src = name.to_sym) ⇒ Object
Return attribute
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rom/schema.rb', line 188 def [](key, src = name.to_sym) attr = if count_index[key].equal?(1) name_index[key] else source_index[src][key] end unless attr raise(KeyError, "#{key.inspect} attribute doesn't exist in #{src} schema") end attr end |
#append(*new_attributes) ⇒ Schema
Append more attributes to the schema
This returns a new schema instance
305 306 307 |
# File 'lib/rom/schema.rb', line 305 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 uncludes attributes from other relations etc.
Default implementation is a no-op and it simply returns back untouched relation
149 150 151 |
# File 'lib/rom/schema.rb', line 149 def call(relation) relation end |
#canonical? ⇒ Boolean
Return if a schema is canonical
338 339 340 |
# File 'lib/rom/schema.rb', line 338 def canonical? self.equal?(canonical) end |
#each {|Attribute| ... } ⇒ Object
Iterate over schema’s attributes
158 159 160 |
# File 'lib/rom/schema.rb', line 158 def each(&block) attributes.each(&block) end |
#empty? ⇒ TrueClass, FalseClass
Check if schema has any attributes
167 168 169 |
# File 'lib/rom/schema.rb', line 167 def empty? attributes.size == 0 end |
#exclude(*names) ⇒ Schema
Exclude provided attributes from a schema
221 222 223 |
# File 'lib/rom/schema.rb', line 221 def exclude(*names) project(*(map(&:name) - names)) end |
#finalize!(**opts) ⇒ 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
347 348 349 350 |
# File 'lib/rom/schema.rb', line 347 def finalize!(**opts) return self if frozen? freeze end |
#finalize_associations!(relations:) ⇒ 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 associations defined in a schema
377 378 379 380 |
# File 'lib/rom/schema.rb', line 377 def finalize_associations!(relations:) set!(:associations, yield) if associations.any? self end |
#finalize_attributes!(gateway: nil, relations: 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
360 361 362 363 364 365 366 367 368 |
# File 'lib/rom/schema.rb', line 360 def finalize_attributes!(gateway: nil, relations: 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
271 272 273 |
# File 'lib/rom/schema.rb', line 271 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
329 330 331 |
# File 'lib/rom/schema.rb', line 329 def key?(name) ! attributes.detect { |attr| attr.name == name }.nil? end |
#merge(other) ⇒ Schema Also known as: +
Merge with another schema
291 292 293 |
# File 'lib/rom/schema.rb', line 291 def merge(other) append(*other) end |
#prefix(prefix) ⇒ Schema
Project a schema with renamed attributes using provided prefix
248 249 250 |
# File 'lib/rom/schema.rb', line 248 def prefix(prefix) new(map { |attr| attr.prefixed(prefix) }) end |
#primary_key ⇒ Array<Attribute>
Return primary key attributes
280 281 282 |
# File 'lib/rom/schema.rb', line 280 def primary_key select(&:primary_key?) end |
#project(*names) ⇒ Schema
Project a schema to include only specified attributes
210 211 212 |
# File 'lib/rom/schema.rb', line 210 def project(*names) new(names.map { |name| name.is_a?(Symbol) ? self[name] : name }) end |
#rename(mapping) ⇒ Schema
Project a schema with renamed attributes
232 233 234 235 236 237 238 239 |
# File 'lib/rom/schema.rb', line 232 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 |
#to_ary ⇒ Array
Returns Array with schema attributes.
104 |
# File 'lib/rom/schema.rb', line 104 option :attributes, default: -> { EMPTY_ARRAY } |
#to_ast ⇒ Array
Return AST for the schema
414 415 416 |
# File 'lib/rom/schema.rb', line 414 def to_ast [:schema, [name, attributes.map(&:to_ast)]] end |
#to_h ⇒ Hash
Coerce schema into a <AttributeName=>Attribute> Hash
176 177 178 |
# File 'lib/rom/schema.rb', line 176 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
403 404 405 406 407 |
# File 'lib/rom/schema.rb', line 403 def to_input_hash Types::Coercible::Hash.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
389 390 391 392 393 |
# File 'lib/rom/schema.rb', line 389 def to_output_hash Types::Coercible::Hash.schema( map { |attr| [attr.key, attr.to_read_type] }.to_h ) end |
#uniq(&block) ⇒ Schema
Return a new schema with uniq attributes
314 315 316 317 318 319 320 |
# File 'lib/rom/schema.rb', line 314 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
262 263 264 |
# File 'lib/rom/schema.rb', line 262 def wrap(prefix = name.dataset) new(map { |attr| attr.wrapped? ? attr : attr.wrapped(prefix) }) end |