rom 5.4.1 → 6.0.0.alpha1
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 +4 -4
- data/CHANGELOG.md +58 -65
- data/LICENSE +1 -1
- data/README.md +7 -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 +302 -23
@@ -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
|