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,45 @@
1
+ module ROM
2
+ # Abstract plugin base
3
+ #
4
+ # @private
5
+ class PluginBase
6
+ # @return [Module] a module representing the plugin
7
+ #
8
+ # @api private
9
+ attr_reader :mod
10
+
11
+ # @return [Hash] configuration options
12
+ #
13
+ # @api private
14
+ attr_reader :options
15
+
16
+ # @api private
17
+ attr_reader :type
18
+
19
+ # @api private
20
+ def initialize(mod, options)
21
+ @mod = mod
22
+ @options = options
23
+ @type = options.fetch(:type)
24
+ end
25
+
26
+ # @api private
27
+ def relation?
28
+ type == :relation
29
+ end
30
+
31
+ # @api private
32
+ def schema?
33
+ type == :schema
34
+ end
35
+
36
+ # Apply this plugin to the provided class
37
+ #
38
+ # @param [Mixed] base
39
+ #
40
+ # @api private
41
+ def apply_to(_base)
42
+ raise NotImplementedError, "#{self.class}#apply_to not implemented"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,197 @@
1
+ require 'rom/registry'
2
+
3
+ module ROM
4
+ # Stores all registered plugins
5
+ #
6
+ # @api private
7
+ class PluginRegistry
8
+ # Internal registry for configuration plugins
9
+ #
10
+ # @return [ConfigurationPluginRegistry]
11
+ #
12
+ # @api private
13
+ attr_reader :configuration
14
+
15
+ # Internal registry for command plugins
16
+ #
17
+ # @return [InternalPluginRegistry]
18
+ #
19
+ # @api private
20
+ attr_reader :commands
21
+
22
+ # Internal registry for mapper plugins
23
+ #
24
+ # @return [InternalPluginRegistry]
25
+ #
26
+ # @api private
27
+ attr_reader :mappers
28
+
29
+ # Internal registry for relation plugins
30
+ #
31
+ # @return [InternalPluginRegistry]
32
+ #
33
+ # @api private
34
+ attr_reader :relations
35
+
36
+ # Internal registry for schema plugins
37
+ #
38
+ # @return [InternalPluginRegistry]
39
+ #
40
+ # @api private
41
+ attr_reader :schemas
42
+
43
+ # @api private
44
+ def initialize
45
+ @configuration = ConfigurationPluginRegistry.new
46
+ @mappers = InternalPluginRegistry.new
47
+ @commands = InternalPluginRegistry.new
48
+ @relations = InternalPluginRegistry.new
49
+ @schemas = InternalPluginRegistry.new(SchemaPlugin)
50
+ end
51
+
52
+ # Register a plugin for future use
53
+ #
54
+ # @param [Symbol] name The registration name for the plugin
55
+ # @param [Module] mod The plugin to register
56
+ # @param [Hash] options optional configuration data
57
+ # @option options [Symbol] :type What type of plugin this is (command,
58
+ # relation or mapper)
59
+ # @option options [Symbol] :adapter (:default) which adapter this plugin
60
+ # applies to. Leave blank for all adapters
61
+ def register(name, mod, options = EMPTY_HASH)
62
+ type = options.fetch(:type)
63
+ adapter = options.fetch(:adapter, :default)
64
+
65
+ plugins_for(type, adapter).register(name, mod, options)
66
+ end
67
+
68
+ private
69
+
70
+ # Determine which specific registry to use
71
+ #
72
+ # @api private
73
+ def plugins_for(type, adapter)
74
+ case type
75
+ when :configuration then configuration
76
+ when :command then commands.adapter(adapter)
77
+ when :mapper then mappers.adapter(adapter)
78
+ when :relation then relations.adapter(adapter)
79
+ when :schema then schemas.adapter(adapter)
80
+ end
81
+ end
82
+ end
83
+
84
+ # Abstract registry defining common behaviour
85
+ #
86
+ # @api private
87
+ class PluginRegistryBase < Registry
88
+ include Dry::Equalizer(:elements, :plugin_type)
89
+
90
+ # !@attribute [r] plugin_type
91
+ # @return [Class] Typically ROM::PluginBase or its descendant
92
+ option :plugin_type
93
+
94
+ # Retrieve a registered plugin
95
+ #
96
+ # @param [Symbol] name The plugin to retrieve
97
+ #
98
+ # @return [Plugin]
99
+ #
100
+ # @api public
101
+ def [](name)
102
+ elements[name]
103
+ end
104
+
105
+ # Assign a plugin to this environment registry
106
+ #
107
+ # @param [Symbol] name The registered plugin name
108
+ # @param [Module] mod The plugin to register
109
+ # @param [Hash] options optional configuration data
110
+ #
111
+ # @api private
112
+ def register(name, mod, options)
113
+ elements[name] = plugin_type.new(mod, options)
114
+ end
115
+
116
+ # Returns plugin name by instance
117
+ #
118
+ # @return [Symbol] Plugin name
119
+ #
120
+ # @api private
121
+ def plugin_name(plugin)
122
+ tuple = elements.find { |(_, p)| p.equal?(plugin) }
123
+ tuple[0] if tuple
124
+ end
125
+ end
126
+
127
+ # A registry storing environment specific plugins
128
+ #
129
+ # @api private
130
+ class ConfigurationPluginRegistry < PluginRegistryBase
131
+ # @api private
132
+ def initialize(*args, **kwargs)
133
+ super(*args, **kwargs, plugin_type: ConfigurationPlugin)
134
+ end
135
+
136
+ # Return an environment plugin
137
+ #
138
+ # @param [Symbol] name The name of the environment plugin
139
+ #
140
+ # @raises [UnknownPluginError] if no plugin is found with the given name
141
+ #
142
+ # @api public
143
+ def fetch(name)
144
+ self[name] || raise(UnknownPluginError, name)
145
+ end
146
+ end
147
+
148
+ # A registry storing adapter specific plugins
149
+ #
150
+ # @api private
151
+ class AdapterPluginRegistry < PluginRegistryBase
152
+ option :plugin_type, default: -> { Plugin }
153
+ end
154
+
155
+ # Store a set of registries grouped by adapter
156
+ #
157
+ # @api private
158
+ class InternalPluginRegistry
159
+ # Return the existing registries
160
+ #
161
+ # @return [Hash]
162
+ #
163
+ # @api private
164
+ attr_reader :registries
165
+
166
+ # @api private
167
+ def initialize(plugin_type = Plugin)
168
+ @registries = Hash.new { |h, v| h[v] = AdapterPluginRegistry.new({}, plugin_type: plugin_type) }
169
+ end
170
+
171
+ # Return the plugin registry for a specific adapter
172
+ #
173
+ # @param [Symbol] name The name of the adapter
174
+ #
175
+ # @return [AdapterRegistry]
176
+ #
177
+ # @api private
178
+ def adapter(name)
179
+ registries[name]
180
+ end
181
+
182
+ # Return the plugin for a given adapter
183
+ #
184
+ # @param [Symbol] name The name of the plugin
185
+ # @param [Symbol] adapter_name (:default) The name of the adapter used
186
+ #
187
+ # @raises [UnknownPluginError] if no plugin is found with the given name
188
+ #
189
+ # @api public
190
+ def fetch(name, adapter_name = :default)
191
+ adapter(adapter_name)[name] || adapter(:default)[name] ||
192
+ raise(UnknownPluginError, name)
193
+ end
194
+
195
+ alias_method :[], :fetch
196
+ end
197
+ end
@@ -0,0 +1,37 @@
1
+ module ROM
2
+ module Plugins
3
+ module Command
4
+ # @api private
5
+ module Schema
6
+ def self.included(klass)
7
+ super
8
+ klass.extend(ClassInterface)
9
+ end
10
+
11
+ # @api private
12
+ module ClassInterface
13
+ # @see Command.build
14
+ # @api public
15
+ def build(relation, options = {})
16
+ if options.key?(:input) || !relation.schema?
17
+ super
18
+ else
19
+ default_input = options.fetch(:input, input)
20
+
21
+ input_handler =
22
+ if default_input != Hash && relation.schema?
23
+ -> tuple { relation.input_schema[input[tuple]] }
24
+ elsif relation.schema?
25
+ relation.input_schema
26
+ else
27
+ default_input
28
+ end
29
+
30
+ super(relation, options.merge(input: input_handler))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require 'rom/configuration_dsl'
2
+ require 'dry/core/deprecations'
3
+
4
+ module ROM
5
+ module ConfigurationPlugins
6
+ # Provides macros for defining relations, mappers and commands
7
+ #
8
+ # @api public
9
+ module ConfigurationDSL
10
+
11
+ # @api private
12
+ def self.apply(configuration, options = {})
13
+ Dry::Core::Deprecations.announce(
14
+ :macros,
15
+ "Calling `use(:macros)` is no longer necessary. Macros are enabled by default.",
16
+ tag: :rom
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ module ROM
4
+ module Plugins
5
+ module Relation
6
+ # Experimental plugin for configuring relations with an external
7
+ # instrumentation system like dry-monitor or ActiveSupport::Notifications
8
+ #
9
+ # @api public
10
+ module Instrumentation
11
+ extend Dry::Core::ClassAttributes
12
+
13
+ # This hooks sets up a relation class with injectible notifications object
14
+ #
15
+ # @api private
16
+ def self.included(klass)
17
+ super
18
+ klass.option :notifications
19
+ klass.extend(ClassInterface)
20
+ klass.prepend(mixin)
21
+ klass.instrument(:to_a)
22
+ end
23
+
24
+ defines :mixin
25
+ mixin Module.new
26
+
27
+ module ClassInterface
28
+ def instrument(*methods)
29
+ (methods - Instrumentation.mixin.instance_methods).each do |meth|
30
+ Instrumentation.mixin.send(:define_method, meth) do
31
+ instrument { super() }
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # @api public
38
+ def instrument(&block)
39
+ notifications.instrument(self.class.adapter, name: name.relation, **notification_payload(self), &block)
40
+ end
41
+
42
+ private
43
+
44
+ # @api private
45
+ def notification_payload(relation)
46
+ EMPTY_HASH
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ require 'dry/core/cache'
2
+ require 'rom/constants'
3
+
4
+ module ROM
5
+ module Plugins
6
+ module Relation
7
+ # Allows relations to access all other relations through registry
8
+ #
9
+ # For now this plugin is always enabled
10
+ #
11
+ # @api public
12
+ class RegistryReader < Module
13
+ extend Dry::Core::Cache
14
+
15
+ EMPTY_REGISTRY = RelationRegistry.new(EMPTY_HASH).freeze
16
+
17
+ # @api private
18
+ attr_reader :relations
19
+
20
+ # @api private
21
+ def initialize(relations)
22
+ @relations = relations
23
+ define_readers!
24
+ end
25
+
26
+ # @api private
27
+ def included(klass)
28
+ super
29
+ return if klass.instance_methods.include?(:__registry__)
30
+ klass.option :__registry__, default: -> { EMPTY_REGISTRY }
31
+ end
32
+
33
+ private
34
+
35
+ # @api private
36
+ def define_readers!
37
+ relations.each do |name|
38
+ define_method(name) { __registry__[name] }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+ module ROM
2
+ module Plugins
3
+ module Schema
4
+ # A plugin for automatically adding timestamp fields
5
+ # to the schema definition
6
+ #
7
+ # @example
8
+ # schema do
9
+ # use :timestamps
10
+ # end
11
+ #
12
+ # # using non-default names
13
+ # schema do
14
+ # use :timestamps, attributes: %i(created_on updated_on)
15
+ # end
16
+ #
17
+ # # using other types
18
+ # schema do
19
+ # use :timestamps, type: Types::Date
20
+ # end
21
+ #
22
+ # @api public
23
+ module Timestamps
24
+ DEFAULT_TIMESTAMPS = %i(created_at updated_at).freeze
25
+
26
+ # @api private
27
+ def self.apply(schema, options)
28
+ type = options.fetch(:type, Types::Time)
29
+ names = options.fetch(:attributes, DEFAULT_TIMESTAMPS)
30
+ attributes = names.map { |name| type.meta(name: name, source: schema.name) }
31
+
32
+ schema.attributes.concat(
33
+ schema.class.attributes(attributes, schema.attr_class)
34
+ )
35
+ end
36
+
37
+ # @api private
38
+ module DSL
39
+ # Sets non-default timestamp attributes
40
+ #
41
+ # @example
42
+ # schema do
43
+ # use :timestamps
44
+ # timestamps :create_on, :updated_on
45
+ # end
46
+ #
47
+ # @api public
48
+ def timestamps(*names)
49
+ options = plugin_options(:timestamps)
50
+ options[:attributes] = names unless names.empty?
51
+
52
+ self
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end