Module: ROM::Relation::ClassInterface

Extended by:
Notifications::Listener
Included in:
ROM::Relation
Defined in:
lib/rom/relation/class_interface.rb

Overview

Global class-level API for relation classes

rubocop:disable Metrics/ModuleLength

Constant Summary collapse

DEFAULT_DATASET_PROC =
-> * { self }.freeze
INVALID_RELATIONS_NAMES =
[
  :relations
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Notifications::Listener

subscribe

Instance Attribute Details

#relation_nameObject (readonly)

Raises:



138
139
140
141
142
# File 'lib/rom/relation/class_interface.rb', line 138

def relation_name
  raise MissingSchemaError, self unless defined?(@relation_name)

  @relation_name
end

#schema_procObject (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.



134
135
136
# File 'lib/rom/relation/class_interface.rb', line 134

def schema_proc
  @schema_proc
end

Instance Method Details

#[](adapter) ⇒ Class

Return adapter-specific relation subclass

Examples:

ROM::Relation[:memory]
# => ROM::Memory::Relation


48
49
50
51
52
# File 'lib/rom/relation/class_interface.rb', line 48

def [](adapter)
  ROM.adapters.fetch(adapter).const_get(:Relation)
rescue KeyError
  raise AdapterNotPresentError.new(adapter, :relation)
end

#curriedObject

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.



280
# File 'lib/rom/relation/class_interface.rb', line 280

def curried = Curried

#dataset(&block) ⇒ Object

Set or get custom dataset block

This block will be evaluated when a relation is instantiated and registered in a relation registry.

Examples:

class Users < ROM::Relation[:memory]
  dataset { sort_by(:id) }
end


65
66
67
68
69
70
71
# File 'lib/rom/relation/class_interface.rb', line 65

def dataset(&block)
  if defined?(@dataset)
    @dataset
  else
    @dataset = block || DEFAULT_DATASET_PROC
  end
end

#default_nameName

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 default relation name used in schemas



300
301
302
# File 'lib/rom/relation/class_interface.rb', line 300

def default_name
  Name[Inflector.underscore(name).tr('/', '_').to_sym]
end

#default_schema(klass = self) ⇒ 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.



305
306
307
308
309
310
311
312
# File 'lib/rom/relation/class_interface.rb', line 305

def default_schema(klass = self)
  klass.schema ||
    if klass.schema_proc
      klass.set_schema!(klass.schema_proc.call)
    else
      klass.schema_class.define(klass.default_name)
    end
end

#forward(*methods) ⇒ Object

Dynamically define a method that will forward to the dataset and wrap response in the relation itself

Examples:

class SomeAdapterRelation < ROM::Relation
  forward :super_query
end


240
241
242
243
244
245
246
247
248
# File 'lib/rom/relation/class_interface.rb', line 240

def forward(*methods)
  methods.each do |method|
    class_eval("      def \#{method}(...)                        # def super_query(...)\n        new(dataset.__send__(:\#{method}, ...))  #   new(dataset.__send__(:super_query, ...))\n      end                                       # end\n    RUBY\n  end\nend\n", __FILE__, __LINE__ + 1)

#mapper_registry(opts = EMPTY_HASH) ⇒ MapperRegistry

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.

Build default mapper registry



266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/rom/relation/class_interface.rb', line 266

def mapper_registry(opts = EMPTY_HASH)
  adapter_ns = ROM.adapters[adapter]

  compiler =
    if adapter_ns&.const_defined?(:MapperCompiler)
      adapter_ns.const_get(:MapperCompiler)
    else
      MapperCompiler
    end

  MapperRegistry.new({}, compiler: compiler.new(**opts), **opts)
end

#nameObject

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.



315
# File 'lib/rom/relation/class_interface.rb', line 315

def name = super || superclass.name

#schema(dataset = nil, as: nil, infer: false) ⇒ Schema

Specify canonical schema for a relation

With a schema defined commands will set up a type-safe input handler automatically

rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity

Examples:

class Users < ROM::Relation[:sql]
  schema do
    attribute :id, Types::Serial
    attribute :name, Types::String
  end
end

# access schema from a finalized relation
users.schema


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rom/relation/class_interface.rb', line 96

def schema(dataset = nil, as: nil, infer: false, &)
  if defined?(@schema) && !block_given? && !infer
    @schema
  elsif block_given? || infer
    raise MissingSchemaClassError, self unless schema_class

    ds_name = dataset || schema_opts.fetch(:dataset, default_name.dataset)
    relation = as || schema_opts.fetch(:relation, ds_name)

    raise InvalidRelationName, relation if invalid_relation_name?(relation)

    @relation_name = Name[relation, ds_name]

    @schema_proc = proc do |*args, &inner_block|
      schema_dsl.new(
        relation_name,
        schema_class: schema_class,
        attr_class: schema_attr_class,
        inferrer: schema_inferrer.with(enabled: infer),
        &
      ).call(*args, &inner_block)
    end
  end
end

#schemasObject

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.



291
292
293
# File 'lib/rom/relation/class_interface.rb', line 291

def schemas
  @schemas ||= {}
end

#set_schema!(schema) ⇒ 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.

Assign a schema to a relation class



129
130
131
# File 'lib/rom/relation/class_interface.rb', line 129

def set_schema!(schema)
  @schema = schema
end

#use(plugin, **options) ⇒ Object

Include a registered plugin in this relation class

Options Hash (**options):

  • :adapter (Symbol) — default: :default

    first adapter to check for plugin



257
258
259
# File 'lib/rom/relation/class_interface.rb', line 257

def use(plugin, **options)
  ROM.plugin_registry[:relation].fetch(plugin, adapter).apply_to(self, **options)
end

#view(name, schema, &block) ⇒ Symbol #view(name, &block) ⇒ Symbol

Define a relation view with a specific schema

This method should only be used in cases where a given adapter doesn’t support automatic schema projection at run-time.

**It’s not needed in rom-sql**

rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity

Overloads:

  • #view(name, schema, &block) ⇒ Symbol

    Examples:

    View with the canonical schema

    class Users < ROM::Relation[:sql]
      view(:listing, schema) do
        order(:name)
      end
    end
    

    View with a projected schema

    class Users < ROM::Relation[:sql]
      view(:listing, schema.project(:id, :name)) do
        order(:name)
      end
    end
    
  • #view(name, &block) ⇒ Symbol

    Examples:

    View with the canonical schema and arguments

    class Users < ROM::Relation[:sql]
      view(:by_name) do |name|
        where(name: name)
      end
    end
    

    View with projected schema and arguments

    class Users < ROM::Relation[:sql]
      view(:by_name) do
        schema { project(:id, :name) }
        relation { |name| where(name: name) }
      end
    end
    

    View with a schema extended with foreign attributes

    class Users < ROM::Relation[:sql]
      view(:index) do
        schema { append(relations[:tasks][:title]) }
        relation { |name| where(name: name) }
      end
    end
    


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/rom/relation/class_interface.rb', line 194

def view(*args, &block)
  if args.size == 1 && block.arity.positive?
    raise ArgumentError, 'schema attribute names must be provided as the second argument'
  end

  name, new_schema_fn, relation_block =
    if args.size == 1
      ViewDSL.new(*args, schema, &block).call
    else
      [*args, block]
    end

  schemas[name] =
    if args.size == 2
      -> _ { schema.project(*args[1]) }
    else
      new_schema_fn
    end

  if relation_block.arity.positive?
    auto_curry_guard do
      define_method(name, &relation_block)

      auto_curry(name) do
        schemas[name].(self)
      end
    end
  else
    define_method(name) do
      schemas[name].(instance_exec(&relation_block))
    end
  end

  name
end

#view_methodsObject

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.



283
284
285
286
287
288
# File 'lib/rom/relation/class_interface.rb', line 283

def view_methods
  ancestor_methods = ancestors.reject { |klass| klass == self }
    .map(&:instance_methods).flatten(1)

  instance_methods - ancestor_methods + auto_curried_methods.to_a
end