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,120 @@
|
|
1
|
+
require 'rom/lint/linter'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Lint
|
5
|
+
# Ensures that a [ROM::Gateway] extension provides datasets through the
|
6
|
+
# expected methods
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Gateway < ROM::Lint::Linter
|
10
|
+
# The gateway identifier e.g. +:memory+
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
attr_reader :identifier
|
14
|
+
|
15
|
+
# The gateway class
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
attr_reader :gateway
|
19
|
+
|
20
|
+
# The optional URI
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
attr_reader :uri
|
24
|
+
|
25
|
+
# Gateway instance used in lint tests
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
attr_reader :gateway_instance
|
29
|
+
|
30
|
+
# Create a gateway linter
|
31
|
+
#
|
32
|
+
# @param [Symbol] identifier
|
33
|
+
# @param [Class] gateway
|
34
|
+
# @param [String] uri optional
|
35
|
+
def initialize(identifier, gateway, uri = nil)
|
36
|
+
@identifier = identifier
|
37
|
+
@gateway = gateway
|
38
|
+
@uri = uri
|
39
|
+
@gateway_instance = setup_gateway_instance
|
40
|
+
end
|
41
|
+
|
42
|
+
# Lint: Ensure that +gateway+ setups up its instance
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def lint_gateway_setup
|
46
|
+
return if gateway_instance.instance_of? gateway
|
47
|
+
|
48
|
+
complain <<-STRING
|
49
|
+
#{gateway}.setup must return a gateway instance but
|
50
|
+
returned #{gateway_instance.inspect}
|
51
|
+
STRING
|
52
|
+
end
|
53
|
+
|
54
|
+
# Lint: Ensure that +gateway_instance+ responds to +[]+
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
def lint_dataset_reader
|
58
|
+
return if gateway_instance.respond_to? :[]
|
59
|
+
|
60
|
+
complain "#{gateway_instance} must respond to []"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Lint: Ensure that +gateway_instance+ responds to +dataset?+
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def lint_dataset_predicate
|
67
|
+
return if gateway_instance.respond_to? :dataset?
|
68
|
+
|
69
|
+
complain "#{gateway_instance} must respond to dataset?"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Lint: Ensure +gateway_instance+ supports +transaction+ interface
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def lint_transaction_support
|
76
|
+
result = gateway_instance.transaction { 1 }
|
77
|
+
|
78
|
+
if result != 1
|
79
|
+
complain "#{gateway_instance} must return the result of a transaction block"
|
80
|
+
end
|
81
|
+
|
82
|
+
gateway_instance.transaction do |t|
|
83
|
+
t.rollback!
|
84
|
+
|
85
|
+
complain "#{gateway_instance} must interrupt a transaction on rollback"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Lint: Ensure +gateway_instance+ returns adapter name
|
90
|
+
def lint_adapter_reader
|
91
|
+
if gateway_instance.adapter != identifier
|
92
|
+
complain "#{gateway_instance} must have the adapter identifier set to #{identifier.inspect}"
|
93
|
+
end
|
94
|
+
rescue MissingAdapterIdentifierError
|
95
|
+
complain "#{gateway_instance} is missing the adapter identifier"
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Setup gateway instance
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
def setup_gateway_instance
|
104
|
+
if uri
|
105
|
+
ROM::Gateway.setup(identifier, uri)
|
106
|
+
else
|
107
|
+
ROM::Gateway.setup(identifier)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Run Gateway#disconnect
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
def after_lint
|
115
|
+
super
|
116
|
+
gateway_instance.disconnect
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module ROM
|
2
|
+
module Lint
|
3
|
+
# Base class for building linters that check source code
|
4
|
+
#
|
5
|
+
# Linters are used by authors of ROM adapters to verify that their
|
6
|
+
# integration complies with the ROM api.
|
7
|
+
#
|
8
|
+
# Most of the time, authors won't need to construct linters directly
|
9
|
+
# because the provided test helpers will automatically run when required
|
10
|
+
# in tests and specs.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# require 'rom/lint/spec'
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
class Linter
|
18
|
+
# A failure raised by +complain+
|
19
|
+
Failure = Class.new(StandardError)
|
20
|
+
|
21
|
+
# Iterate over all lint methods
|
22
|
+
#
|
23
|
+
# @yield [String, ROM::Lint]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def self.each_lint
|
27
|
+
return to_enum unless block_given?
|
28
|
+
lints.each { |lint| yield lint, self }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Run a lint method
|
32
|
+
#
|
33
|
+
# @param [String] name
|
34
|
+
#
|
35
|
+
# @raise [ROM::Lint::Linter::Failure] if linting fails
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def lint(name)
|
39
|
+
before_lint
|
40
|
+
public_send name
|
41
|
+
after_lint
|
42
|
+
true # for assertions
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Return a list a lint methods
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def self.lints
|
53
|
+
public_instance_methods(true).grep(/^lint_/).map(&:to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Raise a failure if a lint verification fails
|
57
|
+
#
|
58
|
+
# @raise [ROM::Lint::Linter::Failure]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
def complain(*args)
|
62
|
+
raise Failure, *args
|
63
|
+
end
|
64
|
+
|
65
|
+
# Hook method executed before each lint method run
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def before_lint
|
69
|
+
end
|
70
|
+
|
71
|
+
# Hook method executed after each lint method run
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
def after_lint
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rom/lint/gateway'
|
2
|
+
require 'rom/lint/enumerable_dataset'
|
3
|
+
|
4
|
+
RSpec.shared_examples "a rom gateway" do
|
5
|
+
ROM::Lint::Gateway.each_lint do |name, linter|
|
6
|
+
it name do
|
7
|
+
result = linter.new(identifier, gateway, uri).lint(name)
|
8
|
+
expect(result).to be_truthy
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.shared_examples "a rom enumerable dataset" do
|
14
|
+
ROM::Lint::EnumerableDataset.each_lint do |name, linter|
|
15
|
+
it name do
|
16
|
+
result = linter.new(dataset, data).lint(name)
|
17
|
+
expect(result).to be_truthy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'rom/lint/gateway'
|
2
|
+
require 'rom/lint/enumerable_dataset'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module Lint
|
6
|
+
# A module that helps to define test methods
|
7
|
+
module Test
|
8
|
+
# Defines a test method converting lint failures to assertions
|
9
|
+
#
|
10
|
+
# @param [String] name
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def define_test_method(name, &block)
|
14
|
+
define_method "test_#{name}" do
|
15
|
+
begin
|
16
|
+
instance_eval(&block)
|
17
|
+
rescue ROM::Lint::Linter::Failure => f
|
18
|
+
raise Minitest::Assertion, f.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is a simple lint-test for gateway class to ensure the
|
25
|
+
# basic interfaces are in place
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
#
|
29
|
+
# class MyGatewayTest < Minitest::Test
|
30
|
+
# include ROM::Lint::TestGateway
|
31
|
+
#
|
32
|
+
# def setup
|
33
|
+
# @gateway = MyGateway
|
34
|
+
# @uri = "super_db://something"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
module TestGateway
|
40
|
+
extend ROM::Lint::Test
|
41
|
+
|
42
|
+
# Returns the gateway identifier e.g. +:memory+
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
attr_reader :identifier
|
46
|
+
|
47
|
+
# Returns the gateway class
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
attr_reader :gateway
|
51
|
+
|
52
|
+
# Returns gateway's URI e.g. "super_db://something"
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
attr_reader :uri
|
56
|
+
|
57
|
+
ROM::Lint::Gateway.each_lint do |name, linter|
|
58
|
+
define_test_method name do
|
59
|
+
assert linter.new(identifier, gateway, uri).lint(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# This is a simple lint-test for a gateway dataset class to ensure the
|
65
|
+
# basic behavior is correct
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
#
|
69
|
+
# class MyDatasetLintTest < Minitest::Test
|
70
|
+
# include ROM::Lint::TestEnumerableDataset
|
71
|
+
#
|
72
|
+
# def setup
|
73
|
+
# @data = [{ name: 'Jane', age: 24 }, { name: 'Joe', age: 25 }]
|
74
|
+
# @dataset = MyDataset.new(@data, [:name, :age])
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
# @api public
|
78
|
+
module TestEnumerableDataset
|
79
|
+
extend ROM::Lint::Test
|
80
|
+
|
81
|
+
# Returns the dataset instance
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
attr_reader :dataset
|
85
|
+
|
86
|
+
# Returns the expected data
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
attr_reader :data
|
90
|
+
|
91
|
+
ROM::Lint::EnumerableDataset.each_lint do |name, linter|
|
92
|
+
define_test_method name do
|
93
|
+
assert linter.new(dataset, data).lint(name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rom/struct'
|
2
|
+
require 'rom/registry'
|
3
|
+
require 'rom/mapper_compiler'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# @private
|
7
|
+
class MapperRegistry < Registry
|
8
|
+
def self.element_not_found_error
|
9
|
+
MapperMissingError
|
10
|
+
end
|
11
|
+
|
12
|
+
option :compiler, default: -> { MapperCompiler.new }
|
13
|
+
|
14
|
+
# @see Registry
|
15
|
+
# @api public
|
16
|
+
def [](*args)
|
17
|
+
if args[0].is_a?(Symbol)
|
18
|
+
super
|
19
|
+
else
|
20
|
+
cache.fetch_or_store(args.hash) { compiler.(*args) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/rom/memory.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rom/commands'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Memory
|
5
|
+
# Memory adapter commands namespace
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
module Commands
|
9
|
+
# In-memory create command
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class Create < ROM::Commands::Create
|
13
|
+
adapter :memory
|
14
|
+
use :schema
|
15
|
+
|
16
|
+
# @see ROM::Commands::Create#execute
|
17
|
+
def execute(tuples)
|
18
|
+
Array([tuples]).flatten.map { |tuple|
|
19
|
+
attributes = input[tuple]
|
20
|
+
relation.insert(attributes.to_h)
|
21
|
+
attributes
|
22
|
+
}.to_a
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# In-memory update command
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
class Update < ROM::Commands::Update
|
30
|
+
adapter :memory
|
31
|
+
use :schema
|
32
|
+
|
33
|
+
# @see ROM::Commands::Update#execute
|
34
|
+
def execute(params)
|
35
|
+
attributes = input[params]
|
36
|
+
relation.map { |tuple| tuple.update(attributes.to_h) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# In-memory delete command
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
class Delete < ROM::Commands::Delete
|
44
|
+
adapter :memory
|
45
|
+
|
46
|
+
# @see ROM::Commands::Delete#execute
|
47
|
+
def execute
|
48
|
+
relation.to_a.map do |tuple|
|
49
|
+
source.delete(tuple)
|
50
|
+
tuple
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|