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
data/lib/rom/mapper.rb ADDED
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/core"
4
+ require "rom/plugins/class_methods"
5
+
6
+ require_relative "mapper/dsl"
7
+ require_relative "components/provider"
8
+
9
+ module ROM
10
+ # Mapper is a simple object that uses transformers to load relations
11
+ #
12
+ # @private
13
+ class Mapper
14
+ extend ROM::Provider(type: :mapper)
15
+ extend Plugins::ClassMethods
16
+
17
+ include Dry::Equalizer(:transformers, :header)
18
+ include DSL
19
+
20
+ setting :inherit_header, default: true
21
+ setting :reject_keys, default: false
22
+ setting :prefix_separator, default: "_"
23
+ setting :symbolize_keys
24
+ setting :copy_keys
25
+ setting :prefix
26
+
27
+ # @return [Object] transformers object built by a processor
28
+ #
29
+ # @api private
30
+ attr_reader :transformers
31
+
32
+ # @return [Header] header that was used to build the transformers
33
+ #
34
+ # @api private
35
+ attr_reader :header
36
+
37
+ # @return [Hash] registered processors
38
+ #
39
+ # @api private
40
+ def self.processors
41
+ @_processors ||= {}
42
+ end
43
+
44
+ # Register a processor class
45
+ #
46
+ # @return [Hash]
47
+ #
48
+ # @api private
49
+ def self.register_processor(processor)
50
+ name = processor.name.split("::").last.downcase.to_sym
51
+ processors.update(name => processor)
52
+ end
53
+ require "rom/processor/transformer"
54
+
55
+ # Prepares an array of headers for a potentially multistep mapper
56
+ #
57
+ # @return [Array<Header>]
58
+ #
59
+ # @api private
60
+ def self.headers(header)
61
+ return [header] if steps.empty?
62
+ return steps.map(&:header) if attributes.empty?
63
+
64
+ raise(MapperMisconfiguredError, "cannot mix outer attributes and steps")
65
+ end
66
+
67
+ # Build a mapper using provided processor type
68
+ #
69
+ # @return [Mapper]
70
+ #
71
+ # @api private
72
+ def self.build(header = self.header, processor = :transformer)
73
+ new(header, processor)
74
+ end
75
+
76
+ # @api private
77
+ def initialize(header, processor = :transformer)
78
+ processor = Mapper.processors.fetch(processor)
79
+ @transformers = self.class.headers(header).map do |hdr|
80
+ processor.build(self, hdr)
81
+ end
82
+ @header = header
83
+ end
84
+
85
+ # @return [Class] optional model that is instantiated by a mapper
86
+ #
87
+ # @api private
88
+ def model
89
+ header.model
90
+ end
91
+
92
+ # Process a relation using the transformers
93
+ #
94
+ # @api private
95
+ def call(relation)
96
+ transformers.reduce(relation.to_a) { |a, e| e.call(a) }
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/class_attributes"
4
+
5
+ require "rom/constants"
6
+ require "rom/initializer"
7
+ require "rom/struct"
8
+ require "rom/struct_compiler"
9
+ require "rom/cache"
10
+
11
+ module ROM
12
+ # @api private
13
+ class MapperCompiler
14
+ extend Dry::Core::ClassAttributes
15
+ extend Initializer
16
+
17
+ defines :mapper_options
18
+
19
+ mapper_options(EMPTY_HASH)
20
+
21
+ option :cache, default: -> { Cache.new }
22
+
23
+ attr_reader :struct_compiler
24
+
25
+ attr_reader :mapper_options
26
+
27
+ def initialize(*)
28
+ super
29
+ @struct_compiler = StructCompiler.new(cache: cache)
30
+ @cache = cache.namespaced(:mappers)
31
+ @mapper_options = self.class.mapper_options
32
+ end
33
+ ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
34
+
35
+ def call(ast)
36
+ cache.fetch_or_store(ast.hash) { Mapper.build(Header.coerce(*visit(ast))) }
37
+ end
38
+ alias_method :[], :call
39
+
40
+ private
41
+
42
+ def visit(node)
43
+ name, node = node
44
+ __send__("visit_#{name}", node)
45
+ end
46
+
47
+ def visit_relation(node)
48
+ rel_name, header, meta_options = node
49
+ name = meta_options[:combine_name] || meta_options[:alias] || rel_name
50
+ namespace = meta_options.fetch(:struct_namespace)
51
+
52
+ model = meta_options.fetch(:model) do
53
+ if meta_options[:combine_name]
54
+ false
55
+ else
56
+ struct_compiler[name, header, namespace]
57
+ end
58
+ end
59
+
60
+ options = [header.map(&method(:visit)), mapper_options.merge(model: model)]
61
+
62
+ if meta_options[:combine_type]
63
+ type = meta_options[:combine_type] == :many ? :array : :hash
64
+ keys = meta_options.fetch(:keys)
65
+
66
+ [name, {combine: true, type: type, keys: keys, header: Header.coerce(*options)}]
67
+ elsif meta_options[:wrap]
68
+ [name, {wrap: true, type: :hash, header: Header.coerce(*options)}]
69
+ else
70
+ options
71
+ end
72
+ end
73
+
74
+ def visit_attribute(node)
75
+ name, _, meta_options = node
76
+
77
+ if meta_options[:alias]
78
+ [meta_options[:alias], {from: name}]
79
+ else
80
+ [name]
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/associations/many_to_many"
4
+
5
+ module ROM
6
+ module Memory
7
+ module Associations
8
+ class ManyToMany < ROM::Associations::ManyToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/associations/many_to_one"
4
+
5
+ module ROM
6
+ module Memory
7
+ module Associations
8
+ class ManyToOne < ROM::Associations::ManyToOne
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/associations/one_to_many"
4
+
5
+ module ROM
6
+ module Memory
7
+ module Associations
8
+ class OneToMany < ROM::Associations::OneToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/memory/associations/one_to_many"
4
+
5
+ module ROM
6
+ module Memory
7
+ module Associations
8
+ class OneToOne < OneToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/memory/associations/many_to_many"
4
+ require "rom/memory/associations/many_to_one"
5
+ require "rom/memory/associations/one_to_many"
6
+ require "rom/memory/associations/one_to_one"
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/commands"
4
+
5
+ module ROM
6
+ module Memory
7
+ # Memory adapter commands namespace
8
+ #
9
+ # @api public
10
+ module Commands
11
+ # In-memory create command
12
+ #
13
+ # @api public
14
+ class Create < ROM::Commands::Create
15
+ config.component.adapter = :memory
16
+
17
+ use :schema
18
+
19
+ # @see ROM::Commands::Create#execute
20
+ def execute(tuples)
21
+ Array([tuples]).flatten.map { |tuple|
22
+ attributes = input[tuple]
23
+ relation.insert(attributes.to_h)
24
+ attributes
25
+ }.to_a
26
+ end
27
+ end
28
+
29
+ # In-memory update command
30
+ #
31
+ # @api public
32
+ class Update < ROM::Commands::Update
33
+ config.component.adapter = :memory
34
+
35
+ use :schema
36
+
37
+ # @see ROM::Commands::Update#execute
38
+ def execute(params)
39
+ attributes = input[params]
40
+ relation.map { |tuple| tuple.update(attributes.to_h) }
41
+ end
42
+ end
43
+
44
+ # In-memory delete command
45
+ #
46
+ # @api public
47
+ class Delete < ROM::Commands::Delete
48
+ config.component.adapter = :memory
49
+
50
+ # @see ROM::Commands::Delete#execute
51
+ def execute
52
+ relation.to_a.map do |tuple|
53
+ source.delete(tuple)
54
+ tuple
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/array_dataset"
4
+
5
+ module ROM
6
+ module Memory
7
+ # In-memory dataset
8
+ #
9
+ # This class can be used as a base class for other adapters.
10
+ #
11
+ # @api public
12
+ class Dataset
13
+ include ArrayDataset
14
+
15
+ # Join with other datasets
16
+ #
17
+ # @param [Array<Dataset>] args A list of dataset to join with
18
+ #
19
+ # @return [Dataset]
20
+ #
21
+ # @api public
22
+ def join(*args)
23
+ left, right = args.size > 1 ? args : [self, args.first]
24
+
25
+ join_map = left.each_with_object({}) { |tuple, h|
26
+ others = right.to_a.find_all { |t| (tuple.to_a & t.to_a).any? }
27
+ (h[tuple] ||= []).concat(others)
28
+ }
29
+
30
+ tuples = left.flat_map { |tuple|
31
+ join_map[tuple].map { |other| tuple.merge(other) }
32
+ }
33
+
34
+ self.class.new(tuples, **options)
35
+ end
36
+
37
+ # Restrict a dataset
38
+ #
39
+ # @param [Hash] criteria A hash with conditions
40
+ #
41
+ # @return [Dataset]
42
+ #
43
+ # @api public
44
+ def restrict(criteria = nil, &block)
45
+ return find_all(&block) unless criteria
46
+
47
+ find_all do |tuple|
48
+ criteria.all? do |k, v|
49
+ case v
50
+ when Array then v.include?(tuple[k])
51
+ when Regexp then tuple[k].match(v)
52
+ else tuple[k].eql?(v)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Project a dataset
59
+ #
60
+ # @param [Array<Symbol>] names A list of attribute names
61
+ #
62
+ # @return [Dataset]
63
+ #
64
+ # @api public
65
+ def project(*names)
66
+ map { |tuple| tuple.select { |key| names.include?(key) } }
67
+ end
68
+
69
+ # Sort a dataset
70
+ #
71
+ # @param [Array<Symbol>] fields
72
+ # Names of fields to order tuples by
73
+ #
74
+ # @option [Boolean] :nils_first (false)
75
+ # Whether `nil` values should be placed before others
76
+ #
77
+ # @return [Dataset]
78
+ #
79
+ # @api public
80
+ def order(*fields)
81
+ nils_first = fields.pop[:nils_first] if fields.last.is_a?(Hash)
82
+
83
+ sort do |a, b|
84
+ fields # finds the first difference between selected fields of tuples
85
+ .map { |n| __compare__ a[n], b[n], nils_first }
86
+ .detect(-> { 0 }) { |r| r != 0 }
87
+ end
88
+ end
89
+
90
+ # Insert tuple into a dataset
91
+ #
92
+ # @param [Hash] tuple A new tuple for insertion
93
+ #
94
+ # @api public
95
+ #
96
+ # @return [Dataset]
97
+ def insert(tuple)
98
+ data << tuple
99
+ self
100
+ end
101
+ alias_method :<<, :insert
102
+
103
+ # Delete tuples from a dataset
104
+ #
105
+ # @param [Hash] tuple A new tuple for deletion
106
+ #
107
+ # @return [Dataset]
108
+ #
109
+ # @api public
110
+ def delete(tuple)
111
+ data.delete(tuple)
112
+ self
113
+ end
114
+
115
+ private
116
+
117
+ # Compares two values, that are either comparable, or can be nils
118
+ #
119
+ # @api private
120
+ def __compare__(a, b, nils_first)
121
+ return a <=> b unless a.nil? ^ b.nil?
122
+
123
+ nils_first ^ b.nil? ? -1 : 1
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/gateway"
4
+ require "rom/memory/storage"
5
+ require "rom/memory/commands"
6
+
7
+ module ROM
8
+ module Memory
9
+ # In-memory gateway interface
10
+ #
11
+ # @example
12
+ # gateway = ROM::Memory::Gateway.new
13
+ # gateway.dataset(:users)
14
+ # gateway[:users].insert(name: 'Jane')
15
+ #
16
+ # @api public
17
+ class Gateway < ROM::Gateway
18
+ adapter :memory
19
+
20
+ # @return [Object] default logger
21
+ #
22
+ # @api public
23
+ attr_reader :logger
24
+
25
+ # @api private
26
+ def initialize
27
+ @connection = Storage.new
28
+ end
29
+
30
+ # Set default logger for the gateway
31
+ #
32
+ # @param [Object] logger object
33
+ #
34
+ # @api public
35
+ def use_logger(logger)
36
+ @logger = logger
37
+ end
38
+
39
+ # Register a dataset in the gateway
40
+ #
41
+ # If dataset already exists it will be returned
42
+ #
43
+ # @return [Dataset]
44
+ #
45
+ # @api public
46
+ def dataset(name)
47
+ self[name] || connection.create_dataset(name)
48
+ end
49
+
50
+ # @see ROM::Gateway#dataset?
51
+ def dataset?(name)
52
+ connection.key?(name)
53
+ end
54
+
55
+ # Return dataset with the given name
56
+ #
57
+ # @param (see ROM::Gateway#[])
58
+ # @return [Memory::Dataset]
59
+ #
60
+ # @api public
61
+ def [](name)
62
+ connection[name]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/mapper_compiler"
4
+
5
+ module ROM
6
+ module Memory
7
+ class MapperCompiler < ROM::MapperCompiler
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/relation"
4
+ require "rom/memory/types"
5
+ require "rom/memory/schema"
6
+
7
+ module ROM
8
+ module Memory
9
+ # Relation subclass for memory adapter
10
+ #
11
+ # @example
12
+ # class Users < ROM::Relation[:memory]
13
+ # end
14
+ #
15
+ # @api public
16
+ class Relation < ROM::Relation
17
+ include Enumerable
18
+ include Memory
19
+
20
+ config.component.adapter = :memory
21
+ config.schema.constant = Memory::Schema
22
+
23
+ # @!method take(amount)
24
+ # @param (see Dataset#take)
25
+ # @return [Relation]
26
+ # @see Dataset#take
27
+ #
28
+ # @!method join(*args)
29
+ # @param (see Dataset#take)
30
+ # @return [Relation]
31
+ # @see Dataset#join
32
+ #
33
+ # @!method restrict(criteria = nil)
34
+ # @param (see Dataset#restrict)
35
+ # @return [Relation]
36
+ # @see Dataset#restrict
37
+ #
38
+ # @!method order(*fields)
39
+ # @param (see Dataset#order)
40
+ # @return [Relation]
41
+ # @see Dataset#order
42
+ forward :take, :join, :restrict, :order
43
+
44
+ # Project a relation with provided attribute names
45
+ #
46
+ # @param [*Array] names A list with attribute names
47
+ #
48
+ # @return [Memory::Relation]
49
+ #
50
+ # @api public
51
+ def project(*names)
52
+ schema.project(*names).(self)
53
+ end
54
+
55
+ # Rename attributes in a relation
56
+ #
57
+ # @api public
58
+ def rename(mapping)
59
+ schema.rename(mapping).(self)
60
+ end
61
+
62
+ # Insert tuples into the relation
63
+ #
64
+ # @example
65
+ # users.insert(name: 'Jane')
66
+ #
67
+ # @return [Relation]
68
+ #
69
+ # @api public
70
+ def insert(*args)
71
+ dataset.insert(*args)
72
+ self
73
+ end
74
+ alias_method :<<, :insert
75
+
76
+ # Delete tuples from the relation
77
+ #
78
+ # @example
79
+ # users.insert(name: 'Jane')
80
+ # users.delete(name: 'Jane')
81
+ #
82
+ # @return [Relation]
83
+ #
84
+ # @api public
85
+ def delete(*args)
86
+ dataset.delete(*args)
87
+ self
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/schema"
4
+ require "rom/memory/associations"
5
+
6
+ module ROM
7
+ module Memory
8
+ # Specialized schema for memory adapter
9
+ #
10
+ # @api public
11
+ class Schema < ROM::Schema
12
+ # @see Schema#call
13
+ # @api public
14
+ def call(relation)
15
+ relation.new(relation.dataset.project(*map(&:name)), schema: self)
16
+ end
17
+
18
+ # Internal hook used during setup process
19
+ #
20
+ # @see Schema#finalize_associations!
21
+ #
22
+ # @api private
23
+ def finalize_associations!(relations:)
24
+ super do
25
+ associations.map do |definition|
26
+ Memory::Associations.const_get(definition.type).new(definition, relations)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/hash"
4
+ require "concurrent/array"
5
+
6
+ require "rom/memory/dataset"
7
+
8
+ module ROM
9
+ module Memory
10
+ # In-memory thread-safe data storage
11
+ #
12
+ # @private
13
+ class Storage
14
+ # Dataset registry
15
+ #
16
+ # @return [ThreadSafe::Hash]
17
+ #
18
+ # @api private
19
+ attr_reader :data
20
+
21
+ # @api private
22
+ def initialize
23
+ @data = Concurrent::Hash.new
24
+ end
25
+
26
+ # @return [Dataset]
27
+ #
28
+ # @api private
29
+ def [](name)
30
+ data[name]
31
+ end
32
+
33
+ # Register a new dataset
34
+ #
35
+ # @return [Dataset]
36
+ #
37
+ # @api private
38
+ def create_dataset(name)
39
+ data[name] = Dataset.new(Concurrent::Array.new)
40
+ end
41
+
42
+ # Check if there's dataset under specified key
43
+ #
44
+ # @return [Boolean]
45
+ #
46
+ # @api private
47
+ def key?(name)
48
+ data.key?(name)
49
+ end
50
+
51
+ # Return registered datasets count
52
+ #
53
+ # @return [Integer]
54
+ #
55
+ # @api private
56
+ def size
57
+ data.size
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/types"
4
+
5
+ module ROM
6
+ module Memory
7
+ module Types
8
+ include ROM::Types
9
+ end
10
+ end
11
+ end