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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +603 -0
  3. data/LICENSE +20 -0
  4. data/README.md +18 -0
  5. data/lib/rom-core.rb +1 -0
  6. data/lib/rom/array_dataset.rb +44 -0
  7. data/lib/rom/association_set.rb +16 -0
  8. data/lib/rom/associations/abstract.rb +135 -0
  9. data/lib/rom/associations/definitions.rb +5 -0
  10. data/lib/rom/associations/definitions/abstract.rb +116 -0
  11. data/lib/rom/associations/definitions/many_to_many.rb +24 -0
  12. data/lib/rom/associations/definitions/many_to_one.rb +11 -0
  13. data/lib/rom/associations/definitions/one_to_many.rb +11 -0
  14. data/lib/rom/associations/definitions/one_to_one.rb +11 -0
  15. data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
  16. data/lib/rom/associations/many_to_many.rb +81 -0
  17. data/lib/rom/associations/many_to_one.rb +37 -0
  18. data/lib/rom/associations/one_to_many.rb +37 -0
  19. data/lib/rom/associations/one_to_one.rb +8 -0
  20. data/lib/rom/associations/one_to_one_through.rb +8 -0
  21. data/lib/rom/associations/through_identifier.rb +39 -0
  22. data/lib/rom/auto_curry.rb +55 -0
  23. data/lib/rom/cache.rb +46 -0
  24. data/lib/rom/command.rb +488 -0
  25. data/lib/rom/command_compiler.rb +239 -0
  26. data/lib/rom/command_proxy.rb +24 -0
  27. data/lib/rom/command_registry.rb +141 -0
  28. data/lib/rom/commands.rb +3 -0
  29. data/lib/rom/commands/class_interface.rb +270 -0
  30. data/lib/rom/commands/composite.rb +53 -0
  31. data/lib/rom/commands/create.rb +13 -0
  32. data/lib/rom/commands/delete.rb +14 -0
  33. data/lib/rom/commands/graph.rb +88 -0
  34. data/lib/rom/commands/graph/class_interface.rb +62 -0
  35. data/lib/rom/commands/graph/input_evaluator.rb +62 -0
  36. data/lib/rom/commands/lazy.rb +99 -0
  37. data/lib/rom/commands/lazy/create.rb +23 -0
  38. data/lib/rom/commands/lazy/delete.rb +27 -0
  39. data/lib/rom/commands/lazy/update.rb +34 -0
  40. data/lib/rom/commands/result.rb +96 -0
  41. data/lib/rom/commands/update.rb +14 -0
  42. data/lib/rom/configuration.rb +114 -0
  43. data/lib/rom/configuration_dsl.rb +87 -0
  44. data/lib/rom/configuration_dsl/command.rb +41 -0
  45. data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
  46. data/lib/rom/configuration_dsl/relation.rb +26 -0
  47. data/lib/rom/configuration_plugin.rb +17 -0
  48. data/lib/rom/constants.rb +64 -0
  49. data/lib/rom/container.rb +147 -0
  50. data/lib/rom/core.rb +46 -0
  51. data/lib/rom/create_container.rb +60 -0
  52. data/lib/rom/data_proxy.rb +94 -0
  53. data/lib/rom/enumerable_dataset.rb +68 -0
  54. data/lib/rom/environment.rb +70 -0
  55. data/lib/rom/gateway.rb +184 -0
  56. data/lib/rom/global.rb +58 -0
  57. data/lib/rom/global/plugin_dsl.rb +47 -0
  58. data/lib/rom/initializer.rb +64 -0
  59. data/lib/rom/lint/enumerable_dataset.rb +54 -0
  60. data/lib/rom/lint/gateway.rb +120 -0
  61. data/lib/rom/lint/linter.rb +78 -0
  62. data/lib/rom/lint/spec.rb +20 -0
  63. data/lib/rom/lint/test.rb +98 -0
  64. data/lib/rom/mapper_registry.rb +24 -0
  65. data/lib/rom/memory.rb +4 -0
  66. data/lib/rom/memory/associations.rb +4 -0
  67. data/lib/rom/memory/associations/many_to_many.rb +10 -0
  68. data/lib/rom/memory/associations/many_to_one.rb +10 -0
  69. data/lib/rom/memory/associations/one_to_many.rb +10 -0
  70. data/lib/rom/memory/associations/one_to_one.rb +10 -0
  71. data/lib/rom/memory/commands.rb +56 -0
  72. data/lib/rom/memory/dataset.rb +97 -0
  73. data/lib/rom/memory/gateway.rb +64 -0
  74. data/lib/rom/memory/relation.rb +62 -0
  75. data/lib/rom/memory/schema.rb +23 -0
  76. data/lib/rom/memory/storage.rb +59 -0
  77. data/lib/rom/memory/types.rb +9 -0
  78. data/lib/rom/pipeline.rb +105 -0
  79. data/lib/rom/plugin.rb +25 -0
  80. data/lib/rom/plugin_base.rb +45 -0
  81. data/lib/rom/plugin_registry.rb +197 -0
  82. data/lib/rom/plugins/command/schema.rb +37 -0
  83. data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
  84. data/lib/rom/plugins/relation/instrumentation.rb +51 -0
  85. data/lib/rom/plugins/relation/registry_reader.rb +44 -0
  86. data/lib/rom/plugins/schema/timestamps.rb +58 -0
  87. data/lib/rom/registry.rb +71 -0
  88. data/lib/rom/relation.rb +548 -0
  89. data/lib/rom/relation/class_interface.rb +282 -0
  90. data/lib/rom/relation/commands.rb +23 -0
  91. data/lib/rom/relation/composite.rb +46 -0
  92. data/lib/rom/relation/curried.rb +103 -0
  93. data/lib/rom/relation/graph.rb +197 -0
  94. data/lib/rom/relation/loaded.rb +127 -0
  95. data/lib/rom/relation/materializable.rb +66 -0
  96. data/lib/rom/relation/name.rb +111 -0
  97. data/lib/rom/relation/view_dsl.rb +64 -0
  98. data/lib/rom/relation/wrap.rb +83 -0
  99. data/lib/rom/relation_registry.rb +10 -0
  100. data/lib/rom/schema.rb +437 -0
  101. data/lib/rom/schema/associations_dsl.rb +195 -0
  102. data/lib/rom/schema/attribute.rb +419 -0
  103. data/lib/rom/schema/dsl.rb +164 -0
  104. data/lib/rom/schema/inferrer.rb +66 -0
  105. data/lib/rom/schema_plugin.rb +27 -0
  106. data/lib/rom/setup.rb +68 -0
  107. data/lib/rom/setup/auto_registration.rb +74 -0
  108. data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
  109. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
  110. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
  111. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
  112. data/lib/rom/setup/finalize.rb +103 -0
  113. data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
  114. data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
  115. data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
  116. data/lib/rom/support/configurable.rb +85 -0
  117. data/lib/rom/support/memoizable.rb +58 -0
  118. data/lib/rom/support/notifications.rb +103 -0
  119. data/lib/rom/transaction.rb +24 -0
  120. data/lib/rom/types.rb +26 -0
  121. data/lib/rom/version.rb +5 -0
  122. metadata +289 -0
@@ -0,0 +1,70 @@
1
+ require 'rom/support/configurable'
2
+ require 'rom/gateway'
3
+
4
+ module ROM
5
+ # Core gateway configuration interface
6
+ #
7
+ # @api public
8
+ class Environment
9
+ include Configurable
10
+
11
+ attr_reader :gateways, :gateways_map
12
+
13
+ # @api public
14
+ def initialize(*args)
15
+ @gateways = {}
16
+ @gateways_map = {}
17
+
18
+ configure_gateways(*args) unless args.empty?
19
+ end
20
+
21
+ private
22
+
23
+ def configure_gateways(*args)
24
+ normalized_gateway_args = normalize_gateway_args(*args)
25
+ normalized_gateways = normalize_gateways(normalized_gateway_args)
26
+
27
+ @gateways, @gateways_map = normalized_gateways.values_at(:gateways, :map)
28
+
29
+ normalized_gateway_args.each_with_object(config) do |(name, gateway_config), config|
30
+ options = gateway_config.is_a?(Array) && gateway_config.last
31
+ load_config(config.gateways[name], options) if options.is_a?(Hash)
32
+ end
33
+ end
34
+
35
+
36
+ # @api private
37
+ def normalize_gateway_args(*args)
38
+ args.first.is_a?(Hash) ? args.first : { default: args }
39
+ end
40
+
41
+ # Build gateways using the setup interface
42
+ #
43
+ # @api private
44
+ def normalize_gateways(gateways_config)
45
+ gateways_config.each_with_object(map: {}, gateways: {}) do |(name, spec), hash|
46
+ identifier, *args = Array(spec)
47
+
48
+ if identifier.is_a?(Gateway)
49
+ gateway = identifier
50
+ else
51
+ gateway = Gateway.setup(identifier, *args.flatten)
52
+ end
53
+
54
+ hash[:map][gateway] = name
55
+ hash[:gateways][name] = gateway
56
+ end
57
+ end
58
+
59
+ # @api private
60
+ def load_config(config, hash)
61
+ hash.each do |key, value|
62
+ if value.is_a?(Hash)
63
+ load_config(config[key], value)
64
+ else
65
+ config.send("#{key}=", value)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,184 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ require 'rom/transaction'
4
+ require 'rom/support/notifications'
5
+
6
+ module ROM
7
+ # Abstract gateway class
8
+ #
9
+ # Every adapter needs to inherit from this class and implement
10
+ # required interface
11
+ #
12
+ # @abstract
13
+ #
14
+ # @api public
15
+ class Gateway
16
+ extend Dry::Core::ClassAttributes
17
+ extend Notifications::Listener
18
+
19
+ defines :adapter
20
+
21
+ # @!attribute [r] connection
22
+ # @return [Object] The gateway's connection object (type varies across adapters)
23
+ attr_reader :connection
24
+
25
+ # Set up a gateway
26
+ #
27
+ # @overload setup(type, *args)
28
+ # Sets up a single-gateway given a gateway type.
29
+ # For custom gateways, create an instance and pass it directly.
30
+ #
31
+ # @example
32
+ # module SuperDB
33
+ # class Gateway < ROM::Gateway
34
+ # def initialize(options)
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # ROM.register_adapter(:super_db, SuperDB)
40
+ #
41
+ # Gateway.setup(:super_db, some: 'options')
42
+ # # SuperDB::Gateway.new(some: 'options') is called
43
+ #
44
+ # @param [Symbol] type Registered gateway identifier
45
+ # @param [Array] args Additional gateway options
46
+ #
47
+ # @overload setup(gateway)
48
+ # Set up a gateway instance
49
+ #
50
+ # @example
51
+ # module SuperDB
52
+ # class Gateway < ROM::Gateway
53
+ # def initialize(options)
54
+ # end
55
+ # end
56
+ # end
57
+ #
58
+ # ROM.register_adapter(:super_db, SuperDB)
59
+ #
60
+ # Gateway.setup(SuperDB::Gateway.new(some: 'options'))
61
+ #
62
+ # @param [Gateway] gateway
63
+ #
64
+ # @return [Gateway] a specific gateway subclass
65
+ #
66
+ # @api public
67
+ def self.setup(gateway_or_scheme, *args)
68
+ case gateway_or_scheme
69
+ when String
70
+ raise ArgumentError, <<-STRING.gsub(/^ {10}/, '')
71
+ URIs without an explicit scheme are not supported anymore.
72
+ See https://github.com/rom-rb/rom/blob/master/CHANGELOG.md
73
+ STRING
74
+ when Symbol
75
+ klass = class_from_symbol(gateway_or_scheme)
76
+
77
+ if klass.instance_method(:initialize).arity == 0
78
+ klass.new
79
+ else
80
+ klass.new(*args)
81
+ end
82
+ else
83
+ if args.empty?
84
+ gateway_or_scheme
85
+ else
86
+ raise ArgumentError, "Can't accept arguments when passing an instance"
87
+ end
88
+ end
89
+ end
90
+
91
+ # Get gateway subclass for a specific adapter
92
+ #
93
+ # @param [Symbol] type Adapter identifier
94
+ #
95
+ # @return [Class]
96
+ #
97
+ # @api private
98
+ def self.class_from_symbol(type)
99
+ adapter = ROM.adapters.fetch(type) {
100
+ begin
101
+ require "rom/#{type}"
102
+ rescue LoadError
103
+ raise AdapterLoadError, "Failed to load adapter rom/#{type}"
104
+ end
105
+
106
+ ROM.adapters.fetch(type)
107
+ }
108
+
109
+ adapter.const_get(:Gateway)
110
+ end
111
+
112
+ # Returns the adapter, defined for the class
113
+ #
114
+ # @return [Symbol]
115
+ #
116
+ # @api public
117
+ def adapter
118
+ self.class.adapter || raise(
119
+ MissingAdapterIdentifierError,
120
+ "gateway class +#{self}+ is missing the adapter identifier"
121
+ )
122
+ end
123
+
124
+ # A generic interface for setting up a logger
125
+ #
126
+ # This is not a required interface, it's a no-op by default
127
+ #
128
+ # @abstract
129
+ #
130
+ # @api public
131
+ def use_logger(*)
132
+ # noop
133
+ end
134
+
135
+ # A generic interface for returning default logger
136
+ #
137
+ # Adapters should implement this method as handling loggers is different
138
+ # across adapters. This is a no-op by default and returns nil.
139
+ #
140
+ # @return [NilClass]
141
+ #
142
+ # @api public
143
+ def logger
144
+ # noop
145
+ end
146
+
147
+ # Schema inference hook
148
+ #
149
+ # Every gateway that supports schema inference should implement this method
150
+ #
151
+ # @return [Array] An array with dataset names
152
+ #
153
+ # @api private
154
+ def schema
155
+ []
156
+ end
157
+
158
+ # Disconnect is optional and it's a no-op by default
159
+ #
160
+ # @api public
161
+ def disconnect
162
+ # noop
163
+ end
164
+
165
+ # Runs a block inside a transaction. The underlying transaction engine
166
+ # is adapter-specific
167
+ #
168
+ # @param [Hash] Transaction options
169
+ # @return The result of yielding the block or +nil+ if
170
+ # the transaction was rolled back
171
+ #
172
+ # @api public
173
+ def transaction(opts = EMPTY_HASH, &block)
174
+ transaction_runner(opts).run(opts, &block)
175
+ end
176
+
177
+ private
178
+
179
+ # @api private
180
+ def transaction_runner(_)
181
+ Transaction::NoOp
182
+ end
183
+ end
184
+ end
data/lib/rom/global.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'rom/plugin_registry'
2
+ require 'rom/global/plugin_dsl'
3
+
4
+ module ROM
5
+ # Globally accessible public interface exposed via ROM module
6
+ #
7
+ # @api public
8
+ module Global
9
+ # Set base global registries in ROM constant
10
+ #
11
+ # @api private
12
+ def self.extended(rom)
13
+ super
14
+
15
+ rom.instance_variable_set('@adapters', {})
16
+ rom.instance_variable_set('@plugin_registry', PluginRegistry.new)
17
+ end
18
+
19
+ # An internal adapter identifier => adapter module map used by setup
20
+ #
21
+ # @return [Hash<Symbol=>Module>]
22
+ #
23
+ # @api private
24
+ attr_reader :adapters
25
+
26
+ # An internal identifier => plugin map used by the setup
27
+ #
28
+ # @return [Hash]
29
+ #
30
+ # @api private
31
+ attr_reader :plugin_registry
32
+
33
+ # Global plugin setup DSL
34
+ #
35
+ # @example
36
+ # ROM.plugins do
37
+ # register :publisher, Plugin::Publisher, type: :command
38
+ # end
39
+ #
40
+ # @example
41
+ def plugins(*args, &block)
42
+ PluginDSL.new(plugin_registry, *args, &block)
43
+ end
44
+
45
+ # Register adapter namespace under a specified identifier
46
+ #
47
+ # @param [Symbol] identifier
48
+ # @param [Class,Module] adapter
49
+ #
50
+ # @return [self]
51
+ #
52
+ # @api private
53
+ def register_adapter(identifier, adapter)
54
+ adapters[identifier] = adapter
55
+ self
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ module ROM
2
+ module Global
3
+ # plugin registration DSL
4
+ #
5
+ # @private
6
+ class PluginDSL
7
+ # Default options passed to plugin registration
8
+ #
9
+ # @return [Hash]
10
+ #
11
+ # @api private
12
+ attr_reader :defaults
13
+
14
+ # Plugin registry
15
+ #
16
+ # @return [PluginRegistry]
17
+ #
18
+ # @api private
19
+ attr_reader :registry
20
+
21
+ # @api private
22
+ def initialize(registry, defaults = EMPTY_HASH, &block)
23
+ @registry = registry
24
+ @defaults = defaults
25
+ instance_exec(&block)
26
+ end
27
+
28
+ # Register a plugin
29
+ #
30
+ # @param [Symbol] name of the plugin
31
+ # @param [Module] mod to include
32
+ # @param [Hash] options
33
+ #
34
+ # @api public
35
+ def register(name, mod, options = EMPTY_HASH)
36
+ registry.register(name, mod, defaults.merge(options))
37
+ end
38
+
39
+ # Register plugins for a specific adapter
40
+ #
41
+ # @param [Symbol] adapter type
42
+ def adapter(type, &block)
43
+ self.class.new(registry, adapter: type, &block)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ require 'dry-initializer'
2
+
3
+ module ROM
4
+
5
+ # @api private
6
+ module Initializer
7
+ # @api private
8
+ module DefineWithHook
9
+ def param(*)
10
+ super
11
+
12
+ __define_with__
13
+ end
14
+
15
+ def option(*)
16
+ super
17
+
18
+ __define_with__ unless method_defined?(:with)
19
+ end
20
+
21
+ def __define_with__
22
+ seq_names = __initializer_mixin__.
23
+ instance_method(:__initialize__).
24
+ parameters.
25
+ select { |type, _| type == :req }.
26
+ map { |_, name| name }.
27
+ join(', ')
28
+
29
+ seq_names << ', ' unless seq_names.empty?
30
+
31
+ undef_method(:with) if method_defined?(:with)
32
+
33
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
34
+ def with(new_options = EMPTY_HASH)
35
+ if new_options.empty?
36
+ self
37
+ else
38
+ self.class.new(#{ seq_names }options.merge(new_options))
39
+ end
40
+ end
41
+ RUBY
42
+ end
43
+ end
44
+
45
+ # @api private
46
+ def self.extended(base)
47
+ base.extend(Dry::Initializer[undefined: false])
48
+ base.extend(DefineWithHook)
49
+ base.include(InstanceMethods)
50
+ end
51
+
52
+ # @api private
53
+ module InstanceMethods
54
+ # Instance options
55
+ #
56
+ # @return [Hash]
57
+ #
58
+ # @api public
59
+ def options
60
+ @__options__
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,54 @@
1
+ require 'rom/lint/linter'
2
+
3
+ module ROM
4
+ module Lint
5
+ # Ensures that a [ROM::EnumerableDataset] extension correctly yields
6
+ # arrays and tuples
7
+ #
8
+ # @api public
9
+ class EnumerableDataset < ROM::Lint::Linter
10
+ # The linted subject
11
+ #
12
+ # @api public
13
+ attr_reader :dataset
14
+
15
+ # The expected data
16
+ #
17
+ # @api public
18
+ attr_reader :data
19
+
20
+ # Create a linter for EnumerableDataset
21
+ #
22
+ # @param [EnumerableDataset] dataset the linted subject
23
+ # @param [Object] data the expected data
24
+ #
25
+ # @api public
26
+ def initialize(dataset, data)
27
+ @dataset = dataset
28
+ @data = data
29
+ end
30
+
31
+ # Lint: Ensure that +dataset+ yield tuples via +each+
32
+ #
33
+ # @api public
34
+ def lint_each
35
+ result = []
36
+ dataset.each do |tuple|
37
+ result << tuple
38
+ end
39
+ return if result == data
40
+
41
+ complain "#{dataset.class}#each must yield tuples"
42
+ end
43
+
44
+ # Lint: Ensure that +dataset+'s array equals to expected +data+
45
+ #
46
+ # @api public
47
+ def lint_to_a
48
+ return if dataset.to_a == data
49
+
50
+ complain "#{dataset.class}#to_a must cast dataset to an array"
51
+ end
52
+ end
53
+ end
54
+ end