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,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