rom 5.4.1 → 6.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -65
- data/LICENSE +1 -1
- data/README.md +7 -6
- data/lib/rom/array_dataset.rb +46 -0
- data/lib/rom/associations/abstract.rb +217 -0
- data/lib/rom/associations/definitions/abstract.rb +150 -0
- data/lib/rom/associations/definitions/many_to_many.rb +29 -0
- data/lib/rom/associations/definitions/many_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_many.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
- data/lib/rom/associations/definitions.rb +7 -0
- data/lib/rom/associations/many_to_many.rb +128 -0
- data/lib/rom/associations/many_to_one.rb +65 -0
- data/lib/rom/associations/one_to_many.rb +65 -0
- data/lib/rom/associations/one_to_one.rb +13 -0
- data/lib/rom/associations/one_to_one_through.rb +13 -0
- data/lib/rom/associations/through_identifier.rb +41 -0
- data/lib/rom/attribute.rb +425 -0
- data/lib/rom/auto_curry.rb +70 -0
- data/lib/rom/cache.rb +87 -0
- data/lib/rom/changeset/associated.rb +110 -0
- data/lib/rom/changeset/create.rb +18 -0
- data/lib/rom/changeset/delete.rb +15 -0
- data/lib/rom/changeset/extensions/relation.rb +26 -0
- data/lib/rom/changeset/pipe.rb +81 -0
- data/lib/rom/changeset/pipe_registry.rb +27 -0
- data/lib/rom/changeset/stateful.rb +285 -0
- data/lib/rom/changeset/update.rb +81 -0
- data/lib/rom/changeset.rb +185 -0
- data/lib/rom/command.rb +351 -0
- data/lib/rom/command_compiler.rb +201 -0
- data/lib/rom/command_proxy.rb +36 -0
- data/lib/rom/commands/class_interface.rb +236 -0
- data/lib/rom/commands/composite.rb +55 -0
- data/lib/rom/commands/create.rb +15 -0
- data/lib/rom/commands/delete.rb +16 -0
- data/lib/rom/commands/graph/class_interface.rb +64 -0
- data/lib/rom/commands/graph/input_evaluator.rb +94 -0
- data/lib/rom/commands/graph.rb +88 -0
- data/lib/rom/commands/lazy/create.rb +35 -0
- data/lib/rom/commands/lazy/delete.rb +39 -0
- data/lib/rom/commands/lazy/update.rb +46 -0
- data/lib/rom/commands/lazy.rb +106 -0
- data/lib/rom/commands/update.rb +16 -0
- data/lib/rom/commands.rb +5 -0
- data/lib/rom/compat/auto_registration.rb +115 -0
- data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
- data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
- data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
- data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
- data/lib/rom/compat/command.rb +74 -0
- data/lib/rom/compat/components/dsl/schema.rb +130 -0
- data/lib/rom/compat/components.rb +91 -0
- data/lib/rom/compat/global.rb +17 -0
- data/lib/rom/compat/mapper.rb +22 -0
- data/lib/rom/compat/registries.rb +47 -0
- data/lib/rom/compat/relation.rb +40 -0
- data/lib/rom/compat/schema/dsl.rb +260 -0
- data/lib/rom/compat/setting_proxy.rb +44 -0
- data/lib/rom/compat/setup.rb +151 -0
- data/lib/rom/compat/transformer.rb +49 -0
- data/lib/rom/compat.rb +22 -0
- data/lib/rom/components/association.rb +26 -0
- data/lib/rom/components/command.rb +24 -0
- data/lib/rom/components/core.rb +148 -0
- data/lib/rom/components/dataset.rb +60 -0
- data/lib/rom/components/dsl/association.rb +47 -0
- data/lib/rom/components/dsl/command.rb +60 -0
- data/lib/rom/components/dsl/core.rb +126 -0
- data/lib/rom/components/dsl/dataset.rb +33 -0
- data/lib/rom/components/dsl/gateway.rb +14 -0
- data/lib/rom/components/dsl/mapper.rb +70 -0
- data/lib/rom/components/dsl/relation.rb +49 -0
- data/lib/rom/components/dsl/schema.rb +150 -0
- data/lib/rom/components/dsl/view.rb +82 -0
- data/lib/rom/components/dsl.rb +255 -0
- data/lib/rom/components/gateway.rb +50 -0
- data/lib/rom/components/mapper.rb +29 -0
- data/lib/rom/components/provider.rb +160 -0
- data/lib/rom/components/registry.rb +154 -0
- data/lib/rom/components/relation.rb +41 -0
- data/lib/rom/components/schema.rb +61 -0
- data/lib/rom/components/view.rb +55 -0
- data/lib/rom/components.rb +55 -0
- data/lib/rom/configuration_dsl.rb +4 -0
- data/lib/rom/constants.rb +135 -0
- data/lib/rom/container.rb +182 -0
- data/lib/rom/core.rb +125 -0
- data/lib/rom/data_proxy.rb +97 -0
- data/lib/rom/enumerable_dataset.rb +70 -0
- data/lib/rom/gateway.rb +232 -0
- data/lib/rom/global.rb +56 -0
- data/lib/rom/header/attribute.rb +190 -0
- data/lib/rom/header.rb +198 -0
- data/lib/rom/inferrer.rb +55 -0
- data/lib/rom/initializer.rb +80 -0
- data/lib/rom/lint/enumerable_dataset.rb +56 -0
- data/lib/rom/lint/gateway.rb +120 -0
- data/lib/rom/lint/linter.rb +79 -0
- data/lib/rom/lint/spec.rb +22 -0
- data/lib/rom/lint/test.rb +98 -0
- data/lib/rom/loader.rb +161 -0
- data/lib/rom/mapper/attribute_dsl.rb +480 -0
- data/lib/rom/mapper/dsl.rb +107 -0
- data/lib/rom/mapper/model_dsl.rb +61 -0
- data/lib/rom/mapper.rb +99 -0
- data/lib/rom/mapper_compiler.rb +84 -0
- data/lib/rom/memory/associations/many_to_many.rb +12 -0
- data/lib/rom/memory/associations/many_to_one.rb +12 -0
- data/lib/rom/memory/associations/one_to_many.rb +12 -0
- data/lib/rom/memory/associations/one_to_one.rb +12 -0
- data/lib/rom/memory/associations.rb +6 -0
- data/lib/rom/memory/commands.rb +60 -0
- data/lib/rom/memory/dataset.rb +127 -0
- data/lib/rom/memory/gateway.rb +66 -0
- data/lib/rom/memory/mapper_compiler.rb +10 -0
- data/lib/rom/memory/relation.rb +91 -0
- data/lib/rom/memory/schema.rb +32 -0
- data/lib/rom/memory/storage.rb +61 -0
- data/lib/rom/memory/types.rb +11 -0
- data/lib/rom/memory.rb +7 -0
- data/lib/rom/model_builder.rb +103 -0
- data/lib/rom/open_struct.rb +112 -0
- data/lib/rom/pipeline.rb +111 -0
- data/lib/rom/plugin.rb +130 -0
- data/lib/rom/plugins/class_methods.rb +37 -0
- data/lib/rom/plugins/command/schema.rb +45 -0
- data/lib/rom/plugins/command/timestamps.rb +149 -0
- data/lib/rom/plugins/dsl.rb +53 -0
- data/lib/rom/plugins/relation/changeset.rb +97 -0
- data/lib/rom/plugins/relation/instrumentation.rb +66 -0
- data/lib/rom/plugins/relation/registry_reader.rb +36 -0
- data/lib/rom/plugins/schema/timestamps.rb +59 -0
- data/lib/rom/plugins.rb +100 -0
- data/lib/rom/processor/composer.rb +37 -0
- data/lib/rom/processor/transformer.rb +415 -0
- data/lib/rom/processor.rb +30 -0
- data/lib/rom/registries/associations.rb +26 -0
- data/lib/rom/registries/commands.rb +11 -0
- data/lib/rom/registries/container.rb +12 -0
- data/lib/rom/registries/datasets.rb +21 -0
- data/lib/rom/registries/gateways.rb +8 -0
- data/lib/rom/registries/mappers.rb +21 -0
- data/lib/rom/registries/nestable.rb +32 -0
- data/lib/rom/registries/relations.rb +8 -0
- data/lib/rom/registries/root.rb +203 -0
- data/lib/rom/registries/schemas.rb +44 -0
- data/lib/rom/registries/views.rb +11 -0
- data/lib/rom/relation/class_interface.rb +61 -0
- data/lib/rom/relation/combined.rb +160 -0
- data/lib/rom/relation/commands.rb +65 -0
- data/lib/rom/relation/composite.rb +53 -0
- data/lib/rom/relation/curried.rb +129 -0
- data/lib/rom/relation/graph.rb +107 -0
- data/lib/rom/relation/loaded.rb +136 -0
- data/lib/rom/relation/materializable.rb +62 -0
- data/lib/rom/relation/name.rb +122 -0
- data/lib/rom/relation/wrap.rb +64 -0
- data/lib/rom/relation.rb +625 -0
- data/lib/rom/repository/class_interface.rb +162 -0
- data/lib/rom/repository/relation_reader.rb +48 -0
- data/lib/rom/repository/root.rb +75 -0
- data/lib/rom/repository/session.rb +60 -0
- data/lib/rom/repository.rb +179 -0
- data/lib/rom/schema/associations_dsl.rb +222 -0
- data/lib/rom/schema/inferrer.rb +106 -0
- data/lib/rom/schema.rb +471 -0
- data/lib/rom/settings.rb +141 -0
- data/lib/rom/setup.rb +297 -0
- data/lib/rom/struct.rb +99 -0
- data/lib/rom/struct_compiler.rb +114 -0
- data/lib/rom/support/configurable.rb +213 -0
- data/lib/rom/support/inflector.rb +31 -0
- data/lib/rom/support/memoizable.rb +61 -0
- data/lib/rom/support/notifications.rb +238 -0
- data/lib/rom/transaction.rb +26 -0
- data/lib/rom/transformer.rb +46 -0
- data/lib/rom/types.rb +74 -0
- data/lib/rom/version.rb +1 -1
- data/lib/rom-changeset.rb +4 -0
- data/lib/rom-core.rb +3 -0
- data/lib/rom-repository.rb +4 -0
- data/lib/rom.rb +3 -3
- metadata +302 -23
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
require "rom/types"
|
6
|
+
require "rom/initializer"
|
7
|
+
require "rom/pipeline"
|
8
|
+
require "rom/relation/name"
|
9
|
+
require "rom/relation/materializable"
|
10
|
+
|
11
|
+
module ROM
|
12
|
+
class Relation
|
13
|
+
# Curried relation is a special relation proxy used by auto-curry mechanism.
|
14
|
+
#
|
15
|
+
# When a relation view method is called without all arguments, a curried proxy
|
16
|
+
# is returned that can be fully applied later on.
|
17
|
+
#
|
18
|
+
# Curried relations are typically used for relation composition
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
class Curried
|
22
|
+
extend Initializer
|
23
|
+
|
24
|
+
include Dry::Equalizer(:relation, :options)
|
25
|
+
include Materializable
|
26
|
+
include Pipeline
|
27
|
+
|
28
|
+
undef :map_with
|
29
|
+
|
30
|
+
# @!attribute [r] relation
|
31
|
+
# @return [Relation] The source relation that is curried
|
32
|
+
param :relation
|
33
|
+
|
34
|
+
# @!attribute [r] view
|
35
|
+
# @return [Symbol] The name of relation's view method
|
36
|
+
option :view, type: Types::Strict::Symbol
|
37
|
+
|
38
|
+
# @!attribute [r] arity
|
39
|
+
# @return [Integer] View's arity
|
40
|
+
option :arity, type: Types::Strict::Integer
|
41
|
+
|
42
|
+
# @!attribute [r] curry_args
|
43
|
+
# @return [Array] Arguments that will be passed to curried view
|
44
|
+
option :curry_args, default: -> { EMPTY_ARRAY }
|
45
|
+
|
46
|
+
# Load relation if args match the arity
|
47
|
+
#
|
48
|
+
# @return [Loaded,Curried]
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def call(*args)
|
52
|
+
all_args = curry_args + args
|
53
|
+
|
54
|
+
if all_args.empty?
|
55
|
+
raise ArgumentError,
|
56
|
+
"curried #{relation.class}##{view} relation was called without any arguments"
|
57
|
+
end
|
58
|
+
|
59
|
+
if args.empty?
|
60
|
+
self
|
61
|
+
elsif arity == all_args.size
|
62
|
+
Loaded.new(relation.__send__(view, *all_args))
|
63
|
+
else
|
64
|
+
__new__(relation, curry_args: all_args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
alias_method :[], :call
|
68
|
+
|
69
|
+
# Relations are coercible to an array but a curried relation cannot be coerced
|
70
|
+
# When something tries to do this, an exception will be raised
|
71
|
+
#
|
72
|
+
# @raise ArgumentError
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def to_a
|
76
|
+
raise(
|
77
|
+
ArgumentError,
|
78
|
+
"#{relation.class}##{view} arity is #{arity} " \
|
79
|
+
"(#{curry_args.size} args given)"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
alias_method :to_ary, :to_a
|
83
|
+
|
84
|
+
# Return if this lazy relation is curried
|
85
|
+
#
|
86
|
+
# @return [true]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def curried?
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
def respond_to_missing?(name, include_private = false)
|
95
|
+
super || relation.respond_to?(name, include_private)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# @api private
|
101
|
+
def __new__(relation, **new_opts)
|
102
|
+
self.class.new(relation, **options, **new_opts)
|
103
|
+
end
|
104
|
+
|
105
|
+
# @api private
|
106
|
+
def composite_class
|
107
|
+
Relation::Composite
|
108
|
+
end
|
109
|
+
|
110
|
+
# @api private
|
111
|
+
def method_missing(meth, *args, &block)
|
112
|
+
if relation.respond_to?(meth)
|
113
|
+
response = relation.__send__(meth, *args, &block)
|
114
|
+
|
115
|
+
super if response.is_a?(self.class)
|
116
|
+
|
117
|
+
if response.is_a?(Relation) || response.is_a?(Graph) || response.is_a?(Wrap) || response.is_a?(Composite)
|
118
|
+
__new__(response)
|
119
|
+
else
|
120
|
+
response
|
121
|
+
end
|
122
|
+
else
|
123
|
+
super
|
124
|
+
end
|
125
|
+
end
|
126
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
require "rom/initializer"
|
6
|
+
|
7
|
+
require "rom/relation/loaded"
|
8
|
+
require "rom/relation/composite"
|
9
|
+
require "rom/relation/materializable"
|
10
|
+
require "rom/pipeline"
|
11
|
+
require "rom/support/memoizable"
|
12
|
+
|
13
|
+
module ROM
|
14
|
+
class Relation
|
15
|
+
# Abstract relation graph class
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
class Graph
|
19
|
+
extend Initializer
|
20
|
+
|
21
|
+
include Memoizable
|
22
|
+
|
23
|
+
# @!attribute [r] root
|
24
|
+
# @return [Relation] The root relation
|
25
|
+
param :root
|
26
|
+
|
27
|
+
# @!attribute [r] nodes
|
28
|
+
# @return [Array<Relation>] An array with relation nodes
|
29
|
+
param :nodes
|
30
|
+
|
31
|
+
include Dry::Equalizer(:root, :nodes)
|
32
|
+
include Materializable
|
33
|
+
include Pipeline
|
34
|
+
include Pipeline::Proxy
|
35
|
+
|
36
|
+
# for compatibility with the pipeline
|
37
|
+
alias_method :left, :root
|
38
|
+
alias_method :right, :nodes
|
39
|
+
|
40
|
+
# Rebuild a graph with new nodes
|
41
|
+
#
|
42
|
+
# @param [Array<Relation>] nodes
|
43
|
+
#
|
44
|
+
# @return [Graph]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def with_nodes(nodes)
|
48
|
+
self.class.new(root, nodes)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return if this is a graph relation
|
52
|
+
#
|
53
|
+
# @return [true]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def graph?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Map graph tuples via custom mappers
|
61
|
+
#
|
62
|
+
# @see Relation#map_with
|
63
|
+
#
|
64
|
+
# @return [Relation::Composite]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def map_with(*names, **opts)
|
68
|
+
names.reduce(self.class.new(root.with(opts), nodes)) { |a, e| a >> mappers[e] }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Map graph tuples to custom objects
|
72
|
+
#
|
73
|
+
# @see Relation#map_to
|
74
|
+
#
|
75
|
+
# @return [Graph]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def map_to(klass)
|
79
|
+
self.class.new(root.map_to(klass), nodes)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @see Relation#mapper
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
def mapper
|
86
|
+
mappers[to_ast]
|
87
|
+
end
|
88
|
+
|
89
|
+
# @api private
|
90
|
+
memoize def to_ast
|
91
|
+
[:relation, [name.relation, attr_ast + nodes.map(&:to_ast), meta_ast]]
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# @api private
|
97
|
+
def decorate?(other)
|
98
|
+
super || other.is_a?(Composite) || other.is_a?(Curried)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
def composite_class
|
103
|
+
Relation::Composite
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
class Relation
|
7
|
+
# Materializes a relation and exposes interface to access the data.
|
8
|
+
#
|
9
|
+
# This relation type is returned when a lazy relation is called
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class Loaded
|
13
|
+
include Enumerable
|
14
|
+
include Dry::Equalizer(:source, :collection)
|
15
|
+
|
16
|
+
# Coerce loaded relation to an array
|
17
|
+
#
|
18
|
+
# @return [Array]
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
alias_method :to_ary, :to_a
|
22
|
+
|
23
|
+
# Source relation
|
24
|
+
#
|
25
|
+
# @return [Relation]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
attr_reader :source
|
29
|
+
|
30
|
+
# Materialized relation
|
31
|
+
#
|
32
|
+
# @return [Object]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
attr_reader :collection
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
def initialize(source, collection = source.to_a)
|
39
|
+
@source = source
|
40
|
+
@collection = collection
|
41
|
+
end
|
42
|
+
|
43
|
+
# Yield relation tuples
|
44
|
+
#
|
45
|
+
# @yield [Hash]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def each(&block)
|
49
|
+
return to_enum unless block_given?
|
50
|
+
|
51
|
+
collection.each(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns a single tuple from the relation if there is one.
|
55
|
+
#
|
56
|
+
# @raise [ROM::TupleCountMismatchError] if the relation contains more than
|
57
|
+
# one tuple
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def one
|
61
|
+
if collection.count > 1
|
62
|
+
raise(
|
63
|
+
TupleCountMismatchError,
|
64
|
+
"The relation consists of more than one tuple"
|
65
|
+
)
|
66
|
+
else
|
67
|
+
collection.first
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Like [one], but additionally raises an error if the relation is empty.
|
72
|
+
#
|
73
|
+
# @raise [ROM::TupleCountMismatchError] if the relation does not contain
|
74
|
+
# exactly one tuple
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def one!
|
78
|
+
one || raise(
|
79
|
+
TupleCountMismatchError,
|
80
|
+
"The relation does not contain any tuples"
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return a list of values under provided key
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# all_users = rom.relations[:users].call
|
88
|
+
# all_users.pluck(:name)
|
89
|
+
# # ["Jane", "Joe"]
|
90
|
+
#
|
91
|
+
# @param [Symbol] key The key name
|
92
|
+
#
|
93
|
+
# @return [Array]
|
94
|
+
#
|
95
|
+
# @raise KeyError when provided key doesn't exist in any of the tuples
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
def pluck(key)
|
99
|
+
map { |tuple| tuple.fetch(key) }
|
100
|
+
end
|
101
|
+
|
102
|
+
# Pluck primary key values
|
103
|
+
#
|
104
|
+
# This method *may not work* with adapters that don't provide relations
|
105
|
+
# that have primary key configured
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# users = rom.relations[:users].call
|
109
|
+
# users.primary_keys
|
110
|
+
# # [1, 2, 3]
|
111
|
+
#
|
112
|
+
# @return [Array]
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
def primary_keys
|
116
|
+
pluck(source.primary_key)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return if loaded relation is empty
|
120
|
+
#
|
121
|
+
# @return [TrueClass,FalseClass]
|
122
|
+
#
|
123
|
+
# @api public
|
124
|
+
def empty?
|
125
|
+
collection.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Return a loaded relation with a new collection
|
129
|
+
#
|
130
|
+
# @api public
|
131
|
+
def new(collection)
|
132
|
+
self.class.new(source, collection)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
class Relation
|
5
|
+
# Interface for objects that can be materialized into a loaded relation
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
module Materializable
|
9
|
+
# Coerce the relation to an array
|
10
|
+
#
|
11
|
+
# @return [Array]
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def to_a
|
15
|
+
call.to_a
|
16
|
+
end
|
17
|
+
alias_method :to_ary, :to_a
|
18
|
+
|
19
|
+
# Yield relation tuples
|
20
|
+
#
|
21
|
+
# @yield [Hash,Object]
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def each(&block)
|
25
|
+
return to_enum unless block_given?
|
26
|
+
|
27
|
+
to_a.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Delegate to loaded relation and return one object
|
31
|
+
#
|
32
|
+
# @return [Object]
|
33
|
+
#
|
34
|
+
# @see Loaded#one
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def one
|
38
|
+
call.one
|
39
|
+
end
|
40
|
+
|
41
|
+
# Delegate to loaded relation and return one object
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
# @see Loaded#one
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def one!
|
49
|
+
call.one!
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return first tuple from a relation coerced to an array
|
53
|
+
#
|
54
|
+
# @return [Object]
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
def first
|
58
|
+
to_a.first
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
require "dry/core/equalizer"
|
5
|
+
require "rom/support/inflector"
|
6
|
+
|
7
|
+
module ROM
|
8
|
+
class Relation
|
9
|
+
# Relation name container
|
10
|
+
#
|
11
|
+
# This is a simple struct with two fields.
|
12
|
+
# It handles both relation registration name (i.e. Symbol) and dataset name.
|
13
|
+
# The reason we need it is a simplification of passing around these two objects.
|
14
|
+
# It is quite common to have a dataset named differently from a relation
|
15
|
+
# built on top if you are dealing with a legacy DB and often you need both
|
16
|
+
# to support things such as associations (rom-sql as an example).
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
class Name
|
20
|
+
include Dry::Equalizer(:relation, :dataset, :key)
|
21
|
+
|
22
|
+
# Coerce an object to a Name instance
|
23
|
+
#
|
24
|
+
# @return [ROM::Relation::Name]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def self.call(*args)
|
28
|
+
cache.fetch_or_store(args.hash) do
|
29
|
+
relation, dataset, aliaz = args
|
30
|
+
|
31
|
+
case relation
|
32
|
+
when self then relation
|
33
|
+
when Symbol then new(relation, dataset, aliaz)
|
34
|
+
when Class, String then new(Inflector.component_id(relation).to_sym)
|
35
|
+
else
|
36
|
+
raise ArgumentError, "+#{relation}+ is not supported"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
# @api private
|
43
|
+
alias_method :[], :call
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def self.cache
|
48
|
+
@cache ||= Concurrent::Map.new
|
49
|
+
end
|
50
|
+
|
51
|
+
# Relation registration name
|
52
|
+
#
|
53
|
+
# @return [Symbol]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
attr_reader :relation
|
57
|
+
|
58
|
+
# Underlying dataset name
|
59
|
+
#
|
60
|
+
# @return [Symbol]
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
attr_reader :dataset
|
64
|
+
|
65
|
+
attr_reader :aliaz
|
66
|
+
|
67
|
+
attr_reader :key
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def initialize(relation, dataset = relation, aliaz = nil)
|
71
|
+
@relation = relation
|
72
|
+
@dataset = dataset || relation
|
73
|
+
@key = aliaz || relation
|
74
|
+
@aliaz = aliaz
|
75
|
+
freeze
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
def as(aliaz)
|
80
|
+
self.class[relation, dataset, aliaz]
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
def aliased?
|
85
|
+
aliaz && aliaz != relation
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return relation name
|
89
|
+
#
|
90
|
+
# @return [String]
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
def to_s
|
94
|
+
if aliased?
|
95
|
+
"#{relation} on #{dataset} as #{aliaz}"
|
96
|
+
elsif relation == dataset
|
97
|
+
relation.to_s
|
98
|
+
else
|
99
|
+
"#{relation} on #{dataset}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Alias for registration key implicitly called by ROM::Registry
|
104
|
+
#
|
105
|
+
# @return [Symbol]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
def to_sym
|
109
|
+
relation
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return inspected relation
|
113
|
+
#
|
114
|
+
# @return [String]
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def inspect
|
118
|
+
"#{self.class.name}(#{self})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/relation/graph"
|
4
|
+
require "rom/relation/combined"
|
5
|
+
|
6
|
+
module ROM
|
7
|
+
class Relation
|
8
|
+
# Relation wrapping other relations
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Wrap < Graph
|
12
|
+
# Wrap more relations
|
13
|
+
#
|
14
|
+
# @see Relation#wrap
|
15
|
+
#
|
16
|
+
# @return [Wrap]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def wrap(*args)
|
20
|
+
self.class.new(root, nodes + root.wrap(*args).nodes)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Materialize a wrap
|
24
|
+
#
|
25
|
+
# @see Relation#call
|
26
|
+
#
|
27
|
+
# @return [Loaded]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def call(*args)
|
31
|
+
if auto_map?
|
32
|
+
Loaded.new(self, mapper.(relation.with(auto_map: false, auto_struct: false)))
|
33
|
+
else
|
34
|
+
Loaded.new(self, relation.(*args))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return an adapter-specific relation representing a wrap
|
39
|
+
#
|
40
|
+
# @abstract
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def relation
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return if this is a wrap relation
|
48
|
+
#
|
49
|
+
# @return [true]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def wrap?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
def decorate?(other)
|
60
|
+
super || other.is_a?(Combined)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|