Class: ROM::Schema

Inherits:
Object
  • Object
show all
Extended by:
Notifications::Listener
Includes:
Enumerable
Defined in:
core/lib/rom/schema.rb,
core/lib/rom/schema/dsl.rb,
core/lib/rom/schema/inferrer.rb,
core/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

Memory::Schema

Defined Under Namespace

Classes: AssociationsDSL, DSL

Constant Summary collapse

EMPTY_ASSOCIATION_SET =
AssociationSet.build(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

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#associationsAssociationSet (readonly)

Returns Optional association set (this is adapter-specific).

Returns:

  • (AssociationSet)

    Optional association set (this is adapter-specific)



95
# File 'core/lib/rom/schema.rb', line 95

option :associations, default: -> { EMPTY_ASSOCIATION_SET }

#attributesArray (readonly)

Returns Array with schema attributes.

Returns:

  • (Array)

    Array with schema attributes



91
# File 'core/lib/rom/schema.rb', line 91

option :attributes, default: -> { EMPTY_ARRAY }

#canonicalSymbol (readonly)

Returns The canonical schema which is carried in all schema instances.

Returns:

  • (Symbol)

    The canonical schema which is carried in all schema instances



106
# File 'core/lib/rom/schema.rb', line 106

option :canonical, default: -> { self }

#inferrer#call (readonly)

Returns An optional inferrer object used in finalize!.

Returns:

  • (#call)

    An optional inferrer object used in finalize!



99
# File 'core/lib/rom/schema.rb', line 99

option :inferrer, default: -> { DEFAULT_INFERRER }

#nameSymbol (readonly)

Returns The name of this schema.

Returns:

  • (Symbol)

    The name of this schema



87
# File 'core/lib/rom/schema.rb', line 87

param :name

#primary_key_nameSymbol (readonly)

Returns The name of the primary key. This is set because in most of the cases relations don't have composite pks.

Returns:

  • (Symbol)

    The name of the primary key. This is set because in most of the cases relations don't have composite pks



114
# File 'core/lib/rom/schema.rb', line 114

option :primary_key_name, optional: true

#primary_key_namesArray<Symbol> (readonly)

Returns A list of all pk names.

Returns:

  • (Array<Symbol>)

    A list of all pk names



118
# File 'core/lib/rom/schema.rb', line 118

option :primary_key_names, optional: true

Class Method Details

.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

Parameters:

  • name (Relation::Name, Symbol)

    The schema name, typically ROM::Relation::Name

Returns:



132
133
134
135
136
137
138
139
# File 'core/lib/rom/schema.rb', line 132

def self.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options)
  new(
    name,
    attr_class: attr_class,
    attributes: attributes(attributes, attr_class),
    **options
  ) { |schema| yield(schema) if block_given? }
end

.subscribe(event_id, query = EMPTY_HASH, &block) ⇒ Object Originally defined in module Notifications::Listener

Subscribe to events

Parameters:

  • event_id (String)

    The event key

  • query (Hash) (defaults to: EMPTY_HASH)

    An optional event filter

Returns:

  • (Object)

    self

Instance Method Details

#[](key, src = name.to_sym) ⇒ Object

Return attribute

Parameters:

  • key (Symbol)

    The attribute name

  • src (Symbol, Relation::Name) (defaults to: name.to_sym)

    The source relation (for merged schemas)

Raises:

  • KeyError



224
225
226
227
228
229
230
231
232
233
234
235
# File 'core/lib/rom/schema.rb', line 224

def [](key, src = name.to_sym)
  attr =
    if count_index[key].equal?(1)
      name_index[key]
    else
      source_index[src][key]
    end

  raise(KeyError, "#{key.inspect} attribute doesn't exist in #{src} schema") unless attr

  attr
end

#append(*new_attributes) ⇒ Schema

Append more attributes to the schema

This returns a new schema instance

Parameters:

Returns:



339
340
341
# File 'core/lib/rom/schema.rb', line 339

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

Parameters:

Returns:



185
186
187
# File 'core/lib/rom/schema.rb', line 185

def call(relation)
  relation
end

#canonical?Boolean

Return if a schema is canonical

Returns:

  • (Boolean)


372
373
374
# File 'core/lib/rom/schema.rb', line 372

def canonical?
  equal?(canonical)
end

#each {|Attribute| ... } ⇒ Object

Iterate over schema's attributes

Yields:



194
195
196
# File 'core/lib/rom/schema.rb', line 194

def each(&block)
  attributes.each(&block)
end

#empty?TrueClass, FalseClass

Check if schema has any attributes

Returns:

  • (TrueClass, FalseClass)


203
204
205
# File 'core/lib/rom/schema.rb', line 203

def empty?
  attributes.empty?
end

#exclude(*names) ⇒ Schema

Exclude provided attributes from a schema

Parameters:

  • names (*Array)

    Attribute names

Returns:



255
256
257
# File 'core/lib/rom/schema.rb', line 255

def exclude(*names)
  project(*(map(&:name) - names))
end

#foreign_key(relation) ⇒ Attribute

Return FK attribute for a given relation name

Returns:



305
306
307
# File 'core/lib/rom/schema.rb', line 305

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

Parameters:

  • name (Symbol)

    The name of the attribute

Returns:

  • (Boolean)


363
364
365
# File 'core/lib/rom/schema.rb', line 363

def key?(name)
  !attributes.detect { |attr| attr.name == name }.nil?
end

#merge(other) ⇒ Schema Also known as: +

Merge with another schema

Parameters:

  • other (Schema)

    Other schema

Returns:



325
326
327
# File 'core/lib/rom/schema.rb', line 325

def merge(other)
  append(*other)
end

#prefix(prefix) ⇒ Schema

Project a schema with renamed attributes using provided prefix

Parameters:

  • prefix (Symbol)

    The name of the prefix

Returns:



282
283
284
# File 'core/lib/rom/schema.rb', line 282

def prefix(prefix)
  new(map { |attr| attr.prefixed(prefix) })
end

#primary_keyArray<Attribute>

Return primary key attributes

Returns:



314
315
316
# File 'core/lib/rom/schema.rb', line 314

def primary_key
  select(&:primary_key?)
end

#project(*names) ⇒ Schema

Project a schema to include only specified attributes

Parameters:

  • names (*Array<Symbol, Attribute>)

    Attribute names

Returns:



244
245
246
# File 'core/lib/rom/schema.rb', line 244

def project(*names)
  new(names.map { |name| name.is_a?(Symbol) ? self[name] : name })
end

#rename(mapping) ⇒ Schema

Project a schema with renamed attributes

Parameters:

  • mapping (Hash)

    The attribute mappings

Returns:



266
267
268
269
270
271
272
273
# File 'core/lib/rom/schema.rb', line 266

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_aryArray

Returns Array with schema attributes.

Returns:

  • (Array)

    Array with schema attributes



120
# File 'core/lib/rom/schema.rb', line 120

option :attributes, default: -> { EMPTY_ARRAY }

#to_astArray

Return AST for the schema

Returns:

  • (Array)


449
450
451
# File 'core/lib/rom/schema.rb', line 449

def to_ast
  [:schema, [name, attributes.map(&:to_ast)]]
end

#to_hHash

Coerce schema into a Attribute> Hash

Returns:

  • (Hash)


212
213
214
# File 'core/lib/rom/schema.rb', line 212

def to_h
  each_with_object({}) { |attr, h| h[attr.name] = attr }
end

#uniq(&block) ⇒ Schema

Return a new schema with uniq attributes

Returns:



348
349
350
351
352
353
354
# File 'core/lib/rom/schema.rb', line 348

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

Parameters:

  • prefix (Symbol) (defaults to: name.dataset)

    The prefix used for aliasing wrapped attributes

Returns:



296
297
298
# File 'core/lib/rom/schema.rb', line 296

def wrap(prefix = name.dataset)
  new(map { |attr| attr.wrapped? ? attr : attr.wrapped(prefix) })
end