rom-core 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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