Skip to content

Commit 1a788f0

Browse files
committed
Improve and simplify gateway config and setup
This makes gateway setup more inline with how components are handled. It'll make it easier to extend configuration with support for global defaults and simplify handling of gateways at runtime, because they now have a name under which they are registered + all config and opts associated with a gateway instance are stored within the gateway registry which makes it easily accessible. It didn't make sense to improve Gateway abstract class because making it backward compatible would be too much work and breaking API would be even more work, considering all the adapters that would have to be updated. In summary: - Simplify gateway handling - Simplify configuring gateways - Add Gateway#name - Move config stuff from gateways environment to Configuration - Stop memoizing plugins in components - Turn Environment into a standard registry with gateways - Move Configuration#relation_classes to rom/compat
1 parent d8587d0 commit 1a788f0

File tree

16 files changed

+330
-199
lines changed

16 files changed

+330
-199
lines changed

lib/rom/compat.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,24 @@
55
module ROM
66
class Configuration
77
def_delegators :@setup, :auto_registration
8+
def_delegators :@gateways, :gateways_map
9+
10+
alias_method :environment, :gateways
11+
12+
# @api private
13+
def relation_classes(gateway = nil)
14+
classes = setup.components.relations.map(&:constant)
15+
16+
return classes unless gateway
17+
18+
gw_name = gateway.is_a?(Symbol) ? gateway : gateways_map[gateway]
19+
classes.select { |rel| rel.gateway == gw_name }
20+
end
21+
22+
# @api private
23+
# @deprecated
24+
def gateways_map
25+
@gateways_map ||= gateways.to_a.map(&:reverse).to_h
26+
end
827
end
928
end

lib/rom/components/core.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ def apply_plugins
8080
end
8181

8282
# @api public
83-
memoize def plugins
83+
def plugins
8484
configuration.plugins.select { |plugin| plugin.type == self.class.id }
8585
end
8686

8787
# @api public
88-
memoize def plugin_options
88+
def plugin_options
8989
plugins.map(&:config).map(&:to_hash).reduce(:merge) || EMPTY_HASH
9090
end
9191

lib/rom/configuration.rb

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
require "forwardable"
44

55
require "rom/support/notifications"
6-
require "rom/environment"
76
require "rom/setup"
87
require "rom/configuration_dsl"
8+
require "rom/support/configurable"
99
require "rom/support/inflector"
10+
require "rom/gateway_registry"
1011
require "rom/relation_registry"
1112

1213
module ROM
@@ -23,12 +24,13 @@ class Configuration
2324
register_event("configuration.commands.class.before_build")
2425

2526
include ROM::ConfigurationDSL
27+
include Configurable
2628

2729
NoDefaultAdapterError = Class.new(StandardError)
2830

29-
# @!attribute [r] environment
30-
# @return [Environment] Environment object with gateways
31-
attr_reader :environment
31+
# @!attribute [r] gateways
32+
# @return [GatewayRegistry] Configured runtime gateways
33+
attr_reader :gateways
3234

3335
# @!attribute [r] setup
3436
# @return [Setup] Setup object which collects component classes and plugins
@@ -50,24 +52,49 @@ class Configuration
5052
:register_mapper, :register_plugin, :auto_register,
5153
:inflector, :inflector=, :components, :plugins
5254

53-
def_delegators :@environment, :gateways, :gateways_map, :configure, :config
54-
5555
# Initialize a new configuration
5656
#
57-
# @see Environment#initialize
58-
#
5957
# @return [Configuration]
6058
#
6159
# @api private
6260
def initialize(*args, &block)
63-
@environment = Environment.new(*args)
64-
@setup = Setup.new
6561
@notifications = Notifications.event_bus(:configuration)
62+
63+
@setup = Setup.new
6664
@cache = Cache.new
6765

66+
config.gateways = Config.new
67+
@gateways = GatewayRegistry.new({}, cache: cache, config: config.gateways)
68+
69+
configure(*args, &block)
70+
6871
@relations = RelationRegistry.build
72+
end
73+
74+
# @api public
75+
def configure(*args)
76+
unless args.empty?
77+
gateways_config = args.first.is_a?(Hash) ? args.first : {default: args}
78+
79+
gateways_config.each do |name, value|
80+
args = Array(value)
81+
82+
adapter, *rest = args
83+
84+
if rest.size > 1 && rest.last.is_a?(Hash)
85+
load_config(config.gateways[name], {adapter: adapter, args: rest[0..-1], **rest.last})
86+
else
87+
options = rest.first.is_a?(Hash) ? rest.first : {args: rest.flatten(1)}
88+
load_config(config.gateways[name], {adapter: adapter, **options})
89+
end
90+
end
91+
end
92+
93+
load_gateways
94+
95+
yield(self) if block_given?
6996

70-
block&.call(self)
97+
self
7198
end
7299

73100
# @api private
@@ -76,17 +103,6 @@ def finalize
76103
self
77104
end
78105

79-
# @api private
80-
def command_compiler
81-
@command_compiler ||= CommandCompiler.new(
82-
gateways,
83-
relations,
84-
Registry.new,
85-
notifications,
86-
inflector: inflector
87-
)
88-
end
89-
90106
# Apply a plugin to the configuration
91107
#
92108
# @param [Mixed] plugin The plugin identifier, usually a Symbol
@@ -96,10 +112,9 @@ def command_compiler
96112
#
97113
# @api public
98114
def use(plugin, options = {})
99-
if plugin.is_a?(Array)
100-
plugin.each { |p| use(p) }
101-
elsif plugin.is_a?(Hash)
102-
plugin.to_a.each { |p| use(*p) }
115+
case plugin
116+
when Array then plugin.each { |p| use(p) }
117+
when Hash then plugin.to_a.each { |p| use(*p) }
103118
else
104119
ROM.plugin_registry[:configuration].fetch(plugin).apply_to(self, options)
105120
end
@@ -116,16 +131,14 @@ def [](name)
116131
gateways.fetch(name)
117132
end
118133

119-
# Hook for respond_to? used internally
120-
#
121134
# @api private
122-
def respond_to?(name, include_all = false)
123-
gateways.key?(name) || super
135+
def default_gateway
136+
@default_gateway ||= gateways[:default] if gateways.key?(:default)
124137
end
125138

126139
# @api private
127-
def default_gateway
128-
@default_gateway ||= gateways[:default]
140+
def default_adapter
141+
@default_adapter ||= adapter_for_gateway(default_gateway) || ROM.adapters.keys.first
129142
end
130143

131144
# @api private
@@ -136,22 +149,51 @@ def adapter_for_gateway(gateway)
136149
end
137150

138151
# @api private
139-
def relation_classes(gateway = nil)
140-
classes = setup.components.relations.map(&:constant)
141-
142-
return classes unless gateway
143-
144-
gw_name = gateway.is_a?(Symbol) ? gateway : gateways_map[gateway]
145-
classes.select { |rel| rel.gateway == gw_name }
152+
def command_compiler
153+
@command_compiler ||= CommandCompiler.new(
154+
gateways,
155+
relations,
156+
Registry.new,
157+
notifications,
158+
inflector: inflector
159+
)
146160
end
147161

148162
# @api private
149-
def default_adapter
150-
@default_adapter ||= adapter_for_gateway(default_gateway) || ROM.adapters.keys.first
163+
def respond_to_missing?(name, include_all = false)
164+
gateways.key?(name) || super
151165
end
152166

153167
private
154168

169+
# @api private
170+
def load_gateways
171+
config.gateways.each do |name, gateway_config|
172+
gateway =
173+
if gateway_config.adapter.is_a?(Gateway)
174+
gateway_config.adapter
175+
else
176+
Gateway.setup(gateway_config.adapter, gateway_config)
177+
end
178+
179+
# TODO: this is here to keep backward compatibility
180+
gateway.instance_variable_set(:"@name", name)
181+
182+
gateways.add(name, gateway)
183+
end
184+
end
185+
186+
# @api private
187+
def load_config(config, hash)
188+
hash.each do |key, value|
189+
if value.is_a?(Hash)
190+
load_config(config[key], value)
191+
else
192+
config.send("#{key}=", value)
193+
end
194+
end
195+
end
196+
155197
# Returns gateway if method is a name of a registered gateway
156198
#
157199
# @return [Gateway]

lib/rom/constants.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def initialize(adapter, component)
2020
end
2121

2222
EnvAlreadyFinalizedError = Class.new(StandardError)
23+
GatewayAlreadyDefinedError = Class.new(StandardError)
2324
RelationAlreadyDefinedError = Class.new(StandardError)
2425
CommandAlreadyDefinedError = Class.new(StandardError)
2526
MapperAlreadyDefinedError = Class.new(StandardError)

lib/rom/environment.rb

Lines changed: 0 additions & 75 deletions
This file was deleted.

lib/rom/gateway.rb

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class Gateway
3434
# @param [Symbol] adapter The adapter identifier
3535
defines :adapter
3636

37+
# @!attribute [r] name
38+
# @return [Object] Gateway's registry identifier
39+
attr_reader :name
40+
3741
# @!attribute [r] connection
3842
# @return [Object] The gateway's connection object (type varies across adapters)
3943
attr_reader :connection
@@ -81,25 +85,42 @@ class Gateway
8185
#
8286
# @api public
8387
def self.setup(gateway_or_scheme, *args)
84-
case gateway_or_scheme
85-
when String
86-
raise ArgumentError, <<-STRING.gsub(/^ {10}/, "")
87-
URIs without an explicit scheme are not supported anymore.
88-
See https://github.com/rom-rb/rom/blob/master/CHANGELOG.md
89-
STRING
90-
when Symbol
91-
klass = class_from_symbol(gateway_or_scheme)
92-
93-
if klass.instance_method(:initialize).arity.zero?
94-
klass.new
88+
gateway =
89+
case gateway_or_scheme
90+
when Gateway
91+
unless args.empty?
92+
raise ArgumentError, "Can't accept arguments when passing an instance"
93+
end
94+
95+
gateway_or_scheme
96+
when String
97+
raise ArgumentError, <<-STRING.gsub(/^ {10}/, "")
98+
URIs without an explicit scheme are not supported anymore.
99+
See https://github.com/rom-rb/rom/blob/master/CHANGELOG.md
100+
STRING
101+
when Symbol
102+
klass = class_from_symbol(gateway_or_scheme)
103+
104+
if klass.instance_method(:initialize).arity.zero?
105+
klass.new
106+
else
107+
if args.size.equal?(1) && args.first.is_a?(Configurable::Config)
108+
if args.first.respond_to?(:args)
109+
setup(gateway_or_scheme, *args.first.args)
110+
else
111+
klass.new(**config)
112+
end
113+
elsif args.size.equal?(2) && args.last.is_a?(Hash)
114+
klass.new(*args[0..-1], **args.last)
115+
else
116+
klass.new(*args)
117+
end
118+
end
95119
else
96-
klass.new(*args)
120+
gateway_or_scheme
97121
end
98-
else
99-
raise ArgumentError, "Can't accept arguments when passing an instance" unless args.empty?
100122

101-
gateway_or_scheme
102-
end
123+
gateway
103124
end
104125

105126
class << self

0 commit comments

Comments
 (0)