Class: ROM::Relation::Combined

Inherits:
Graph
  • Object
show all
Includes:
Commands
Defined in:
core/lib/rom/relation/combined.rb

Overview

Represents a relation graphs which combines root relation with other relation nodes

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ROM::Pipeline::Proxy

Class Method Details

.new(relation, nodes) ⇒ Combined

Create a new relation combined with others

Parameters:

Returns:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'core/lib/rom/relation/combined.rb', line 23

def self.new(relation, nodes)
  struct_ns = relation.options[:struct_namespace]
  new_nodes = nodes.uniq(&:name).map { |node| node.struct_namespace(struct_ns) }

  root =
    if relation.is_a?(self)
      new_nodes.concat(relation.nodes)
      relation.root
    else
      relation
    end

  super(root, new_nodes)
end

Instance Method Details

#call(*args) ⇒ Loaded

Materialize combined relation

Returns:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'core/lib/rom/relation/combined.rb', line 65

def call(*args)
  left = root.with(auto_map: false, auto_struct: false).call(*args)

  right =
    if left.empty?
      nodes.map { |node| Loaded.new(node, EMPTY_ARRAY) }
    else
      nodes.map { |node| node.call(left) }
    end

  if auto_map?
    Loaded.new(self, mapper.([left, right]))
  else
    Loaded.new(self, [left, right])
  end
end

#combine(*args) ⇒ Combined

Combine with other relations

Returns:

See Also:



56
57
58
# File 'core/lib/rom/relation/combined.rb', line 56

def combine(*args)
  self.class.new(root, nodes + root.combine(*args).nodes)
end

#combine_with(*others) ⇒ Graph

Combine this graph with more nodes

Parameters:

  • others (Array<Relation>)

    A list of relations

Returns:



45
46
47
# File 'core/lib/rom/relation/combined.rb', line 45

def combine_with(*others)
  self.class.new(root, nodes + others)
end

#command(type, *args) ⇒ Object

Return a :create command that can insert data from a nested hash.

This is limited to :create commands only, because automatic restriction for :update commands would be quite complex. It's possible that in the future support for :update commands will be added though.

Another limitation is that it can only work when you're composing parent and its child(ren), which follows canonical hierarchy from your database, so that parents are created first, then their PKs are set as FKs in child tuples. It should be possible to make it work with both directions (parent => child or child => parent), and it would require converting input tuples based on how they depend on each other, which we could do in the future.

Expanding functionality of this method is planned for rom 5.0.

Raises:

  • NotImplementedError when type is not :create

See Also:

  • Relation#command


143
144
145
146
147
148
149
# File 'core/lib/rom/relation/combined.rb', line 143

def command(type, *args)
  if type == :create
    super
  else
    raise NotImplementedError, "#{self.class}#command doesn't work with #{type.inspect} command type yet"
  end
end

#node(name) {|relation| ... } ⇒ Relation

Return a new combined relation with adjusted node returned from a block

Examples:

with a node identifier

combine(:tasks).node(:tasks) { |tasks| tasks.prioritized }

with a nested path

combine(tasks: :tags).node(tasks: :tags) { |tags| tags.where(name: 'red') }

Parameters:

  • name (Symbol)

    The node relation name

Yield Parameters:

  • relation (Relation)

    The relation node

Yield Returns:

Returns:



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

def node(name, &block)
  if name.is_a?(Symbol) && !nodes.map { |n| n.name.key }.include?(name)
    raise ArgumentError, "#{name.inspect} is not a valid aggregate node name"
  end

  new_nodes = nodes.map { |node|
    case name
    when Symbol
      name == node.name.key ? yield(node) : node
    when Hash
      other, *rest = name.flatten(1)
      if other == node.name.key
        nodes.detect { |n| n.name.key == other }.node(*rest, &block)
      else
        node
      end
    else
      node
    end
  }

  with_nodes(new_nodes)
end