rom 5.4.2 → 6.0.0.alpha1

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -71
  3. data/LICENSE +1 -1
  4. data/README.md +7 -6
  5. data/lib/rom/array_dataset.rb +46 -0
  6. data/lib/rom/associations/abstract.rb +217 -0
  7. data/lib/rom/associations/definitions/abstract.rb +150 -0
  8. data/lib/rom/associations/definitions/many_to_many.rb +29 -0
  9. data/lib/rom/associations/definitions/many_to_one.rb +14 -0
  10. data/lib/rom/associations/definitions/one_to_many.rb +14 -0
  11. data/lib/rom/associations/definitions/one_to_one.rb +14 -0
  12. data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
  13. data/lib/rom/associations/definitions.rb +7 -0
  14. data/lib/rom/associations/many_to_many.rb +128 -0
  15. data/lib/rom/associations/many_to_one.rb +65 -0
  16. data/lib/rom/associations/one_to_many.rb +65 -0
  17. data/lib/rom/associations/one_to_one.rb +13 -0
  18. data/lib/rom/associations/one_to_one_through.rb +13 -0
  19. data/lib/rom/associations/through_identifier.rb +41 -0
  20. data/lib/rom/attribute.rb +425 -0
  21. data/lib/rom/auto_curry.rb +70 -0
  22. data/lib/rom/cache.rb +87 -0
  23. data/lib/rom/changeset/associated.rb +110 -0
  24. data/lib/rom/changeset/create.rb +18 -0
  25. data/lib/rom/changeset/delete.rb +15 -0
  26. data/lib/rom/changeset/extensions/relation.rb +26 -0
  27. data/lib/rom/changeset/pipe.rb +81 -0
  28. data/lib/rom/changeset/pipe_registry.rb +27 -0
  29. data/lib/rom/changeset/stateful.rb +285 -0
  30. data/lib/rom/changeset/update.rb +81 -0
  31. data/lib/rom/changeset.rb +185 -0
  32. data/lib/rom/command.rb +351 -0
  33. data/lib/rom/command_compiler.rb +201 -0
  34. data/lib/rom/command_proxy.rb +36 -0
  35. data/lib/rom/commands/class_interface.rb +236 -0
  36. data/lib/rom/commands/composite.rb +55 -0
  37. data/lib/rom/commands/create.rb +15 -0
  38. data/lib/rom/commands/delete.rb +16 -0
  39. data/lib/rom/commands/graph/class_interface.rb +64 -0
  40. data/lib/rom/commands/graph/input_evaluator.rb +94 -0
  41. data/lib/rom/commands/graph.rb +88 -0
  42. data/lib/rom/commands/lazy/create.rb +35 -0
  43. data/lib/rom/commands/lazy/delete.rb +39 -0
  44. data/lib/rom/commands/lazy/update.rb +46 -0
  45. data/lib/rom/commands/lazy.rb +106 -0
  46. data/lib/rom/commands/update.rb +16 -0
  47. data/lib/rom/commands.rb +5 -0
  48. data/lib/rom/compat/auto_registration.rb +115 -0
  49. data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
  50. data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
  51. data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
  52. data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
  53. data/lib/rom/compat/command.rb +74 -0
  54. data/lib/rom/compat/components/dsl/schema.rb +130 -0
  55. data/lib/rom/compat/components.rb +91 -0
  56. data/lib/rom/compat/global.rb +17 -0
  57. data/lib/rom/compat/mapper.rb +22 -0
  58. data/lib/rom/compat/registries.rb +47 -0
  59. data/lib/rom/compat/relation.rb +40 -0
  60. data/lib/rom/compat/schema/dsl.rb +260 -0
  61. data/lib/rom/compat/setting_proxy.rb +44 -0
  62. data/lib/rom/compat/setup.rb +151 -0
  63. data/lib/rom/compat/transformer.rb +49 -0
  64. data/lib/rom/compat.rb +22 -0
  65. data/lib/rom/components/association.rb +26 -0
  66. data/lib/rom/components/command.rb +24 -0
  67. data/lib/rom/components/core.rb +148 -0
  68. data/lib/rom/components/dataset.rb +60 -0
  69. data/lib/rom/components/dsl/association.rb +47 -0
  70. data/lib/rom/components/dsl/command.rb +60 -0
  71. data/lib/rom/components/dsl/core.rb +126 -0
  72. data/lib/rom/components/dsl/dataset.rb +33 -0
  73. data/lib/rom/components/dsl/gateway.rb +14 -0
  74. data/lib/rom/components/dsl/mapper.rb +70 -0
  75. data/lib/rom/components/dsl/relation.rb +49 -0
  76. data/lib/rom/components/dsl/schema.rb +150 -0
  77. data/lib/rom/components/dsl/view.rb +82 -0
  78. data/lib/rom/components/dsl.rb +255 -0
  79. data/lib/rom/components/gateway.rb +50 -0
  80. data/lib/rom/components/mapper.rb +29 -0
  81. data/lib/rom/components/provider.rb +160 -0
  82. data/lib/rom/components/registry.rb +154 -0
  83. data/lib/rom/components/relation.rb +41 -0
  84. data/lib/rom/components/schema.rb +61 -0
  85. data/lib/rom/components/view.rb +55 -0
  86. data/lib/rom/components.rb +55 -0
  87. data/lib/rom/configuration_dsl.rb +4 -0
  88. data/lib/rom/constants.rb +135 -0
  89. data/lib/rom/container.rb +182 -0
  90. data/lib/rom/core.rb +125 -0
  91. data/lib/rom/data_proxy.rb +97 -0
  92. data/lib/rom/enumerable_dataset.rb +70 -0
  93. data/lib/rom/gateway.rb +232 -0
  94. data/lib/rom/global.rb +56 -0
  95. data/lib/rom/header/attribute.rb +190 -0
  96. data/lib/rom/header.rb +198 -0
  97. data/lib/rom/inferrer.rb +55 -0
  98. data/lib/rom/initializer.rb +80 -0
  99. data/lib/rom/lint/enumerable_dataset.rb +56 -0
  100. data/lib/rom/lint/gateway.rb +120 -0
  101. data/lib/rom/lint/linter.rb +79 -0
  102. data/lib/rom/lint/spec.rb +22 -0
  103. data/lib/rom/lint/test.rb +98 -0
  104. data/lib/rom/loader.rb +161 -0
  105. data/lib/rom/mapper/attribute_dsl.rb +480 -0
  106. data/lib/rom/mapper/dsl.rb +107 -0
  107. data/lib/rom/mapper/model_dsl.rb +61 -0
  108. data/lib/rom/mapper.rb +99 -0
  109. data/lib/rom/mapper_compiler.rb +84 -0
  110. data/lib/rom/memory/associations/many_to_many.rb +12 -0
  111. data/lib/rom/memory/associations/many_to_one.rb +12 -0
  112. data/lib/rom/memory/associations/one_to_many.rb +12 -0
  113. data/lib/rom/memory/associations/one_to_one.rb +12 -0
  114. data/lib/rom/memory/associations.rb +6 -0
  115. data/lib/rom/memory/commands.rb +60 -0
  116. data/lib/rom/memory/dataset.rb +127 -0
  117. data/lib/rom/memory/gateway.rb +66 -0
  118. data/lib/rom/memory/mapper_compiler.rb +10 -0
  119. data/lib/rom/memory/relation.rb +91 -0
  120. data/lib/rom/memory/schema.rb +32 -0
  121. data/lib/rom/memory/storage.rb +61 -0
  122. data/lib/rom/memory/types.rb +11 -0
  123. data/lib/rom/memory.rb +7 -0
  124. data/lib/rom/model_builder.rb +103 -0
  125. data/lib/rom/open_struct.rb +112 -0
  126. data/lib/rom/pipeline.rb +111 -0
  127. data/lib/rom/plugin.rb +130 -0
  128. data/lib/rom/plugins/class_methods.rb +37 -0
  129. data/lib/rom/plugins/command/schema.rb +45 -0
  130. data/lib/rom/plugins/command/timestamps.rb +149 -0
  131. data/lib/rom/plugins/dsl.rb +53 -0
  132. data/lib/rom/plugins/relation/changeset.rb +97 -0
  133. data/lib/rom/plugins/relation/instrumentation.rb +66 -0
  134. data/lib/rom/plugins/relation/registry_reader.rb +36 -0
  135. data/lib/rom/plugins/schema/timestamps.rb +59 -0
  136. data/lib/rom/plugins.rb +100 -0
  137. data/lib/rom/processor/composer.rb +37 -0
  138. data/lib/rom/processor/transformer.rb +415 -0
  139. data/lib/rom/processor.rb +30 -0
  140. data/lib/rom/registries/associations.rb +26 -0
  141. data/lib/rom/registries/commands.rb +11 -0
  142. data/lib/rom/registries/container.rb +12 -0
  143. data/lib/rom/registries/datasets.rb +21 -0
  144. data/lib/rom/registries/gateways.rb +8 -0
  145. data/lib/rom/registries/mappers.rb +21 -0
  146. data/lib/rom/registries/nestable.rb +32 -0
  147. data/lib/rom/registries/relations.rb +8 -0
  148. data/lib/rom/registries/root.rb +203 -0
  149. data/lib/rom/registries/schemas.rb +44 -0
  150. data/lib/rom/registries/views.rb +11 -0
  151. data/lib/rom/relation/class_interface.rb +61 -0
  152. data/lib/rom/relation/combined.rb +160 -0
  153. data/lib/rom/relation/commands.rb +65 -0
  154. data/lib/rom/relation/composite.rb +53 -0
  155. data/lib/rom/relation/curried.rb +129 -0
  156. data/lib/rom/relation/graph.rb +107 -0
  157. data/lib/rom/relation/loaded.rb +136 -0
  158. data/lib/rom/relation/materializable.rb +62 -0
  159. data/lib/rom/relation/name.rb +122 -0
  160. data/lib/rom/relation/wrap.rb +64 -0
  161. data/lib/rom/relation.rb +625 -0
  162. data/lib/rom/repository/class_interface.rb +162 -0
  163. data/lib/rom/repository/relation_reader.rb +48 -0
  164. data/lib/rom/repository/root.rb +75 -0
  165. data/lib/rom/repository/session.rb +60 -0
  166. data/lib/rom/repository.rb +179 -0
  167. data/lib/rom/schema/associations_dsl.rb +222 -0
  168. data/lib/rom/schema/inferrer.rb +106 -0
  169. data/lib/rom/schema.rb +471 -0
  170. data/lib/rom/settings.rb +141 -0
  171. data/lib/rom/setup.rb +297 -0
  172. data/lib/rom/struct.rb +99 -0
  173. data/lib/rom/struct_compiler.rb +114 -0
  174. data/lib/rom/support/configurable.rb +213 -0
  175. data/lib/rom/support/inflector.rb +31 -0
  176. data/lib/rom/support/memoizable.rb +61 -0
  177. data/lib/rom/support/notifications.rb +238 -0
  178. data/lib/rom/transaction.rb +26 -0
  179. data/lib/rom/transformer.rb +46 -0
  180. data/lib/rom/types.rb +74 -0
  181. data/lib/rom/version.rb +1 -1
  182. data/lib/rom-changeset.rb +4 -0
  183. data/lib/rom-core.rb +3 -0
  184. data/lib/rom-repository.rb +4 -0
  185. data/lib/rom.rb +3 -3
  186. metadata +302 -23
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/container"
4
+
5
+ module ROM
6
+ # ROM container is an isolated environment with no global state where all
7
+ # components are registered. Container objects provide access to your
8
+ # relations, commands and mappers. ROM containers are usually configured and
9
+ # handled via framework integrations, although it is easy to use them
10
+ # standalone.
11
+ #
12
+ # There are 3 types of container setup:
13
+ #
14
+ # * Setup DSL - a simple block-based configuration which allows configuring
15
+ # all components and gives you back a container instance. This type is suitable
16
+ # for small scripts, or in some cases rake tasks
17
+ # * Explicit setup - this type requires creating a configuration object,
18
+ # registering component classes (ie relation classes) and passing the config
19
+ # to container builder function. This type is suitable when your environment
20
+ # is not typical and you need full control over component registration
21
+ # * Explicit setup with auto-registration - same as explicit setup but allows
22
+ # you to configure auto-registration mechanism which will register component
23
+ # classes for you, based on dir/file naming conventions. This is the most
24
+ # common type of setup that's used by framework integrations
25
+ #
26
+ # @example in-line setup
27
+ # rom = ROM.setup(:sql, 'sqlite::memory') do |config|
28
+ # config.default.create_table :users do
29
+ # primary_key :id
30
+ # column :name, String, null: false
31
+ # end
32
+ #
33
+ # config.relation(:users) do
34
+ # schema(infer: true)
35
+ #
36
+ # def by_name(name)
37
+ # where(name: name)
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # rom.relations[:users].insert(name: "Jane")
43
+ #
44
+ # rom.relations[:users].by_name("Jane").to_a
45
+ # # [{:id=>1, :name=>"Jane"}]
46
+ #
47
+ # @example multi-step setup with explicit component classes
48
+ # config = ROM::Configuration.new(:sql, 'sqlite::memory')
49
+ #
50
+ # config.default.create_table :users do
51
+ # primary_key :id
52
+ # column :name, String, null: false
53
+ # end
54
+ #
55
+ # class Users < ROM::Relation[:sql]
56
+ # schema(:users, infer: true)
57
+ #
58
+ # def by_name(name)
59
+ # where(name: name)
60
+ # end
61
+ # end
62
+ #
63
+ # config.register_relation(Users)
64
+ #
65
+ # rom = ROM.setup(config)
66
+ #
67
+ # rom.relations[:users].insert(name: "Jane")
68
+ #
69
+ # rom.relations[:users].by_name("Jane").to_a
70
+ # # [{:id=>1, :name=>"Jane"}]
71
+ #
72
+ #
73
+ # @example multi-step setup with auto-registration
74
+ # config = ROM::Configuration.new(:sql, 'sqlite::memory')
75
+ # config.auto_register('./persistence', namespace: false)
76
+ #
77
+ # config.default.create_table :users do
78
+ # primary_key :id
79
+ # column :name, String, null: false
80
+ # end
81
+ #
82
+ # # ./persistence/relations/users.rb
83
+ # class Users < ROM::Relation[:sql]
84
+ # schema(infer: true)
85
+ #
86
+ # def by_name(name)
87
+ # where(name: name)
88
+ # end
89
+ # end
90
+ #
91
+ # rom = ROM.setup(config)
92
+ #
93
+ # rom.relations[:users].insert(name: "Jane")
94
+ #
95
+ # rom.relations[:users].by_name("Jane").to_a
96
+ # # [{:id=>1, :name=>"Jane"}]
97
+ #
98
+ # @api public
99
+ class Container
100
+ include Dry::Container::Mixin
101
+ include Dry::Equalizer(:gateways, :relations, :mappers, :commands)
102
+
103
+ # @api private
104
+ def self.new(configuration)
105
+ super().tap do |container|
106
+ container.register(:configuration, memoize: true) do
107
+ Setup::Configuration.new(
108
+ configuration: configuration, container: container
109
+ )
110
+ end
111
+ end
112
+ end
113
+
114
+ # Return runtime configuration with component registries
115
+ #
116
+ # @return [Setup::Configuration]
117
+ #
118
+ # @api public
119
+ def configuration
120
+ self[:configuration]
121
+ end
122
+
123
+ # Return registered gateways
124
+ #
125
+ # @return [Hash<Symbol=>Gateway>]
126
+ #
127
+ # @api public
128
+ def gateways
129
+ configuration.gateways
130
+ end
131
+
132
+ # Return relation registry
133
+ #
134
+ # @return [RelationRegistry]
135
+ #
136
+ # @api public
137
+ def schemas
138
+ configuration.schemas
139
+ end
140
+
141
+ # Return relation registry
142
+ #
143
+ # @return [RelationRegistry]
144
+ #
145
+ # @api public
146
+ def relations
147
+ configuration.relations
148
+ end
149
+
150
+ # Return mapper registry for all relations
151
+ #
152
+ # @return [Hash<Symbol=>MapperRegistry]
153
+ #
154
+ # @api public
155
+ def mappers
156
+ configuration.mappers
157
+ end
158
+
159
+ # Return command registry
160
+ #
161
+ # @return [Hash<Symbol=>CommandRegistry]
162
+ #
163
+ # @api public
164
+ def commands
165
+ configuration.commands
166
+ end
167
+
168
+ # Disconnect all gateways
169
+ #
170
+ # @example
171
+ # rom = ROM.setup(:sql, 'sqlite://my_db.sqlite')
172
+ # rom.relations[:users].insert(name: "Jane")
173
+ # rom.disconnect
174
+ #
175
+ # @return [Hash<Symbol=>Gateway>] a hash with disconnected gateways
176
+ #
177
+ # @api public
178
+ def disconnect
179
+ gateways.each_value(&:disconnect)
180
+ end
181
+ end
182
+ end
data/lib/rom/core.rb ADDED
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Global interface
4
+ require_relative "global"
5
+
6
+ # Global default settings
7
+ require_relative "settings"
8
+
9
+ # Core components
10
+ require_relative "components/gateway"
11
+ require_relative "components/dataset"
12
+ require_relative "components/schema"
13
+ require_relative "components/relation"
14
+ require_relative "components/view"
15
+ require_relative "components/association"
16
+ require_relative "components/command"
17
+ require_relative "components/mapper"
18
+
19
+ # Core plugins
20
+ require_relative "plugins"
21
+
22
+ # Set up ROM
23
+ #
24
+ # @api public
25
+ def ROM(*args, &block)
26
+ if block
27
+ ROM.setup(*args, &block)
28
+ else
29
+ ROM::Setup.new(*args)
30
+ end
31
+ end
32
+
33
+ # Global ROM interface for core setup
34
+ #
35
+ # @api public
36
+ module ROM
37
+ extend Global
38
+
39
+ module_function
40
+
41
+ # Global component setup
42
+ #
43
+ # @example
44
+ # ROM.components do
45
+ # register :cache, handler: MyApp::MyCacheHandler
46
+ # end
47
+ #
48
+ # @api public
49
+ def components(&block)
50
+ if defined?(@_components)
51
+ @_components.instance_eval(&block) if block
52
+ @_components
53
+ else
54
+ require_relative "components"
55
+ @_components = Components
56
+ components(&block)
57
+ end
58
+ end
59
+
60
+ # Global plugin setup
61
+ #
62
+ # @example
63
+ # ROM.plugins do
64
+ # register :publisher, Plugin::Publisher, type: :command
65
+ # end
66
+ #
67
+ # @api public
68
+ def plugins(*args, &block)
69
+ if defined?(@_plugins)
70
+ @_plugins.dsl(*args, &block) if block
71
+ @_plugins
72
+ else
73
+ require_relative "plugins"
74
+ @_plugins = Plugins
75
+ plugins(*args, &block)
76
+ end
77
+ end
78
+
79
+ # Register core component handlers
80
+ components do
81
+ register :gateway, Components::Gateway
82
+ register :dataset, Components::Dataset
83
+ register :schema, Components::Schema
84
+ register :relation, Components::Relation
85
+ register :view, Components::View
86
+ register :association, Components::Association
87
+ register :command, Components::Command
88
+ register :mapper, Components::Mapper
89
+ end
90
+
91
+ # TODO: this will be automated eventually
92
+ require_relative "relation"
93
+ require_relative "schema"
94
+ require_relative "command"
95
+ require_relative "mapper"
96
+ require_relative "transformer"
97
+
98
+ configs = {
99
+ schema: [ROM::Schema],
100
+ relation: [ROM::Relation],
101
+ command: [ROM::Command],
102
+ mapper: [ROM::Mapper, ROM::Transformer]
103
+ }
104
+
105
+ configs.each do |key, items|
106
+ items.each do |constant|
107
+ constant.config.component.inherit!(config.component)
108
+ config[key].inherit!(constant.config.component)
109
+ end
110
+ end
111
+
112
+ configs.values.flatten(1).each(&:configure).each(&:finalize!)
113
+
114
+ # Register core plugins
115
+ plugins do
116
+ register :timestamps, ROM::Plugins::Schema::Timestamps, type: :schema
117
+ register :registry_reader, ROM::Plugins::Relation::RegistryReader, type: :relation
118
+ register :instrumentation, ROM::Plugins::Relation::Instrumentation, type: :relation
119
+ register :changeset, ROM::Plugins::Relation::Changeset, type: :relation
120
+ register :schema, ROM::Plugins::Command::Schema, type: :command
121
+ register :timestamps, ROM::Plugins::Command::Timestamps, type: :command
122
+ end
123
+
124
+ finalize!
125
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ # Helper module for dataset classes
5
+ #
6
+ # It provides a constructor accepting data, header and an optional row_proc.
7
+ # This module is used internally by EnumerableDataset and ArrayDataset.
8
+ #
9
+ # @private
10
+ module DataProxy
11
+ NON_FORWARDABLE = %i[
12
+ each to_a to_ary kind_of? instance_of? is_a?
13
+ ].freeze
14
+
15
+ # Wrapped data array
16
+ #
17
+ # @return [Object] Data object for the iterator
18
+ #
19
+ # @api private
20
+ attr_reader :data
21
+
22
+ # @return [Proc] tuple processing proc
23
+ #
24
+ # @api private
25
+ attr_reader :row_proc
26
+
27
+ # Extends the class with `forward` DSL and Equalizer using `data` attribute
28
+ #
29
+ # @see ClassMethods#forward
30
+ #
31
+ # @api private
32
+ def self.included(klass)
33
+ klass.class_eval do
34
+ extend ClassMethods
35
+
36
+ include Dry::Equalizer(:data)
37
+
38
+ option :row_proc, default: -> { self.class.row_proc }
39
+ end
40
+ end
41
+
42
+ # Iterate over data using row_proc
43
+ #
44
+ # @return [Enumerator] if block is not given
45
+ #
46
+ # @api private
47
+ def each
48
+ return to_enum unless block_given?
49
+
50
+ data.each { |tuple| yield(row_proc[tuple]) }
51
+ end
52
+
53
+ module ClassMethods
54
+ # Default no-op tuple proc
55
+ #
56
+ # @return [Proc]
57
+ #
58
+ # @api private
59
+ def row_proc
60
+ -> tuple { tuple }
61
+ end
62
+
63
+ # Forward provided methods to the underlaying data object
64
+ #
65
+ # @example
66
+ #
67
+ # class MyDataset
68
+ # include DataProxy
69
+ #
70
+ # forward(:find_all, :map)
71
+ # end
72
+ #
73
+ # @return [undefined]
74
+ #
75
+ # @api public
76
+ def forward(*methods)
77
+ # FIXME: we should probably raise if one of the non-forwardable methods
78
+ # was provided
79
+ (methods - NON_FORWARDABLE).each do |method_name|
80
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
81
+ def #{method_name}(*args, &block)
82
+ response = data.public_send(#{method_name.inspect}, *args, &block)
83
+
84
+ if response.equal?(data)
85
+ self
86
+ elsif response.is_a?(data.class)
87
+ self.class.new(response)
88
+ else
89
+ response
90
+ end
91
+ end
92
+ RUBY
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/initializer"
4
+ require "rom/data_proxy"
5
+
6
+ module ROM
7
+ # A helper module that adds data-proxy behavior to an enumerable object
8
+ #
9
+ # This module is intended to be used by gateways
10
+ #
11
+ # Class that includes this module can define `row_proc` class method which
12
+ # must return a proc-like object which will be used to process each element
13
+ # in the enumerable
14
+ #
15
+ # @example
16
+ # class MyDataset
17
+ # include ROM::EnumerableDataset
18
+ #
19
+ # def self.row_proc
20
+ # -> tuple { tuple.each_with_object({}) { |(k,v), h| h[k.to_sym] = v } }
21
+ # end
22
+ # end
23
+ #
24
+ # ds = MyDataset.new([{ 'name' => 'Jane' }, [:name])
25
+ # ds.to_a # => { :name => 'Jane' }
26
+ #
27
+ # @api public
28
+ module EnumerableDataset
29
+ extend DataProxy::ClassMethods
30
+ include Enumerable
31
+
32
+ # Coerce a dataset to an array
33
+ #
34
+ # @return [Array]
35
+ #
36
+ # @api public
37
+ alias_method :to_ary, :to_a
38
+
39
+ # Included hook which extends a class with DataProxy behavior
40
+ #
41
+ # This module can also be included into other modules so we apply the
42
+ # extension only for classes
43
+ #
44
+ # @api private
45
+ def self.included(klass)
46
+ return unless klass.is_a?(Class)
47
+
48
+ klass.class_eval do
49
+ extend Initializer
50
+ include DataProxy
51
+
52
+ param :data
53
+ end
54
+ end
55
+
56
+ forward :take
57
+
58
+ %i[
59
+ chunk collect collect_concat drop_while find_all flat_map
60
+ grep map reject select sort sort_by take_while
61
+ ].each do |method|
62
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
63
+ def #{method}(*args, &block)
64
+ return to_enum unless block
65
+ self.class.new(super(*args, &block), **options)
66
+ end
67
+ RUBY
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/class_attributes"
4
+
5
+ require_relative "support/notifications"
6
+ require_relative "transaction"
7
+ require_relative "components/provider"
8
+
9
+ module ROM
10
+ # Abstract gateway class
11
+ #
12
+ # Every adapter needs to inherit from this class and implement
13
+ # required interface
14
+ #
15
+ # @abstract
16
+ #
17
+ # @api public
18
+ class Gateway
19
+ extend Dry::Core::ClassAttributes
20
+ extend Notifications::Listener
21
+
22
+ extend ROM::Provider(:plugin, type: :gateway)
23
+
24
+ # @!method self.adapter
25
+ # Get or set gateway's adapter identifier
26
+ #
27
+ # @overload adapter
28
+ # Return adapter identifier
29
+ # @return [Symbol]
30
+ #
31
+ # @overload gateway(adapter)
32
+ # @example
33
+ # class MyGateway < ROM::Gateway
34
+ # config.component.adapter = :my_adapter
35
+ # end
36
+ #
37
+ # @param [Symbol] adapter The adapter identifier
38
+ defines :adapter
39
+
40
+ # @!attribute [r] config
41
+ # @return [Configurable::Config] Gateway's configuration identifier
42
+ attr_reader :config
43
+
44
+ # @!attribute [r] connection
45
+ # @return [Object] The gateway's connection object (type varies across adapters)
46
+ attr_reader :connection
47
+
48
+ # Set up a gateway
49
+ #
50
+ # @overload setup(type, *args)
51
+ # Sets up a single-gateway given a gateway type.
52
+ # For custom gateways, create an instance and pass it directly.
53
+ #
54
+ # @example
55
+ # module SuperDB
56
+ # class Gateway < ROM::Gateway
57
+ # def initialize(options)
58
+ # end
59
+ # end
60
+ # end
61
+ #
62
+ # ROM.register_adapter(:super_db, SuperDB)
63
+ #
64
+ # Gateway.setup(:super_db, some: 'options')
65
+ # # SuperDB::Gateway.new(some: 'options') is called
66
+ #
67
+ # @param [Symbol] type Registered gateway identifier
68
+ # @param [Array] args Additional gateway options
69
+ #
70
+ # @overload setup(gateway)
71
+ # Set up a gateway instance
72
+ #
73
+ # @example
74
+ # module SuperDB
75
+ # class Gateway < ROM::Gateway
76
+ # def initialize(options)
77
+ # end
78
+ # end
79
+ # end
80
+ #
81
+ # ROM.register_adapter(:super_db, SuperDB)
82
+ #
83
+ # Gateway.setup(SuperDB::Gateway.new(some: 'options'))
84
+ #
85
+ # @param [Gateway] gateway
86
+ #
87
+ # @return [Gateway] a specific gateway subclass
88
+ #
89
+ # @api public
90
+ #
91
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
92
+ def self.setup(gateway_or_scheme, *args)
93
+ case gateway_or_scheme
94
+ when Gateway
95
+ unless args.empty?
96
+ raise ArgumentError, "Can't accept arguments when passing an instance"
97
+ end
98
+
99
+ gateway_or_scheme
100
+ when String
101
+ raise ArgumentError, <<-STRING.gsub(/^ {10}/, "")
102
+ URIs without an explicit scheme are not supported anymore.
103
+ See https://github.com/rom-rb/rom/blob/main/CHANGELOG.md
104
+ STRING
105
+ when Symbol
106
+ klass = class_from_symbol(gateway_or_scheme)
107
+
108
+ if klass.instance_method(:initialize).arity.zero?
109
+ klass.new
110
+ elsif args.size.equal?(1) && args.first.respond_to?(:args)
111
+ if args.first.respond_to?(:args)
112
+ setup(gateway_or_scheme, *args.first.args)
113
+ else
114
+ klass.new(**config)
115
+ end
116
+ elsif args.last.is_a?(Hash)
117
+ klass.new(*args[0..-2], **args.last)
118
+ else
119
+ klass.new(*args)
120
+ end
121
+ else
122
+ gateway_or_scheme
123
+ end
124
+ end
125
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
126
+
127
+ class << self
128
+ ruby2_keywords(:setup) if respond_to?(:ruby2_keywords, true)
129
+ end
130
+
131
+ # Get gateway subclass for a specific adapter
132
+ #
133
+ # @param [Symbol] type Adapter identifier
134
+ #
135
+ # @return [Class]
136
+ #
137
+ # @api private
138
+ def self.class_from_symbol(type)
139
+ adapter = ROM.adapters.fetch(type) do
140
+ begin
141
+ require "rom/#{type}"
142
+ rescue LoadError
143
+ raise AdapterLoadError, "Failed to load adapter rom/#{type}"
144
+ end
145
+
146
+ ROM.adapters.fetch(type)
147
+ end
148
+
149
+ adapter.const_get(:Gateway)
150
+ end
151
+
152
+ # Configured gateway name used in the registry
153
+ #
154
+ # @return [Symbol]
155
+ #
156
+ # @api public
157
+ def name
158
+ config.id
159
+ end
160
+
161
+ # Returns the adapter, defined for the class
162
+ #
163
+ # @return [Symbol]
164
+ #
165
+ # @api public
166
+ def adapter
167
+ self.class.adapter || raise(
168
+ MissingAdapterIdentifierError,
169
+ "gateway class +#{self}+ is missing the adapter identifier"
170
+ )
171
+ end
172
+
173
+ # A generic interface for setting up a logger
174
+ #
175
+ # This is not a required interface, it's a no-op by default
176
+ #
177
+ # @abstract
178
+ #
179
+ # @api public
180
+ def use_logger(*)
181
+ # noop
182
+ end
183
+
184
+ # A generic interface for returning default logger
185
+ #
186
+ # Adapters should implement this method as handling loggers is different
187
+ # across adapters. This is a no-op by default and returns nil.
188
+ #
189
+ # @return [NilClass]
190
+ #
191
+ # @api public
192
+ def logger
193
+ # noop
194
+ end
195
+
196
+ # Disconnect is optional and it's a no-op by default
197
+ #
198
+ # @api public
199
+ def disconnect
200
+ # noop
201
+ end
202
+
203
+ # Runs a block inside a transaction. The underlying transaction engine
204
+ # is adapter-specific
205
+ #
206
+ # @param [Hash] opts Transaction options
207
+ #
208
+ # @return The result of yielding the block or +nil+ if
209
+ # the transaction was rolled back
210
+ #
211
+ # @api public
212
+ def transaction(**opts, &block)
213
+ transaction_runner(**opts).run(**opts, &block)
214
+ end
215
+
216
+ # Build a command instance
217
+ #
218
+ # @return [Command]
219
+ #
220
+ # @api public
221
+ def command(klass, relation:, **opts)
222
+ klass.build(relation, **opts)
223
+ end
224
+
225
+ private
226
+
227
+ # @api private
228
+ def transaction_runner(**)
229
+ Transaction::NoOp
230
+ end
231
+ end
232
+ end