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,34 @@
|
|
1
|
+
module ROM
|
2
|
+
module Commands
|
3
|
+
class Lazy
|
4
|
+
class Update < Lazy
|
5
|
+
def call(*args)
|
6
|
+
first = args.first
|
7
|
+
last = args.last
|
8
|
+
size = args.size
|
9
|
+
|
10
|
+
if size > 1 && last.is_a?(Array)
|
11
|
+
last.map.with_index do |parent, index|
|
12
|
+
children = evaluator.call(first, index)
|
13
|
+
|
14
|
+
children.map do |child|
|
15
|
+
command_proc[command, parent, child].call(child, parent)
|
16
|
+
end
|
17
|
+
end.reduce(:concat)
|
18
|
+
else
|
19
|
+
input = evaluator.call(first)
|
20
|
+
|
21
|
+
if input.is_a?(Array)
|
22
|
+
input.map.with_index do |item, index|
|
23
|
+
command_proc[command, last, item].call(item, *args[1..size-1])
|
24
|
+
end
|
25
|
+
else
|
26
|
+
command_proc[command, *(size > 1 ? [last, input] : [input])]
|
27
|
+
.call(input, *args[1..size-1])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ROM
|
2
|
+
module Commands
|
3
|
+
# Abstract result class for success and error results
|
4
|
+
#
|
5
|
+
# @api public
|
6
|
+
class Result
|
7
|
+
# Return command execution result
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
# Return potential command execution result error
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
attr_reader :error
|
16
|
+
|
17
|
+
# Coerce result to an array
|
18
|
+
#
|
19
|
+
# @abstract
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def to_ary
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
alias_method :to_a, :to_ary
|
26
|
+
|
27
|
+
# Return true if command successful
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def success?
|
31
|
+
is_a?(Success)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return true if command failed
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def failure?
|
38
|
+
is_a?(Failure)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Success result has a value and no error
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
class Success < Result
|
45
|
+
# @api private
|
46
|
+
def initialize(value)
|
47
|
+
@value = value.is_a?(self.class) ? value.value : value
|
48
|
+
end
|
49
|
+
|
50
|
+
# Call next command on continuation
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
def >(other)
|
54
|
+
other.call(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the value
|
58
|
+
#
|
59
|
+
# @return [Array]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def to_ary
|
63
|
+
value.to_ary
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Failure result has an error and no value
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
class Failure < Result
|
71
|
+
# @api private
|
72
|
+
def initialize(error)
|
73
|
+
@error = error
|
74
|
+
end
|
75
|
+
|
76
|
+
# Do not call next command on continuation
|
77
|
+
#
|
78
|
+
# @return [self]
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def >(_other)
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return the error
|
86
|
+
#
|
87
|
+
# @return [Array<CommandError>]
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def to_ary
|
91
|
+
error
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'rom/environment'
|
4
|
+
require 'rom/setup'
|
5
|
+
require 'rom/configuration_dsl'
|
6
|
+
require 'rom/support/notifications'
|
7
|
+
|
8
|
+
module ROM
|
9
|
+
class Configuration
|
10
|
+
extend Forwardable
|
11
|
+
extend Notifications
|
12
|
+
|
13
|
+
register_event('configuration.relations.class.ready')
|
14
|
+
register_event('configuration.relations.object.registered')
|
15
|
+
register_event('configuration.relations.registry.created')
|
16
|
+
register_event('configuration.relations.schema.allocated')
|
17
|
+
register_event('configuration.relations.schema.set')
|
18
|
+
register_event('configuration.relations.dataset.allocated')
|
19
|
+
register_event('configuration.commands.class.before_build')
|
20
|
+
|
21
|
+
include ROM::ConfigurationDSL
|
22
|
+
|
23
|
+
NoDefaultAdapterError = Class.new(StandardError)
|
24
|
+
|
25
|
+
attr_reader :environment, :setup, :notifications
|
26
|
+
|
27
|
+
def_delegators :@setup, :register_relation, :register_command, :register_mapper, :register_plugin,
|
28
|
+
:command_classes, :mapper_classes,
|
29
|
+
:auto_registration
|
30
|
+
|
31
|
+
def_delegators :@environment, :gateways, :gateways_map, :configure, :config
|
32
|
+
|
33
|
+
# @api public
|
34
|
+
def initialize(*args, &block)
|
35
|
+
@environment = Environment.new(*args)
|
36
|
+
@notifications = Notifications.event_bus(:configuration)
|
37
|
+
@setup = Setup.new(notifications)
|
38
|
+
|
39
|
+
use :mappers # enable mappers by default
|
40
|
+
|
41
|
+
block.call(self) unless block.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
# Apply a plugin to the configuration
|
45
|
+
#
|
46
|
+
# @param [Mixed] The plugin identifier, usually a Symbol
|
47
|
+
# @param [Hash] Plugin options
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def use(plugin, options = {})
|
51
|
+
if plugin.is_a?(Array)
|
52
|
+
plugin.each { |p| use(p) }
|
53
|
+
elsif plugin.is_a?(Hash)
|
54
|
+
plugin.to_a.each { |p| use(*p) }
|
55
|
+
else
|
56
|
+
ROM.plugin_registry.configuration.fetch(plugin).apply_to(self, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return gateway identified by name
|
63
|
+
#
|
64
|
+
# @return [Gateway]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
def [](name)
|
68
|
+
gateways.fetch(name)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns gateway if method is a name of a registered gateway
|
72
|
+
#
|
73
|
+
# @return [Gateway]
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
def method_missing(name, *)
|
77
|
+
gateways.fetch(name) { super }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Hook for respond_to? used internally
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
def respond_to?(name, include_all = false)
|
84
|
+
gateways.key?(name) || super
|
85
|
+
end
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
def default_gateway
|
89
|
+
@default_gateway ||= gateways[:default]
|
90
|
+
end
|
91
|
+
|
92
|
+
# @api private
|
93
|
+
def adapter_for_gateway(gateway)
|
94
|
+
ROM.adapters.select do |key, value|
|
95
|
+
value.const_defined?(:Gateway) && gateway.kind_of?(value.const_get(:Gateway))
|
96
|
+
end.keys.first
|
97
|
+
end
|
98
|
+
|
99
|
+
# @api private
|
100
|
+
def relation_classes(gateway = nil)
|
101
|
+
if gateway
|
102
|
+
gw_name = gateway.is_a?(Symbol) ? gateway : gateways_map[gateway]
|
103
|
+
setup.relation_classes.select { |rel| rel.gateway == gw_name }
|
104
|
+
else
|
105
|
+
setup.relation_classes
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @api private
|
110
|
+
def default_adapter
|
111
|
+
@default_adapter ||= adapter_for_gateway(default_gateway) || ROM.adapters.keys.first
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'rom/configuration_dsl/relation'
|
2
|
+
require 'rom/configuration_dsl/command_dsl'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
# This extends Configuration class with the DSL methods
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
module ConfigurationDSL
|
9
|
+
# Relation definition DSL
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# setup.relation(:users) do
|
14
|
+
# def names
|
15
|
+
# project(:name)
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def relation(name, options = EMPTY_HASH, &block)
|
21
|
+
klass_opts = { adapter: default_adapter }.merge(options)
|
22
|
+
klass = Relation.build_class(name, klass_opts)
|
23
|
+
klass.schema_opts(dataset: name, relation: name)
|
24
|
+
klass.class_eval(&block) if block
|
25
|
+
register_relation(klass)
|
26
|
+
klass
|
27
|
+
end
|
28
|
+
|
29
|
+
# Command definition DSL
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# setup.commands(:users) do
|
34
|
+
# define(:create) do
|
35
|
+
# input NewUserParams
|
36
|
+
# result :one
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# define(:update) do
|
40
|
+
# input UserParams
|
41
|
+
# result :many
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# define(:delete) do
|
45
|
+
# result :many
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def commands(name, &block)
|
51
|
+
register_command(*CommandDSL.new(name, default_adapter, &block).command_classes)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Configures a plugin for a specific adapter to be enabled for all relations
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# config = ROM::Configuration.new(:sql, 'sqlite::memory')
|
58
|
+
#
|
59
|
+
# config.plugin(:sql, relations: :instrumentation) do |p|
|
60
|
+
# p.notifications = MyNotificationsBackend
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# config.plugin(:sql, relations: :pagination)
|
64
|
+
#
|
65
|
+
# @param [Symbol] adapter The adapter identifier
|
66
|
+
# @param [Hash<Symbol=>Symbol>] spec Component identifier => plugin identifier
|
67
|
+
#
|
68
|
+
# @return [Plugin]
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
def plugin(adapter, spec, &block)
|
72
|
+
type, name = spec.flatten(1)
|
73
|
+
plugin = plugin_registry.send(type).adapter(adapter).fetch(name) { plugin_registry.send(type).fetch(name) }
|
74
|
+
|
75
|
+
if block
|
76
|
+
register_plugin(plugin.configure(&block))
|
77
|
+
else
|
78
|
+
register_plugin(plugin)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @api private
|
83
|
+
def plugin_registry
|
84
|
+
ROM.plugin_registry
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'dry/core/inflector'
|
2
|
+
require 'dry/core/class_builder'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module ConfigurationDSL
|
6
|
+
# Setup DSL-specific command extensions
|
7
|
+
#
|
8
|
+
# @private
|
9
|
+
class Command
|
10
|
+
# Generate a command subclass
|
11
|
+
#
|
12
|
+
# This is used by Setup#commands DSL and its `define` block
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.build_class(name, relation, options = EMPTY_HASH, &block)
|
16
|
+
type = options.fetch(:type) { name }
|
17
|
+
command_type = Dry::Core::Inflector.classify(type)
|
18
|
+
adapter = options.fetch(:adapter)
|
19
|
+
parent = ROM::Command.adapter_namespace(adapter).const_get(command_type)
|
20
|
+
class_name = generate_class_name(adapter, command_type, relation)
|
21
|
+
|
22
|
+
Dry::Core::ClassBuilder.new(name: class_name, parent: parent).call do |klass|
|
23
|
+
klass.register_as(name)
|
24
|
+
klass.relation(relation)
|
25
|
+
klass.class_eval(&block) if block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Create a command subclass name based on adapter, type and relation
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def self.generate_class_name(adapter, command_type, relation)
|
33
|
+
pieces = ['ROM']
|
34
|
+
pieces << Dry::Core::Inflector.classify(adapter)
|
35
|
+
pieces << 'Commands'
|
36
|
+
pieces << "#{command_type}[#{Dry::Core::Inflector.classify(relation)}s]"
|
37
|
+
pieces.join('::')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rom/configuration_dsl/command'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module ConfigurationDSL
|
5
|
+
# Command `define` DSL used by Setup#commands
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
class CommandDSL
|
9
|
+
attr_reader :relation, :adapter, :command_classes
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
def initialize(relation, adapter = nil, &block)
|
13
|
+
@relation = relation
|
14
|
+
@adapter = adapter
|
15
|
+
@command_classes = []
|
16
|
+
instance_exec(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Define a command class
|
20
|
+
#
|
21
|
+
# @param [Symbol] name of the command
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [Symbol] :type The type of the command
|
24
|
+
#
|
25
|
+
# @return [Class] generated class
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def define(name, options = EMPTY_HASH, &block)
|
29
|
+
@command_classes << Command.build_class(
|
30
|
+
name, relation, { adapter: adapter }.merge(options), &block
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'dry/core/class_builder'
|
2
|
+
require 'dry/core/inflector'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module ConfigurationDSL
|
6
|
+
# Setup DSL-specific relation extensions
|
7
|
+
#
|
8
|
+
# @private
|
9
|
+
class Relation
|
10
|
+
# Generate a relation subclass
|
11
|
+
#
|
12
|
+
# This is used by Setup#relation DSL
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.build_class(name, options = EMPTY_HASH)
|
16
|
+
class_name = "ROM::Relation[#{Dry::Core::Inflector.camelize(name)}]"
|
17
|
+
adapter = options.fetch(:adapter)
|
18
|
+
|
19
|
+
Dry::Core::ClassBuilder.new(name: class_name, parent: ROM::Relation[adapter]).call do |klass|
|
20
|
+
klass.gateway(options.fetch(:gateway, :default))
|
21
|
+
klass.schema(name) { }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|