rom 5.3.2 → 6.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -47
- data/LICENSE +1 -1
- data/README.md +6 -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 +273 -36
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/container"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# ROM container is an isolated environment with no global state where all
|
7
|
+
# components are registered. Container objects provide access to your
|
8
|
+
# relations, commands and mappers. ROM containers are usually configured and
|
9
|
+
# handled via framework integrations, although it is easy to use them
|
10
|
+
# standalone.
|
11
|
+
#
|
12
|
+
# There are 3 types of container setup:
|
13
|
+
#
|
14
|
+
# * Setup DSL - a simple block-based configuration which allows configuring
|
15
|
+
# all components and gives you back a container instance. This type is suitable
|
16
|
+
# for small scripts, or in some cases rake tasks
|
17
|
+
# * Explicit setup - this type requires creating a configuration object,
|
18
|
+
# registering component classes (ie relation classes) and passing the config
|
19
|
+
# to container builder function. This type is suitable when your environment
|
20
|
+
# is not typical and you need full control over component registration
|
21
|
+
# * Explicit setup with auto-registration - same as explicit setup but allows
|
22
|
+
# you to configure auto-registration mechanism which will register component
|
23
|
+
# classes for you, based on dir/file naming conventions. This is the most
|
24
|
+
# common type of setup that's used by framework integrations
|
25
|
+
#
|
26
|
+
# @example in-line setup
|
27
|
+
# rom = ROM.setup(:sql, 'sqlite::memory') do |config|
|
28
|
+
# config.default.create_table :users do
|
29
|
+
# primary_key :id
|
30
|
+
# column :name, String, null: false
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# config.relation(:users) do
|
34
|
+
# schema(infer: true)
|
35
|
+
#
|
36
|
+
# def by_name(name)
|
37
|
+
# where(name: name)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# rom.relations[:users].insert(name: "Jane")
|
43
|
+
#
|
44
|
+
# rom.relations[:users].by_name("Jane").to_a
|
45
|
+
# # [{:id=>1, :name=>"Jane"}]
|
46
|
+
#
|
47
|
+
# @example multi-step setup with explicit component classes
|
48
|
+
# config = ROM::Configuration.new(:sql, 'sqlite::memory')
|
49
|
+
#
|
50
|
+
# config.default.create_table :users do
|
51
|
+
# primary_key :id
|
52
|
+
# column :name, String, null: false
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# class Users < ROM::Relation[:sql]
|
56
|
+
# schema(:users, infer: true)
|
57
|
+
#
|
58
|
+
# def by_name(name)
|
59
|
+
# where(name: name)
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# config.register_relation(Users)
|
64
|
+
#
|
65
|
+
# rom = ROM.setup(config)
|
66
|
+
#
|
67
|
+
# rom.relations[:users].insert(name: "Jane")
|
68
|
+
#
|
69
|
+
# rom.relations[:users].by_name("Jane").to_a
|
70
|
+
# # [{:id=>1, :name=>"Jane"}]
|
71
|
+
#
|
72
|
+
#
|
73
|
+
# @example multi-step setup with auto-registration
|
74
|
+
# config = ROM::Configuration.new(:sql, 'sqlite::memory')
|
75
|
+
# config.auto_register('./persistence', namespace: false)
|
76
|
+
#
|
77
|
+
# config.default.create_table :users do
|
78
|
+
# primary_key :id
|
79
|
+
# column :name, String, null: false
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# # ./persistence/relations/users.rb
|
83
|
+
# class Users < ROM::Relation[:sql]
|
84
|
+
# schema(infer: true)
|
85
|
+
#
|
86
|
+
# def by_name(name)
|
87
|
+
# where(name: name)
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# rom = ROM.setup(config)
|
92
|
+
#
|
93
|
+
# rom.relations[:users].insert(name: "Jane")
|
94
|
+
#
|
95
|
+
# rom.relations[:users].by_name("Jane").to_a
|
96
|
+
# # [{:id=>1, :name=>"Jane"}]
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
class Container
|
100
|
+
include Dry::Container::Mixin
|
101
|
+
include Dry::Equalizer(:gateways, :relations, :mappers, :commands)
|
102
|
+
|
103
|
+
# @api private
|
104
|
+
def self.new(configuration)
|
105
|
+
super().tap do |container|
|
106
|
+
container.register(:configuration, memoize: true) do
|
107
|
+
Setup::Configuration.new(
|
108
|
+
configuration: configuration, container: container
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return runtime configuration with component registries
|
115
|
+
#
|
116
|
+
# @return [Setup::Configuration]
|
117
|
+
#
|
118
|
+
# @api public
|
119
|
+
def configuration
|
120
|
+
self[:configuration]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Return registered gateways
|
124
|
+
#
|
125
|
+
# @return [Hash<Symbol=>Gateway>]
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
def gateways
|
129
|
+
configuration.gateways
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return relation registry
|
133
|
+
#
|
134
|
+
# @return [RelationRegistry]
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def schemas
|
138
|
+
configuration.schemas
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return relation registry
|
142
|
+
#
|
143
|
+
# @return [RelationRegistry]
|
144
|
+
#
|
145
|
+
# @api public
|
146
|
+
def relations
|
147
|
+
configuration.relations
|
148
|
+
end
|
149
|
+
|
150
|
+
# Return mapper registry for all relations
|
151
|
+
#
|
152
|
+
# @return [Hash<Symbol=>MapperRegistry]
|
153
|
+
#
|
154
|
+
# @api public
|
155
|
+
def mappers
|
156
|
+
configuration.mappers
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return command registry
|
160
|
+
#
|
161
|
+
# @return [Hash<Symbol=>CommandRegistry]
|
162
|
+
#
|
163
|
+
# @api public
|
164
|
+
def commands
|
165
|
+
configuration.commands
|
166
|
+
end
|
167
|
+
|
168
|
+
# Disconnect all gateways
|
169
|
+
#
|
170
|
+
# @example
|
171
|
+
# rom = ROM.setup(:sql, 'sqlite://my_db.sqlite')
|
172
|
+
# rom.relations[:users].insert(name: "Jane")
|
173
|
+
# rom.disconnect
|
174
|
+
#
|
175
|
+
# @return [Hash<Symbol=>Gateway>] a hash with disconnected gateways
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def disconnect
|
179
|
+
gateways.each_value(&:disconnect)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/lib/rom/core.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Global interface
|
4
|
+
require_relative "global"
|
5
|
+
|
6
|
+
# Global default settings
|
7
|
+
require_relative "settings"
|
8
|
+
|
9
|
+
# Core components
|
10
|
+
require_relative "components/gateway"
|
11
|
+
require_relative "components/dataset"
|
12
|
+
require_relative "components/schema"
|
13
|
+
require_relative "components/relation"
|
14
|
+
require_relative "components/view"
|
15
|
+
require_relative "components/association"
|
16
|
+
require_relative "components/command"
|
17
|
+
require_relative "components/mapper"
|
18
|
+
|
19
|
+
# Core plugins
|
20
|
+
require_relative "plugins"
|
21
|
+
|
22
|
+
# Set up ROM
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def ROM(*args, &block)
|
26
|
+
if block
|
27
|
+
ROM.setup(*args, &block)
|
28
|
+
else
|
29
|
+
ROM::Setup.new(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Global ROM interface for core setup
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
module ROM
|
37
|
+
extend Global
|
38
|
+
|
39
|
+
module_function
|
40
|
+
|
41
|
+
# Global component setup
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# ROM.components do
|
45
|
+
# register :cache, handler: MyApp::MyCacheHandler
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def components(&block)
|
50
|
+
if defined?(@_components)
|
51
|
+
@_components.instance_eval(&block) if block
|
52
|
+
@_components
|
53
|
+
else
|
54
|
+
require_relative "components"
|
55
|
+
@_components = Components
|
56
|
+
components(&block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Global plugin setup
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# ROM.plugins do
|
64
|
+
# register :publisher, Plugin::Publisher, type: :command
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def plugins(*args, &block)
|
69
|
+
if defined?(@_plugins)
|
70
|
+
@_plugins.dsl(*args, &block) if block
|
71
|
+
@_plugins
|
72
|
+
else
|
73
|
+
require_relative "plugins"
|
74
|
+
@_plugins = Plugins
|
75
|
+
plugins(*args, &block)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Register core component handlers
|
80
|
+
components do
|
81
|
+
register :gateway, Components::Gateway
|
82
|
+
register :dataset, Components::Dataset
|
83
|
+
register :schema, Components::Schema
|
84
|
+
register :relation, Components::Relation
|
85
|
+
register :view, Components::View
|
86
|
+
register :association, Components::Association
|
87
|
+
register :command, Components::Command
|
88
|
+
register :mapper, Components::Mapper
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO: this will be automated eventually
|
92
|
+
require_relative "relation"
|
93
|
+
require_relative "schema"
|
94
|
+
require_relative "command"
|
95
|
+
require_relative "mapper"
|
96
|
+
require_relative "transformer"
|
97
|
+
|
98
|
+
configs = {
|
99
|
+
schema: [ROM::Schema],
|
100
|
+
relation: [ROM::Relation],
|
101
|
+
command: [ROM::Command],
|
102
|
+
mapper: [ROM::Mapper, ROM::Transformer]
|
103
|
+
}
|
104
|
+
|
105
|
+
configs.each do |key, items|
|
106
|
+
items.each do |constant|
|
107
|
+
constant.config.component.inherit!(config.component)
|
108
|
+
config[key].inherit!(constant.config.component)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
configs.values.flatten(1).each(&:configure).each(&:finalize!)
|
113
|
+
|
114
|
+
# Register core plugins
|
115
|
+
plugins do
|
116
|
+
register :timestamps, ROM::Plugins::Schema::Timestamps, type: :schema
|
117
|
+
register :registry_reader, ROM::Plugins::Relation::RegistryReader, type: :relation
|
118
|
+
register :instrumentation, ROM::Plugins::Relation::Instrumentation, type: :relation
|
119
|
+
register :changeset, ROM::Plugins::Relation::Changeset, type: :relation
|
120
|
+
register :schema, ROM::Plugins::Command::Schema, type: :command
|
121
|
+
register :timestamps, ROM::Plugins::Command::Timestamps, type: :command
|
122
|
+
end
|
123
|
+
|
124
|
+
finalize!
|
125
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
# Helper module for dataset classes
|
5
|
+
#
|
6
|
+
# It provides a constructor accepting data, header and an optional row_proc.
|
7
|
+
# This module is used internally by EnumerableDataset and ArrayDataset.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
module DataProxy
|
11
|
+
NON_FORWARDABLE = %i[
|
12
|
+
each to_a to_ary kind_of? instance_of? is_a?
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
# Wrapped data array
|
16
|
+
#
|
17
|
+
# @return [Object] Data object for the iterator
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
attr_reader :data
|
21
|
+
|
22
|
+
# @return [Proc] tuple processing proc
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
attr_reader :row_proc
|
26
|
+
|
27
|
+
# Extends the class with `forward` DSL and Equalizer using `data` attribute
|
28
|
+
#
|
29
|
+
# @see ClassMethods#forward
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def self.included(klass)
|
33
|
+
klass.class_eval do
|
34
|
+
extend ClassMethods
|
35
|
+
|
36
|
+
include Dry::Equalizer(:data)
|
37
|
+
|
38
|
+
option :row_proc, default: -> { self.class.row_proc }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Iterate over data using row_proc
|
43
|
+
#
|
44
|
+
# @return [Enumerator] if block is not given
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def each
|
48
|
+
return to_enum unless block_given?
|
49
|
+
|
50
|
+
data.each { |tuple| yield(row_proc[tuple]) }
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
# Default no-op tuple proc
|
55
|
+
#
|
56
|
+
# @return [Proc]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def row_proc
|
60
|
+
-> tuple { tuple }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Forward provided methods to the underlaying data object
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
#
|
67
|
+
# class MyDataset
|
68
|
+
# include DataProxy
|
69
|
+
#
|
70
|
+
# forward(:find_all, :map)
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# @return [undefined]
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
def forward(*methods)
|
77
|
+
# FIXME: we should probably raise if one of the non-forwardable methods
|
78
|
+
# was provided
|
79
|
+
(methods - NON_FORWARDABLE).each do |method_name|
|
80
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
81
|
+
def #{method_name}(*args, &block)
|
82
|
+
response = data.public_send(#{method_name.inspect}, *args, &block)
|
83
|
+
|
84
|
+
if response.equal?(data)
|
85
|
+
self
|
86
|
+
elsif response.is_a?(data.class)
|
87
|
+
self.class.new(response)
|
88
|
+
else
|
89
|
+
response
|
90
|
+
end
|
91
|
+
end
|
92
|
+
RUBY
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/initializer"
|
4
|
+
require "rom/data_proxy"
|
5
|
+
|
6
|
+
module ROM
|
7
|
+
# A helper module that adds data-proxy behavior to an enumerable object
|
8
|
+
#
|
9
|
+
# This module is intended to be used by gateways
|
10
|
+
#
|
11
|
+
# Class that includes this module can define `row_proc` class method which
|
12
|
+
# must return a proc-like object which will be used to process each element
|
13
|
+
# in the enumerable
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# class MyDataset
|
17
|
+
# include ROM::EnumerableDataset
|
18
|
+
#
|
19
|
+
# def self.row_proc
|
20
|
+
# -> tuple { tuple.each_with_object({}) { |(k,v), h| h[k.to_sym] = v } }
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# ds = MyDataset.new([{ 'name' => 'Jane' }, [:name])
|
25
|
+
# ds.to_a # => { :name => 'Jane' }
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
module EnumerableDataset
|
29
|
+
extend DataProxy::ClassMethods
|
30
|
+
include Enumerable
|
31
|
+
|
32
|
+
# Coerce a dataset to an array
|
33
|
+
#
|
34
|
+
# @return [Array]
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
alias_method :to_ary, :to_a
|
38
|
+
|
39
|
+
# Included hook which extends a class with DataProxy behavior
|
40
|
+
#
|
41
|
+
# This module can also be included into other modules so we apply the
|
42
|
+
# extension only for classes
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def self.included(klass)
|
46
|
+
return unless klass.is_a?(Class)
|
47
|
+
|
48
|
+
klass.class_eval do
|
49
|
+
extend Initializer
|
50
|
+
include DataProxy
|
51
|
+
|
52
|
+
param :data
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
forward :take
|
57
|
+
|
58
|
+
%i[
|
59
|
+
chunk collect collect_concat drop_while find_all flat_map
|
60
|
+
grep map reject select sort sort_by take_while
|
61
|
+
].each do |method|
|
62
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
63
|
+
def #{method}(*args, &block)
|
64
|
+
return to_enum unless block
|
65
|
+
self.class.new(super(*args, &block), **options)
|
66
|
+
end
|
67
|
+
RUBY
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/rom/gateway.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/class_attributes"
|
4
|
+
|
5
|
+
require_relative "support/notifications"
|
6
|
+
require_relative "transaction"
|
7
|
+
require_relative "components/provider"
|
8
|
+
|
9
|
+
module ROM
|
10
|
+
# Abstract gateway class
|
11
|
+
#
|
12
|
+
# Every adapter needs to inherit from this class and implement
|
13
|
+
# required interface
|
14
|
+
#
|
15
|
+
# @abstract
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
class Gateway
|
19
|
+
extend Dry::Core::ClassAttributes
|
20
|
+
extend Notifications::Listener
|
21
|
+
|
22
|
+
extend ROM::Provider(:plugin, type: :gateway)
|
23
|
+
|
24
|
+
# @!method self.adapter
|
25
|
+
# Get or set gateway's adapter identifier
|
26
|
+
#
|
27
|
+
# @overload adapter
|
28
|
+
# Return adapter identifier
|
29
|
+
# @return [Symbol]
|
30
|
+
#
|
31
|
+
# @overload gateway(adapter)
|
32
|
+
# @example
|
33
|
+
# class MyGateway < ROM::Gateway
|
34
|
+
# config.component.adapter = :my_adapter
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @param [Symbol] adapter The adapter identifier
|
38
|
+
defines :adapter
|
39
|
+
|
40
|
+
# @!attribute [r] config
|
41
|
+
# @return [Configurable::Config] Gateway's configuration identifier
|
42
|
+
attr_reader :config
|
43
|
+
|
44
|
+
# @!attribute [r] connection
|
45
|
+
# @return [Object] The gateway's connection object (type varies across adapters)
|
46
|
+
attr_reader :connection
|
47
|
+
|
48
|
+
# Set up a gateway
|
49
|
+
#
|
50
|
+
# @overload setup(type, *args)
|
51
|
+
# Sets up a single-gateway given a gateway type.
|
52
|
+
# For custom gateways, create an instance and pass it directly.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# module SuperDB
|
56
|
+
# class Gateway < ROM::Gateway
|
57
|
+
# def initialize(options)
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# ROM.register_adapter(:super_db, SuperDB)
|
63
|
+
#
|
64
|
+
# Gateway.setup(:super_db, some: 'options')
|
65
|
+
# # SuperDB::Gateway.new(some: 'options') is called
|
66
|
+
#
|
67
|
+
# @param [Symbol] type Registered gateway identifier
|
68
|
+
# @param [Array] args Additional gateway options
|
69
|
+
#
|
70
|
+
# @overload setup(gateway)
|
71
|
+
# Set up a gateway instance
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# module SuperDB
|
75
|
+
# class Gateway < ROM::Gateway
|
76
|
+
# def initialize(options)
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# ROM.register_adapter(:super_db, SuperDB)
|
82
|
+
#
|
83
|
+
# Gateway.setup(SuperDB::Gateway.new(some: 'options'))
|
84
|
+
#
|
85
|
+
# @param [Gateway] gateway
|
86
|
+
#
|
87
|
+
# @return [Gateway] a specific gateway subclass
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
#
|
91
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
92
|
+
def self.setup(gateway_or_scheme, *args)
|
93
|
+
case gateway_or_scheme
|
94
|
+
when Gateway
|
95
|
+
unless args.empty?
|
96
|
+
raise ArgumentError, "Can't accept arguments when passing an instance"
|
97
|
+
end
|
98
|
+
|
99
|
+
gateway_or_scheme
|
100
|
+
when String
|
101
|
+
raise ArgumentError, <<-STRING.gsub(/^ {10}/, "")
|
102
|
+
URIs without an explicit scheme are not supported anymore.
|
103
|
+
See https://github.com/rom-rb/rom/blob/main/CHANGELOG.md
|
104
|
+
STRING
|
105
|
+
when Symbol
|
106
|
+
klass = class_from_symbol(gateway_or_scheme)
|
107
|
+
|
108
|
+
if klass.instance_method(:initialize).arity.zero?
|
109
|
+
klass.new
|
110
|
+
elsif args.size.equal?(1) && args.first.respond_to?(:args)
|
111
|
+
if args.first.respond_to?(:args)
|
112
|
+
setup(gateway_or_scheme, *args.first.args)
|
113
|
+
else
|
114
|
+
klass.new(**config)
|
115
|
+
end
|
116
|
+
elsif args.last.is_a?(Hash)
|
117
|
+
klass.new(*args[0..-2], **args.last)
|
118
|
+
else
|
119
|
+
klass.new(*args)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
gateway_or_scheme
|
123
|
+
end
|
124
|
+
end
|
125
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
|
126
|
+
|
127
|
+
class << self
|
128
|
+
ruby2_keywords(:setup) if respond_to?(:ruby2_keywords, true)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get gateway subclass for a specific adapter
|
132
|
+
#
|
133
|
+
# @param [Symbol] type Adapter identifier
|
134
|
+
#
|
135
|
+
# @return [Class]
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
def self.class_from_symbol(type)
|
139
|
+
adapter = ROM.adapters.fetch(type) do
|
140
|
+
begin
|
141
|
+
require "rom/#{type}"
|
142
|
+
rescue LoadError
|
143
|
+
raise AdapterLoadError, "Failed to load adapter rom/#{type}"
|
144
|
+
end
|
145
|
+
|
146
|
+
ROM.adapters.fetch(type)
|
147
|
+
end
|
148
|
+
|
149
|
+
adapter.const_get(:Gateway)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Configured gateway name used in the registry
|
153
|
+
#
|
154
|
+
# @return [Symbol]
|
155
|
+
#
|
156
|
+
# @api public
|
157
|
+
def name
|
158
|
+
config.id
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the adapter, defined for the class
|
162
|
+
#
|
163
|
+
# @return [Symbol]
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def adapter
|
167
|
+
self.class.adapter || raise(
|
168
|
+
MissingAdapterIdentifierError,
|
169
|
+
"gateway class +#{self}+ is missing the adapter identifier"
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
# A generic interface for setting up a logger
|
174
|
+
#
|
175
|
+
# This is not a required interface, it's a no-op by default
|
176
|
+
#
|
177
|
+
# @abstract
|
178
|
+
#
|
179
|
+
# @api public
|
180
|
+
def use_logger(*)
|
181
|
+
# noop
|
182
|
+
end
|
183
|
+
|
184
|
+
# A generic interface for returning default logger
|
185
|
+
#
|
186
|
+
# Adapters should implement this method as handling loggers is different
|
187
|
+
# across adapters. This is a no-op by default and returns nil.
|
188
|
+
#
|
189
|
+
# @return [NilClass]
|
190
|
+
#
|
191
|
+
# @api public
|
192
|
+
def logger
|
193
|
+
# noop
|
194
|
+
end
|
195
|
+
|
196
|
+
# Disconnect is optional and it's a no-op by default
|
197
|
+
#
|
198
|
+
# @api public
|
199
|
+
def disconnect
|
200
|
+
# noop
|
201
|
+
end
|
202
|
+
|
203
|
+
# Runs a block inside a transaction. The underlying transaction engine
|
204
|
+
# is adapter-specific
|
205
|
+
#
|
206
|
+
# @param [Hash] opts Transaction options
|
207
|
+
#
|
208
|
+
# @return The result of yielding the block or +nil+ if
|
209
|
+
# the transaction was rolled back
|
210
|
+
#
|
211
|
+
# @api public
|
212
|
+
def transaction(**opts, &block)
|
213
|
+
transaction_runner(**opts).run(**opts, &block)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Build a command instance
|
217
|
+
#
|
218
|
+
# @return [Command]
|
219
|
+
#
|
220
|
+
# @api public
|
221
|
+
def command(klass, relation:, **opts)
|
222
|
+
klass.build(relation, **opts)
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
# @api private
|
228
|
+
def transaction_runner(**)
|
229
|
+
Transaction::NoOp
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|