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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -65
- data/LICENSE +1 -1
- data/README.md +7 -6
- data/lib/rom/array_dataset.rb +46 -0
- data/lib/rom/associations/abstract.rb +217 -0
- data/lib/rom/associations/definitions/abstract.rb +150 -0
- data/lib/rom/associations/definitions/many_to_many.rb +29 -0
- data/lib/rom/associations/definitions/many_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_many.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
- data/lib/rom/associations/definitions.rb +7 -0
- data/lib/rom/associations/many_to_many.rb +128 -0
- data/lib/rom/associations/many_to_one.rb +65 -0
- data/lib/rom/associations/one_to_many.rb +65 -0
- data/lib/rom/associations/one_to_one.rb +13 -0
- data/lib/rom/associations/one_to_one_through.rb +13 -0
- data/lib/rom/associations/through_identifier.rb +41 -0
- data/lib/rom/attribute.rb +425 -0
- data/lib/rom/auto_curry.rb +70 -0
- data/lib/rom/cache.rb +87 -0
- data/lib/rom/changeset/associated.rb +110 -0
- data/lib/rom/changeset/create.rb +18 -0
- data/lib/rom/changeset/delete.rb +15 -0
- data/lib/rom/changeset/extensions/relation.rb +26 -0
- data/lib/rom/changeset/pipe.rb +81 -0
- data/lib/rom/changeset/pipe_registry.rb +27 -0
- data/lib/rom/changeset/stateful.rb +285 -0
- data/lib/rom/changeset/update.rb +81 -0
- data/lib/rom/changeset.rb +185 -0
- data/lib/rom/command.rb +351 -0
- data/lib/rom/command_compiler.rb +201 -0
- data/lib/rom/command_proxy.rb +36 -0
- data/lib/rom/commands/class_interface.rb +236 -0
- data/lib/rom/commands/composite.rb +55 -0
- data/lib/rom/commands/create.rb +15 -0
- data/lib/rom/commands/delete.rb +16 -0
- data/lib/rom/commands/graph/class_interface.rb +64 -0
- data/lib/rom/commands/graph/input_evaluator.rb +94 -0
- data/lib/rom/commands/graph.rb +88 -0
- data/lib/rom/commands/lazy/create.rb +35 -0
- data/lib/rom/commands/lazy/delete.rb +39 -0
- data/lib/rom/commands/lazy/update.rb +46 -0
- data/lib/rom/commands/lazy.rb +106 -0
- data/lib/rom/commands/update.rb +16 -0
- data/lib/rom/commands.rb +5 -0
- data/lib/rom/compat/auto_registration.rb +115 -0
- data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
- data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
- data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
- data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
- data/lib/rom/compat/command.rb +74 -0
- data/lib/rom/compat/components/dsl/schema.rb +130 -0
- data/lib/rom/compat/components.rb +91 -0
- data/lib/rom/compat/global.rb +17 -0
- data/lib/rom/compat/mapper.rb +22 -0
- data/lib/rom/compat/registries.rb +47 -0
- data/lib/rom/compat/relation.rb +40 -0
- data/lib/rom/compat/schema/dsl.rb +260 -0
- data/lib/rom/compat/setting_proxy.rb +44 -0
- data/lib/rom/compat/setup.rb +151 -0
- data/lib/rom/compat/transformer.rb +49 -0
- data/lib/rom/compat.rb +22 -0
- data/lib/rom/components/association.rb +26 -0
- data/lib/rom/components/command.rb +24 -0
- data/lib/rom/components/core.rb +148 -0
- data/lib/rom/components/dataset.rb +60 -0
- data/lib/rom/components/dsl/association.rb +47 -0
- data/lib/rom/components/dsl/command.rb +60 -0
- data/lib/rom/components/dsl/core.rb +126 -0
- data/lib/rom/components/dsl/dataset.rb +33 -0
- data/lib/rom/components/dsl/gateway.rb +14 -0
- data/lib/rom/components/dsl/mapper.rb +70 -0
- data/lib/rom/components/dsl/relation.rb +49 -0
- data/lib/rom/components/dsl/schema.rb +150 -0
- data/lib/rom/components/dsl/view.rb +82 -0
- data/lib/rom/components/dsl.rb +255 -0
- data/lib/rom/components/gateway.rb +50 -0
- data/lib/rom/components/mapper.rb +29 -0
- data/lib/rom/components/provider.rb +160 -0
- data/lib/rom/components/registry.rb +154 -0
- data/lib/rom/components/relation.rb +41 -0
- data/lib/rom/components/schema.rb +61 -0
- data/lib/rom/components/view.rb +55 -0
- data/lib/rom/components.rb +55 -0
- data/lib/rom/configuration_dsl.rb +4 -0
- data/lib/rom/constants.rb +135 -0
- data/lib/rom/container.rb +182 -0
- data/lib/rom/core.rb +125 -0
- data/lib/rom/data_proxy.rb +97 -0
- data/lib/rom/enumerable_dataset.rb +70 -0
- data/lib/rom/gateway.rb +232 -0
- data/lib/rom/global.rb +56 -0
- data/lib/rom/header/attribute.rb +190 -0
- data/lib/rom/header.rb +198 -0
- data/lib/rom/inferrer.rb +55 -0
- data/lib/rom/initializer.rb +80 -0
- data/lib/rom/lint/enumerable_dataset.rb +56 -0
- data/lib/rom/lint/gateway.rb +120 -0
- data/lib/rom/lint/linter.rb +79 -0
- data/lib/rom/lint/spec.rb +22 -0
- data/lib/rom/lint/test.rb +98 -0
- data/lib/rom/loader.rb +161 -0
- data/lib/rom/mapper/attribute_dsl.rb +480 -0
- data/lib/rom/mapper/dsl.rb +107 -0
- data/lib/rom/mapper/model_dsl.rb +61 -0
- data/lib/rom/mapper.rb +99 -0
- data/lib/rom/mapper_compiler.rb +84 -0
- data/lib/rom/memory/associations/many_to_many.rb +12 -0
- data/lib/rom/memory/associations/many_to_one.rb +12 -0
- data/lib/rom/memory/associations/one_to_many.rb +12 -0
- data/lib/rom/memory/associations/one_to_one.rb +12 -0
- data/lib/rom/memory/associations.rb +6 -0
- data/lib/rom/memory/commands.rb +60 -0
- data/lib/rom/memory/dataset.rb +127 -0
- data/lib/rom/memory/gateway.rb +66 -0
- data/lib/rom/memory/mapper_compiler.rb +10 -0
- data/lib/rom/memory/relation.rb +91 -0
- data/lib/rom/memory/schema.rb +32 -0
- data/lib/rom/memory/storage.rb +61 -0
- data/lib/rom/memory/types.rb +11 -0
- data/lib/rom/memory.rb +7 -0
- data/lib/rom/model_builder.rb +103 -0
- data/lib/rom/open_struct.rb +112 -0
- data/lib/rom/pipeline.rb +111 -0
- data/lib/rom/plugin.rb +130 -0
- data/lib/rom/plugins/class_methods.rb +37 -0
- data/lib/rom/plugins/command/schema.rb +45 -0
- data/lib/rom/plugins/command/timestamps.rb +149 -0
- data/lib/rom/plugins/dsl.rb +53 -0
- data/lib/rom/plugins/relation/changeset.rb +97 -0
- data/lib/rom/plugins/relation/instrumentation.rb +66 -0
- data/lib/rom/plugins/relation/registry_reader.rb +36 -0
- data/lib/rom/plugins/schema/timestamps.rb +59 -0
- data/lib/rom/plugins.rb +100 -0
- data/lib/rom/processor/composer.rb +37 -0
- data/lib/rom/processor/transformer.rb +415 -0
- data/lib/rom/processor.rb +30 -0
- data/lib/rom/registries/associations.rb +26 -0
- data/lib/rom/registries/commands.rb +11 -0
- data/lib/rom/registries/container.rb +12 -0
- data/lib/rom/registries/datasets.rb +21 -0
- data/lib/rom/registries/gateways.rb +8 -0
- data/lib/rom/registries/mappers.rb +21 -0
- data/lib/rom/registries/nestable.rb +32 -0
- data/lib/rom/registries/relations.rb +8 -0
- data/lib/rom/registries/root.rb +203 -0
- data/lib/rom/registries/schemas.rb +44 -0
- data/lib/rom/registries/views.rb +11 -0
- data/lib/rom/relation/class_interface.rb +61 -0
- data/lib/rom/relation/combined.rb +160 -0
- data/lib/rom/relation/commands.rb +65 -0
- data/lib/rom/relation/composite.rb +53 -0
- data/lib/rom/relation/curried.rb +129 -0
- data/lib/rom/relation/graph.rb +107 -0
- data/lib/rom/relation/loaded.rb +136 -0
- data/lib/rom/relation/materializable.rb +62 -0
- data/lib/rom/relation/name.rb +122 -0
- data/lib/rom/relation/wrap.rb +64 -0
- data/lib/rom/relation.rb +625 -0
- data/lib/rom/repository/class_interface.rb +162 -0
- data/lib/rom/repository/relation_reader.rb +48 -0
- data/lib/rom/repository/root.rb +75 -0
- data/lib/rom/repository/session.rb +60 -0
- data/lib/rom/repository.rb +179 -0
- data/lib/rom/schema/associations_dsl.rb +222 -0
- data/lib/rom/schema/inferrer.rb +106 -0
- data/lib/rom/schema.rb +471 -0
- data/lib/rom/settings.rb +141 -0
- data/lib/rom/setup.rb +297 -0
- data/lib/rom/struct.rb +99 -0
- data/lib/rom/struct_compiler.rb +114 -0
- data/lib/rom/support/configurable.rb +213 -0
- data/lib/rom/support/inflector.rb +31 -0
- data/lib/rom/support/memoizable.rb +61 -0
- data/lib/rom/support/notifications.rb +238 -0
- data/lib/rom/transaction.rb +26 -0
- data/lib/rom/transformer.rb +46 -0
- data/lib/rom/types.rb +74 -0
- data/lib/rom/version.rb +1 -1
- data/lib/rom-changeset.rb +4 -0
- data/lib/rom-core.rb +3 -0
- data/lib/rom-repository.rb +4 -0
- data/lib/rom.rb +3 -3
- 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,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,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
|