rom-core 4.0.0.beta1
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 +7 -0
- data/CHANGELOG.md +603 -0
- data/LICENSE +20 -0
- data/README.md +18 -0
- data/lib/rom-core.rb +1 -0
- data/lib/rom/array_dataset.rb +44 -0
- data/lib/rom/association_set.rb +16 -0
- data/lib/rom/associations/abstract.rb +135 -0
- data/lib/rom/associations/definitions.rb +5 -0
- data/lib/rom/associations/definitions/abstract.rb +116 -0
- data/lib/rom/associations/definitions/many_to_many.rb +24 -0
- data/lib/rom/associations/definitions/many_to_one.rb +11 -0
- data/lib/rom/associations/definitions/one_to_many.rb +11 -0
- data/lib/rom/associations/definitions/one_to_one.rb +11 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
- data/lib/rom/associations/many_to_many.rb +81 -0
- data/lib/rom/associations/many_to_one.rb +37 -0
- data/lib/rom/associations/one_to_many.rb +37 -0
- data/lib/rom/associations/one_to_one.rb +8 -0
- data/lib/rom/associations/one_to_one_through.rb +8 -0
- data/lib/rom/associations/through_identifier.rb +39 -0
- data/lib/rom/auto_curry.rb +55 -0
- data/lib/rom/cache.rb +46 -0
- data/lib/rom/command.rb +488 -0
- data/lib/rom/command_compiler.rb +239 -0
- data/lib/rom/command_proxy.rb +24 -0
- data/lib/rom/command_registry.rb +141 -0
- data/lib/rom/commands.rb +3 -0
- data/lib/rom/commands/class_interface.rb +270 -0
- data/lib/rom/commands/composite.rb +53 -0
- data/lib/rom/commands/create.rb +13 -0
- data/lib/rom/commands/delete.rb +14 -0
- data/lib/rom/commands/graph.rb +88 -0
- data/lib/rom/commands/graph/class_interface.rb +62 -0
- data/lib/rom/commands/graph/input_evaluator.rb +62 -0
- data/lib/rom/commands/lazy.rb +99 -0
- data/lib/rom/commands/lazy/create.rb +23 -0
- data/lib/rom/commands/lazy/delete.rb +27 -0
- data/lib/rom/commands/lazy/update.rb +34 -0
- data/lib/rom/commands/result.rb +96 -0
- data/lib/rom/commands/update.rb +14 -0
- data/lib/rom/configuration.rb +114 -0
- data/lib/rom/configuration_dsl.rb +87 -0
- data/lib/rom/configuration_dsl/command.rb +41 -0
- data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
- data/lib/rom/configuration_dsl/relation.rb +26 -0
- data/lib/rom/configuration_plugin.rb +17 -0
- data/lib/rom/constants.rb +64 -0
- data/lib/rom/container.rb +147 -0
- data/lib/rom/core.rb +46 -0
- data/lib/rom/create_container.rb +60 -0
- data/lib/rom/data_proxy.rb +94 -0
- data/lib/rom/enumerable_dataset.rb +68 -0
- data/lib/rom/environment.rb +70 -0
- data/lib/rom/gateway.rb +184 -0
- data/lib/rom/global.rb +58 -0
- data/lib/rom/global/plugin_dsl.rb +47 -0
- data/lib/rom/initializer.rb +64 -0
- data/lib/rom/lint/enumerable_dataset.rb +54 -0
- data/lib/rom/lint/gateway.rb +120 -0
- data/lib/rom/lint/linter.rb +78 -0
- data/lib/rom/lint/spec.rb +20 -0
- data/lib/rom/lint/test.rb +98 -0
- data/lib/rom/mapper_registry.rb +24 -0
- data/lib/rom/memory.rb +4 -0
- data/lib/rom/memory/associations.rb +4 -0
- data/lib/rom/memory/associations/many_to_many.rb +10 -0
- data/lib/rom/memory/associations/many_to_one.rb +10 -0
- data/lib/rom/memory/associations/one_to_many.rb +10 -0
- data/lib/rom/memory/associations/one_to_one.rb +10 -0
- data/lib/rom/memory/commands.rb +56 -0
- data/lib/rom/memory/dataset.rb +97 -0
- data/lib/rom/memory/gateway.rb +64 -0
- data/lib/rom/memory/relation.rb +62 -0
- data/lib/rom/memory/schema.rb +23 -0
- data/lib/rom/memory/storage.rb +59 -0
- data/lib/rom/memory/types.rb +9 -0
- data/lib/rom/pipeline.rb +105 -0
- data/lib/rom/plugin.rb +25 -0
- data/lib/rom/plugin_base.rb +45 -0
- data/lib/rom/plugin_registry.rb +197 -0
- data/lib/rom/plugins/command/schema.rb +37 -0
- data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
- data/lib/rom/plugins/relation/instrumentation.rb +51 -0
- data/lib/rom/plugins/relation/registry_reader.rb +44 -0
- data/lib/rom/plugins/schema/timestamps.rb +58 -0
- data/lib/rom/registry.rb +71 -0
- data/lib/rom/relation.rb +548 -0
- data/lib/rom/relation/class_interface.rb +282 -0
- data/lib/rom/relation/commands.rb +23 -0
- data/lib/rom/relation/composite.rb +46 -0
- data/lib/rom/relation/curried.rb +103 -0
- data/lib/rom/relation/graph.rb +197 -0
- data/lib/rom/relation/loaded.rb +127 -0
- data/lib/rom/relation/materializable.rb +66 -0
- data/lib/rom/relation/name.rb +111 -0
- data/lib/rom/relation/view_dsl.rb +64 -0
- data/lib/rom/relation/wrap.rb +83 -0
- data/lib/rom/relation_registry.rb +10 -0
- data/lib/rom/schema.rb +437 -0
- data/lib/rom/schema/associations_dsl.rb +195 -0
- data/lib/rom/schema/attribute.rb +419 -0
- data/lib/rom/schema/dsl.rb +164 -0
- data/lib/rom/schema/inferrer.rb +66 -0
- data/lib/rom/schema_plugin.rb +27 -0
- data/lib/rom/setup.rb +68 -0
- data/lib/rom/setup/auto_registration.rb +74 -0
- data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
- data/lib/rom/setup/finalize.rb +103 -0
- data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
- data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
- data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
- data/lib/rom/support/configurable.rb +85 -0
- data/lib/rom/support/memoizable.rb +58 -0
- data/lib/rom/support/notifications.rb +103 -0
- data/lib/rom/transaction.rb +24 -0
- data/lib/rom/types.rb +26 -0
- data/lib/rom/version.rb +5 -0
- metadata +289 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'rom/array_dataset'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Memory
|
5
|
+
# In-memory dataset
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
class Dataset
|
9
|
+
include ArrayDataset
|
10
|
+
|
11
|
+
# Join two datasets
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def join(*args)
|
15
|
+
left, right = args.size > 1 ? args : [self, args.first]
|
16
|
+
|
17
|
+
join_map = left.each_with_object({}) { |tuple, h|
|
18
|
+
others = right.to_a.find_all { |t| (tuple.to_a & t.to_a).any? }
|
19
|
+
(h[tuple] ||= []).concat(others)
|
20
|
+
}
|
21
|
+
|
22
|
+
tuples = left.flat_map { |tuple|
|
23
|
+
join_map[tuple].map { |other| tuple.merge(other) }
|
24
|
+
}
|
25
|
+
|
26
|
+
self.class.new(tuples, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Restrict a dataset
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def restrict(criteria = nil)
|
33
|
+
return find_all { |tuple| yield(tuple) } unless criteria
|
34
|
+
find_all do |tuple|
|
35
|
+
criteria.all? do |k, v|
|
36
|
+
case v
|
37
|
+
when Array then v.include?(tuple[k])
|
38
|
+
when Regexp then tuple[k].match(v)
|
39
|
+
else tuple[k].eql?(v)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Project a dataset
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def project(*names)
|
49
|
+
map { |tuple| tuple.reject { |key| !names.include?(key) } }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Sort a dataset
|
53
|
+
#
|
54
|
+
# @param [Array<Symbol>] names
|
55
|
+
# Names of fields to order tuples by
|
56
|
+
#
|
57
|
+
# @option [Boolean] :nils_first (false)
|
58
|
+
# Whether `nil` values should be placed before others
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
def order(*fields)
|
62
|
+
nils_first = fields.pop[:nils_first] if fields.last.is_a?(Hash)
|
63
|
+
|
64
|
+
sort do |a, b|
|
65
|
+
fields # finds the first difference between selected fields of tuples
|
66
|
+
.map { |n| __compare__ a[n], b[n], nils_first }
|
67
|
+
.detect(-> { 0 }) { |r| r != 0 }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Insert tuple into a dataset
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
def insert(tuple)
|
75
|
+
data << tuple
|
76
|
+
self
|
77
|
+
end
|
78
|
+
alias_method :<<, :insert
|
79
|
+
|
80
|
+
# Delete tuples from a dataset
|
81
|
+
#
|
82
|
+
# @api public
|
83
|
+
def delete(tuple)
|
84
|
+
data.delete(tuple)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Compares two values, that are either comparable, or can be nils
|
91
|
+
def __compare__(a, b, nils_first)
|
92
|
+
return a <=> b unless a.nil? ^ b.nil?
|
93
|
+
(nils_first ^ b.nil?) ? -1 : 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rom/gateway'
|
2
|
+
require 'rom/memory/storage'
|
3
|
+
require 'rom/memory/commands'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module Memory
|
7
|
+
# In-memory gateway interface
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# gateway = ROM::Memory::Gateway.new
|
11
|
+
# gateway.dataset(:users)
|
12
|
+
# gateway[:users].insert(name: 'Jane')
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
class Gateway < ROM::Gateway
|
16
|
+
adapter :memory
|
17
|
+
|
18
|
+
# @return [Object] default logger
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
attr_reader :logger
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def initialize
|
25
|
+
@connection = Storage.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set default logger for the gateway
|
29
|
+
#
|
30
|
+
# @param [Object] logger object
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def use_logger(logger)
|
34
|
+
@logger = logger
|
35
|
+
end
|
36
|
+
|
37
|
+
# Register a dataset in the gateway
|
38
|
+
#
|
39
|
+
# If dataset already exists it will be returned
|
40
|
+
#
|
41
|
+
# @return [Dataset]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def dataset(name)
|
45
|
+
self[name] || connection.create_dataset(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @see ROM::Gateway#dataset?
|
49
|
+
def dataset?(name)
|
50
|
+
connection.key?(name)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return dataset with the given name
|
54
|
+
#
|
55
|
+
# @param (see ROM::Gateway#[])
|
56
|
+
# @return [Memory::Dataset]
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def [](name)
|
60
|
+
connection[name]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rom/memory/types'
|
2
|
+
require 'rom/memory/schema'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module Memory
|
6
|
+
# Relation subclass for memory adapter
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class Users < ROM::Relation[:memory]
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
class Relation < ROM::Relation
|
14
|
+
include Enumerable
|
15
|
+
include Memory
|
16
|
+
|
17
|
+
adapter :memory
|
18
|
+
schema_class Memory::Schema
|
19
|
+
|
20
|
+
forward :take, :join, :restrict, :order
|
21
|
+
|
22
|
+
# Project a relation with provided attribute names
|
23
|
+
#
|
24
|
+
# @param [*Array] names A list with attribute names
|
25
|
+
#
|
26
|
+
# @return [Memory::Relation]
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def project(*names)
|
30
|
+
schema.project(*names).(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Insert tuples into the relation
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# users.insert(name: 'Jane')
|
37
|
+
#
|
38
|
+
# @return [Relation]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def insert(*args)
|
42
|
+
dataset.insert(*args)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias_method :<<, :insert
|
46
|
+
|
47
|
+
# Delete tuples from the relation
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# users.insert(name: 'Jane')
|
51
|
+
# users.delete(name: 'Jane')
|
52
|
+
#
|
53
|
+
# @return [Relation]
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def delete(*args)
|
57
|
+
dataset.delete(*args)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rom/schema'
|
2
|
+
require 'rom/memory/associations'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module Memory
|
6
|
+
class Schema < ROM::Schema
|
7
|
+
# @see Schema#call
|
8
|
+
# @api public
|
9
|
+
def call(relation)
|
10
|
+
relation.new(relation.dataset.project(*map(&:name)), schema: self)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def finalize_associations!(relations:)
|
15
|
+
super do
|
16
|
+
associations.map do |definition|
|
17
|
+
Memory::Associations.const_get(definition.type).new(definition, relations)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'concurrent/hash'
|
2
|
+
require 'concurrent/array'
|
3
|
+
|
4
|
+
require 'rom/memory/dataset'
|
5
|
+
|
6
|
+
module ROM
|
7
|
+
module Memory
|
8
|
+
# In-memory thread-safe data storage
|
9
|
+
#
|
10
|
+
# @private
|
11
|
+
class Storage
|
12
|
+
# Dataset registry
|
13
|
+
#
|
14
|
+
# @return [ThreadSafe::Hash]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
attr_reader :data
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def initialize
|
21
|
+
@data = Concurrent::Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Dataset]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def [](name)
|
28
|
+
data[name]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Register a new dataset
|
32
|
+
#
|
33
|
+
# @return [Dataset]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def create_dataset(name)
|
37
|
+
data[name] = Dataset.new(Concurrent::Array.new)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Check if there's dataset under specified key
|
41
|
+
#
|
42
|
+
# @return [Boolean]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def key?(name)
|
46
|
+
data.key?(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return registered datasets count
|
50
|
+
#
|
51
|
+
# @return [Integer]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def size
|
55
|
+
data.size
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rom/pipeline.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module ROM
|
2
|
+
# Data pipeline common interface
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
module Pipeline
|
6
|
+
# Common `>>` operator extension
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
module Operator
|
10
|
+
# Compose two relation with a left-to-right composition
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# users.by_name('Jane') >> tasks.for_users
|
14
|
+
#
|
15
|
+
# @param [Relation] other The right relation
|
16
|
+
#
|
17
|
+
# @return [Relation::Composite]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def >>(other)
|
21
|
+
composite_class.new(self, other)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
def composite_class
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
include Operator
|
33
|
+
|
34
|
+
# Send data through specified mappers
|
35
|
+
#
|
36
|
+
# @return [Relation::Composite]
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def map_with(*names)
|
40
|
+
[self, *names.map { |name| mappers[name] }]
|
41
|
+
.reduce { |a, e| composite_class.new(a, e) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Forwards messages to the left side of a pipeline
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
module Proxy
|
48
|
+
# @api private
|
49
|
+
def respond_to_missing?(name, include_private = false)
|
50
|
+
left.respond_to?(name) || super
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Check if response from method missing should be decorated
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def decorate?(response)
|
59
|
+
response.is_a?(left.class)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
def method_missing(name, *args, &block)
|
64
|
+
if left.respond_to?(name)
|
65
|
+
response = left.__send__(name, *args, &block)
|
66
|
+
|
67
|
+
if decorate?(response)
|
68
|
+
self.class.new(response, right)
|
69
|
+
else
|
70
|
+
response
|
71
|
+
end
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Base composite class with left-to-right pipeline behavior
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
class Composite
|
82
|
+
include Dry::Equalizer(:left, :right)
|
83
|
+
include Proxy
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
attr_reader :left
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
attr_reader :right
|
90
|
+
|
91
|
+
# @api private
|
92
|
+
def initialize(left, right)
|
93
|
+
@left = left
|
94
|
+
@right = right
|
95
|
+
end
|
96
|
+
|
97
|
+
# Compose this composite with another object
|
98
|
+
#
|
99
|
+
# @api public
|
100
|
+
def >>(other)
|
101
|
+
self.class.new(self, other)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/rom/plugin.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rom/constants'
|
2
|
+
require 'rom/plugin_base'
|
3
|
+
require 'rom/support/configurable'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Plugin is a simple object used to store plugin configurations
|
7
|
+
#
|
8
|
+
# @private
|
9
|
+
class Plugin < PluginBase
|
10
|
+
include Configurable
|
11
|
+
|
12
|
+
# Apply this plugin to the provided class
|
13
|
+
#
|
14
|
+
# @param [Class] klass
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
def apply_to(klass, options = EMPTY_HASH)
|
18
|
+
if options.any?
|
19
|
+
klass.send(:include, mod.new(options))
|
20
|
+
else
|
21
|
+
klass.send(:include, mod)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|