rom 5.4.1 → 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 -65
  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,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "core"
4
+
5
+ module ROM
6
+ module Components
7
+ module DSL
8
+ # Setup DSL-specific relation extensions
9
+ #
10
+ # @private
11
+ class Relation < Core
12
+ key :relations
13
+
14
+ # @api private
15
+ def call
16
+ super(constant: constant, config: constant.config.component)
17
+ end
18
+
19
+ # @api private
20
+ memoize def constant
21
+ build_class do |dsl|
22
+ config.component.adapter = dsl.adapter if dsl.adapter
23
+
24
+ class_exec(&dsl.block) if dsl.block
25
+
26
+ if (schema_dataset = components.schemas.first&.config&.dataset)
27
+ config.component.dataset = schema_dataset
28
+ end
29
+ end
30
+ end
31
+
32
+ # @api private
33
+ memoize def class_name
34
+ class_name_inferrer[
35
+ config.id,
36
+ type: :relation,
37
+ inflector: inflector,
38
+ class_namespace: provider.class_namespace
39
+ ]
40
+ end
41
+
42
+ # @api private
43
+ def class_parent
44
+ ROM::Relation[adapter]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/schema"
4
+ require "rom/attribute"
5
+
6
+ require_relative "core"
7
+
8
+ module ROM
9
+ module Components
10
+ module DSL
11
+ # @private
12
+ class Schema < Core
13
+ key :schemas
14
+
15
+ option :attributes, default: -> { EMPTY_HASH.dup }
16
+
17
+ # Defines a relation attribute with its type and options.
18
+ #
19
+ # When only options are given, type is left as nil. It makes
20
+ # sense when it is used alongside a schema inferrer, which will
21
+ # populate the type.
22
+ #
23
+ # @see Components::DSL#schema
24
+ #
25
+ # @api public
26
+ def attribute(name, type_or_options, options = EMPTY_HASH)
27
+ if attributes.key?(name)
28
+ raise(AttributeAlreadyDefinedError, "Attribute #{name.inspect} already defined")
29
+ end
30
+
31
+ build_attribute_info(name, type_or_options, options).tap do |attr_info|
32
+ attributes[name] = attr_info
33
+ end
34
+ end
35
+
36
+ # Specify which key(s) should be the primary key
37
+ #
38
+ # @api public
39
+ def primary_key(*names)
40
+ names.each do |name|
41
+ attributes[name][:type] = attributes[name][:type].meta(primary_key: true)
42
+ end
43
+ self
44
+ end
45
+
46
+ # @api private
47
+ def call
48
+ # Evaluate block only if it's not a schema defined by Relation.view DSL
49
+ instance_eval(&block) if block && !config.view
50
+
51
+ enabled_plugins.each_value do |plugin|
52
+ plugin.apply unless plugin.applied?
53
+ end
54
+
55
+ configure
56
+
57
+ components.add(key, config: config, block: config.view ? block : nil)
58
+ end
59
+
60
+ # @api private
61
+ # rubocop:disable Metrics/AbcSize
62
+ def configure
63
+ config.update(attributes: attributes.values)
64
+
65
+ # TODO: make this simpler
66
+ config.update(
67
+ relation: relation_id,
68
+ inferrer: config.inferrer.with(enabled: config.infer)
69
+ )
70
+
71
+ if !view? && relation?
72
+ config.join!({namespace: relation_id}, :right) if config.id != relation_id
73
+
74
+ provider.config.component.update(dataset: config.dataset) if config.dataset
75
+ provider.config.component.update(id: config.as) if config.as
76
+ end
77
+
78
+ provider.config.schema.infer = config.infer
79
+
80
+ super
81
+ end
82
+ # rubocop:enable Metrics/AbcSize
83
+
84
+ private
85
+
86
+ # Builds a representation of the information needed to create an
87
+ # attribute. It returns a hash with `:type` and `:options` keys.
88
+ #
89
+ # @return [Hash]
90
+ #
91
+ # @see [Schema.build_attribute_info]
92
+ #
93
+ # @api private
94
+ def build_attribute_info(name, type_or_options, options = EMPTY_HASH)
95
+ type, options = if type_or_options.is_a?(::Hash)
96
+ [nil, type_or_options]
97
+ else
98
+ [build_type(type_or_options, options), options]
99
+ end
100
+ ROM::Schema.build_attribute_info(type, **options, name: name)
101
+ end
102
+
103
+ # Builds a type instance from base type and meta options
104
+ #
105
+ # @return [Dry::Types::Type] Type instance
106
+ #
107
+ # @api private
108
+ def build_type(type, options = EMPTY_HASH)
109
+ meta = ROM::Attribute::META_OPTIONS
110
+ .map { |opt| [opt, options[opt]] if options.key?(opt) }
111
+ .compact
112
+ .to_h
113
+
114
+ # TODO: this should be probably moved to rom/compat
115
+ source = ROM::Relation::Name[relation_id, config.dataset]
116
+
117
+ base =
118
+ if options[:read]
119
+ type.meta(source: source, read: options[:read])
120
+ elsif type.optional? && type.meta[:read]
121
+ type.meta(source: source, read: type.meta[:read].optional)
122
+ else
123
+ type.meta(source: source)
124
+ end
125
+
126
+ if meta.empty?
127
+ base
128
+ else
129
+ base.meta(meta)
130
+ end
131
+ end
132
+
133
+ # @api private
134
+ def relation_id
135
+ relation? ? provider.config.component.id : config.id
136
+ end
137
+
138
+ # @api private
139
+ def relation?
140
+ provider.config.component.type == :relation
141
+ end
142
+
143
+ # @api private
144
+ def view?
145
+ config.view.equal?(true)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "core"
4
+
5
+ module ROM
6
+ module Components
7
+ module DSL
8
+ # @api private
9
+ class View < Core
10
+ key :views
11
+
12
+ # @api private
13
+ attr_reader :schema_block
14
+
15
+ # @api private
16
+ attr_reader :relation_block
17
+
18
+ # @see Components::DSL#view
19
+ #
20
+ # @api public
21
+ def schema(&block)
22
+ @schema_block = block
23
+ self
24
+ end
25
+
26
+ # @see Components::DSL#view
27
+ #
28
+ # @api public
29
+ def relation(&block)
30
+ @relation_block = block
31
+ self
32
+ end
33
+
34
+ # @api private
35
+ def call
36
+ # Nest view under relation ns
37
+ config.join!({namespace: relation_id}, :right)
38
+
39
+ if args.empty? && block.arity.positive?
40
+ raise ArgumentError, "schema attribute names must be provided as the second argument"
41
+ end
42
+
43
+ # Capture schema and relation blocks if there are no args
44
+ # otherwise assume args is a list of attributes to project
45
+ if args.empty? && block
46
+ instance_eval(&block)
47
+ else
48
+ schema { schema.project(*args.first) }
49
+ end
50
+
51
+ provider.schema(
52
+ id: config.id,
53
+ namespace: relation_id,
54
+ relation: relation_id,
55
+ view: true,
56
+ &schema_block
57
+ )
58
+
59
+ components.add(
60
+ key,
61
+ config: config,
62
+ relation_id: relation_id,
63
+ # Default to the block because we assume the schema was set based on args
64
+ relation_block: relation_block || block
65
+ )
66
+ end
67
+
68
+ private
69
+
70
+ # @api private
71
+ def args
72
+ config.args
73
+ end
74
+
75
+ # @api private
76
+ def relation_id
77
+ provider.config.component.id
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/relation/name"
4
+
5
+ require "rom/components/dsl/gateway"
6
+ require "rom/components/dsl/dataset"
7
+ require "rom/components/dsl/schema"
8
+ require "rom/components/dsl/relation"
9
+ require "rom/components/dsl/view"
10
+ require "rom/components/dsl/association"
11
+ require "rom/components/dsl/command"
12
+ require "rom/components/dsl/mapper"
13
+
14
+ module ROM
15
+ # This extends Configuration class with the DSL methods
16
+ #
17
+ # @api public
18
+ module Components
19
+ module DSL
20
+ # Set or get custom dataset block
21
+ #
22
+ # This block will be evaluated when a relation is instantiated and registered
23
+ # in a relation registry.
24
+ #
25
+ # @example
26
+ # class Users < ROM::Relation[:memory]
27
+ # dataset { sort_by(:id) }
28
+ # end
29
+ #
30
+ # @api public
31
+ def dataset(id = nil, **options, &block)
32
+ __dsl__(DSL::Dataset, id: id, **options, &block)
33
+ end
34
+
35
+ # Specify a relation schema
36
+ #
37
+ # With a schema defined commands will set up a type-safe input handler automatically
38
+ #
39
+ # @example
40
+ # class Users < ROM::Relation[:sql]
41
+ # schema do
42
+ # attribute :id, Types::Serial
43
+ # attribute :name, Types::String
44
+ # end
45
+ # end
46
+ #
47
+ # # access schema from a finalized relation
48
+ # users.schema
49
+ #
50
+ # @return [Schema]
51
+ #
52
+ # @param [Symbol] dataset An optional dataset name
53
+ # @param [Boolean] infer Whether to do an automatic schema inferring
54
+ # @param [Boolean, Symbol] view Whether this is a view schema
55
+ #
56
+ # @api public
57
+ def schema(id = nil, **options, &block)
58
+ __dsl__(DSL::Schema, id: id, dataset: id, **options, &block)
59
+ end
60
+
61
+ # Relation definition DSL
62
+ #
63
+ # @example
64
+ # setup.relation(:users) do
65
+ # def names
66
+ # project(:name)
67
+ # end
68
+ # end
69
+ #
70
+ # @api public
71
+ def relation(id, dataset: id, **options, &block)
72
+ __dsl__(DSL::Relation, id: id, dataset: dataset, **options, &block)
73
+ end
74
+
75
+ # Define a relation view with a specific schema
76
+ #
77
+ # This method should only be used in cases where a given adapter doesn't
78
+ # support automatic schema projection at run-time.
79
+ #
80
+ # @overload view(name, schema, &block)
81
+ # @example View with the canonical schema
82
+ # class Users < ROM::Relation[:sql]
83
+ # view(:listing, schema) do
84
+ # order(:name)
85
+ # end
86
+ # end
87
+ #
88
+ # @example View with a projected schema
89
+ # class Users < ROM::Relation[:sql]
90
+ # view(:listing, schema.project(:id, :name)) do
91
+ # order(:name)
92
+ # end
93
+ # end
94
+ #
95
+ # @overload view(name, &block)
96
+ # @example View with the canonical schema and arguments
97
+ # class Users < ROM::Relation[:sql]
98
+ # view(:by_name) do |name|
99
+ # where(name: name)
100
+ # end
101
+ # end
102
+ #
103
+ # @example View with projected schema and arguments
104
+ # class Users < ROM::Relation[:sql]
105
+ # view(:by_name) do
106
+ # schema { project(:id, :name) }
107
+ # relation { |name| where(name: name) }
108
+ # end
109
+ # end
110
+ #
111
+ # @example View with a schema extended with foreign attributes
112
+ # class Users < ROM::Relation[:sql]
113
+ # view(:index) do
114
+ # schema { append(relations[:tasks][:title]) }
115
+ # relation { |name| where(name: name) }
116
+ # end
117
+ # end
118
+ #
119
+ # @return [Symbol] view method name
120
+ #
121
+ # @api public
122
+ def view(id, *args, &block)
123
+ __dsl__(DSL::View, id: id, args: args, &block)
124
+ id
125
+ end
126
+
127
+ # Define associations for a relation
128
+ #
129
+ # @example
130
+ # class Users < ROM::Relation[:sql]
131
+ # associations do
132
+ # has_many :tasks
133
+ # has_many :posts
134
+ # has_many :posts, as: :priority_posts, view: :prioritized
135
+ # belongs_to :account
136
+ # end
137
+ # end
138
+ #
139
+ # class Posts < ROM::Relation[:sql]
140
+ # associations do
141
+ # belongs_to :users, as: :author
142
+ # end
143
+ #
144
+ # view(:prioritized) do
145
+ # where { priority <= 3 }
146
+ # end
147
+ # end
148
+ #
149
+ # @return [Array<Components::Association>]
150
+ #
151
+ # @api public
152
+ def associations(source: config.component.id, namespace: source, **options, &block)
153
+ __dsl__(DSL::Association, source: source, namespace: namespace, **options, &block)
154
+ components.associations
155
+ end
156
+
157
+ # Command definition DSL
158
+ #
159
+ # @example
160
+ # setup.commands(:users) do
161
+ # define(:create) do
162
+ # input NewUserParams
163
+ # result :one
164
+ # end
165
+ #
166
+ # define(:update) do
167
+ # input UserParams
168
+ # result :many
169
+ # end
170
+ #
171
+ # define(:delete) do
172
+ # result :many
173
+ # end
174
+ # end
175
+ #
176
+ # @api public
177
+ def commands(namespace, **options, &block)
178
+ __dsl__(DSL::Command, namespace: namespace, relation: namespace, **options, &block)
179
+ components.commands
180
+ end
181
+
182
+ # Mapper definition DSL
183
+ #
184
+ # @api public
185
+ def mappers(namespace = nil, **options, &block)
186
+ __dsl__(DSL::Mapper, namespace: namespace, relation: namespace, **options, &block)
187
+ components.mappers
188
+ end
189
+
190
+ # Configures a plugin for a specific adapter to be enabled for all relations
191
+ #
192
+ # @example
193
+ # setup = ROM::Setup.new(:sql, 'sqlite::memory')
194
+ #
195
+ # setup.plugin(:sql, relations: :instrumentation) do |p|
196
+ # p.notifications = MyNotificationsBackend
197
+ # end
198
+ #
199
+ # setup.plugin(:sql, relations: :pagination)
200
+ #
201
+ # @param [Symbol] adapter The adapter identifier
202
+ # @param [Hash<Symbol=>Symbol>] spec Component identifier => plugin identifier
203
+ #
204
+ # @return [Plugin]
205
+ #
206
+ # @api public
207
+ def plugin(*args, &block)
208
+ case args.size
209
+ when 2
210
+ adapter, spec = args
211
+ type, name = spec.flatten(1)
212
+
213
+ # TODO: plugin types are singularized, so this is not consistent
214
+ # with the configuration DSL for plugins that uses plural
215
+ # names of the components - this should be unified
216
+ plugin = ROM
217
+ .plugins[Inflector.singularize(type)].adapter(adapter).fetch(name)
218
+ .configure(&block)
219
+
220
+ config.component.plugins << plugin
221
+
222
+ plugin
223
+ when 1
224
+ plugin(self.adapter, *args)
225
+ else
226
+ raise ArgumentError, "+plugin+ accepts either 1 or 2 arguments (#{args.size} given)"
227
+ end
228
+ end
229
+
230
+ # @api public
231
+ def gateway(id, **options, &block)
232
+ __dsl__(DSL::Gateway, id: id, **options, &block)
233
+ end
234
+
235
+ private
236
+
237
+ # @api private
238
+ def __dsl__(klass, type: klass.type, **options, &block)
239
+ type_config = config[type].join(options, :right).inherit!(config.component)
240
+
241
+ if klass.nested && block
242
+ dsl = klass.new(provider: self, config: type_config)
243
+ dsl.enable_plugins
244
+ dsl.configure
245
+ dsl.instance_eval(&block)
246
+ dsl
247
+ else
248
+ dsl = klass.new(provider: self, config: type_config, block: block)
249
+ dsl.enable_plugins
250
+ dsl.()
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/open_struct"
4
+ require_relative "core"
5
+
6
+ module ROM
7
+ module Components
8
+ # @api public
9
+ class Gateway < Core
10
+ # @api public
11
+ def build
12
+ gateway = adapter.is_a?(ROM::Gateway) ? adapter : setup
13
+
14
+ # TODO: once Gateway is unified with the rest of component types, this
15
+ # won't be needed as it'll happen automatically when inheriting
16
+ # config settings
17
+ gateway_config.plugins.concat(gateway.class.config.component.plugins)
18
+
19
+ gateway.instance_variable_set("@config", gateway_config)
20
+ gateway.use_logger(config.logger) if config.logger
21
+
22
+ gateway
23
+ end
24
+
25
+ # @api private
26
+ def adapter
27
+ config.adapter
28
+ end
29
+
30
+ private
31
+
32
+ # @api private
33
+ def setup
34
+ if config.args.empty?
35
+ ROM::Gateway.setup(adapter, **config)
36
+ else
37
+ ROM::Gateway.setup(adapter, *config.args)
38
+ end
39
+ end
40
+
41
+ # @api private
42
+ def gateway_config
43
+ hash = config.to_h
44
+ keys = hash.keys - %i[type namespace opts]
45
+
46
+ ROM::OpenStruct.new(**keys.zip(hash.values_at(*keys)).to_h, **config.opts)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "core"
4
+
5
+ module ROM
6
+ module Components
7
+ # @api public
8
+ class Mapper < Core
9
+ # @!attribute [r] constant
10
+ # @return [Class] Component's target class
11
+ option :constant, type: Types.Interface(:new), optional: true
12
+
13
+ # @!attribute [r] object
14
+ # @return [Class] Pre-initialized object that should be used instead of the constant
15
+ # @api public
16
+ option :object, optional: true
17
+
18
+ # @api public
19
+ def build
20
+ object || constant.build
21
+ end
22
+
23
+ # @api public
24
+ def relation
25
+ config.relation
26
+ end
27
+ end
28
+ end
29
+ end