rom-core 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|