rom-core 4.0.0.beta1

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 (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +603 -0
  3. data/LICENSE +20 -0
  4. data/README.md +18 -0
  5. data/lib/rom-core.rb +1 -0
  6. data/lib/rom/array_dataset.rb +44 -0
  7. data/lib/rom/association_set.rb +16 -0
  8. data/lib/rom/associations/abstract.rb +135 -0
  9. data/lib/rom/associations/definitions.rb +5 -0
  10. data/lib/rom/associations/definitions/abstract.rb +116 -0
  11. data/lib/rom/associations/definitions/many_to_many.rb +24 -0
  12. data/lib/rom/associations/definitions/many_to_one.rb +11 -0
  13. data/lib/rom/associations/definitions/one_to_many.rb +11 -0
  14. data/lib/rom/associations/definitions/one_to_one.rb +11 -0
  15. data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
  16. data/lib/rom/associations/many_to_many.rb +81 -0
  17. data/lib/rom/associations/many_to_one.rb +37 -0
  18. data/lib/rom/associations/one_to_many.rb +37 -0
  19. data/lib/rom/associations/one_to_one.rb +8 -0
  20. data/lib/rom/associations/one_to_one_through.rb +8 -0
  21. data/lib/rom/associations/through_identifier.rb +39 -0
  22. data/lib/rom/auto_curry.rb +55 -0
  23. data/lib/rom/cache.rb +46 -0
  24. data/lib/rom/command.rb +488 -0
  25. data/lib/rom/command_compiler.rb +239 -0
  26. data/lib/rom/command_proxy.rb +24 -0
  27. data/lib/rom/command_registry.rb +141 -0
  28. data/lib/rom/commands.rb +3 -0
  29. data/lib/rom/commands/class_interface.rb +270 -0
  30. data/lib/rom/commands/composite.rb +53 -0
  31. data/lib/rom/commands/create.rb +13 -0
  32. data/lib/rom/commands/delete.rb +14 -0
  33. data/lib/rom/commands/graph.rb +88 -0
  34. data/lib/rom/commands/graph/class_interface.rb +62 -0
  35. data/lib/rom/commands/graph/input_evaluator.rb +62 -0
  36. data/lib/rom/commands/lazy.rb +99 -0
  37. data/lib/rom/commands/lazy/create.rb +23 -0
  38. data/lib/rom/commands/lazy/delete.rb +27 -0
  39. data/lib/rom/commands/lazy/update.rb +34 -0
  40. data/lib/rom/commands/result.rb +96 -0
  41. data/lib/rom/commands/update.rb +14 -0
  42. data/lib/rom/configuration.rb +114 -0
  43. data/lib/rom/configuration_dsl.rb +87 -0
  44. data/lib/rom/configuration_dsl/command.rb +41 -0
  45. data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
  46. data/lib/rom/configuration_dsl/relation.rb +26 -0
  47. data/lib/rom/configuration_plugin.rb +17 -0
  48. data/lib/rom/constants.rb +64 -0
  49. data/lib/rom/container.rb +147 -0
  50. data/lib/rom/core.rb +46 -0
  51. data/lib/rom/create_container.rb +60 -0
  52. data/lib/rom/data_proxy.rb +94 -0
  53. data/lib/rom/enumerable_dataset.rb +68 -0
  54. data/lib/rom/environment.rb +70 -0
  55. data/lib/rom/gateway.rb +184 -0
  56. data/lib/rom/global.rb +58 -0
  57. data/lib/rom/global/plugin_dsl.rb +47 -0
  58. data/lib/rom/initializer.rb +64 -0
  59. data/lib/rom/lint/enumerable_dataset.rb +54 -0
  60. data/lib/rom/lint/gateway.rb +120 -0
  61. data/lib/rom/lint/linter.rb +78 -0
  62. data/lib/rom/lint/spec.rb +20 -0
  63. data/lib/rom/lint/test.rb +98 -0
  64. data/lib/rom/mapper_registry.rb +24 -0
  65. data/lib/rom/memory.rb +4 -0
  66. data/lib/rom/memory/associations.rb +4 -0
  67. data/lib/rom/memory/associations/many_to_many.rb +10 -0
  68. data/lib/rom/memory/associations/many_to_one.rb +10 -0
  69. data/lib/rom/memory/associations/one_to_many.rb +10 -0
  70. data/lib/rom/memory/associations/one_to_one.rb +10 -0
  71. data/lib/rom/memory/commands.rb +56 -0
  72. data/lib/rom/memory/dataset.rb +97 -0
  73. data/lib/rom/memory/gateway.rb +64 -0
  74. data/lib/rom/memory/relation.rb +62 -0
  75. data/lib/rom/memory/schema.rb +23 -0
  76. data/lib/rom/memory/storage.rb +59 -0
  77. data/lib/rom/memory/types.rb +9 -0
  78. data/lib/rom/pipeline.rb +105 -0
  79. data/lib/rom/plugin.rb +25 -0
  80. data/lib/rom/plugin_base.rb +45 -0
  81. data/lib/rom/plugin_registry.rb +197 -0
  82. data/lib/rom/plugins/command/schema.rb +37 -0
  83. data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
  84. data/lib/rom/plugins/relation/instrumentation.rb +51 -0
  85. data/lib/rom/plugins/relation/registry_reader.rb +44 -0
  86. data/lib/rom/plugins/schema/timestamps.rb +58 -0
  87. data/lib/rom/registry.rb +71 -0
  88. data/lib/rom/relation.rb +548 -0
  89. data/lib/rom/relation/class_interface.rb +282 -0
  90. data/lib/rom/relation/commands.rb +23 -0
  91. data/lib/rom/relation/composite.rb +46 -0
  92. data/lib/rom/relation/curried.rb +103 -0
  93. data/lib/rom/relation/graph.rb +197 -0
  94. data/lib/rom/relation/loaded.rb +127 -0
  95. data/lib/rom/relation/materializable.rb +66 -0
  96. data/lib/rom/relation/name.rb +111 -0
  97. data/lib/rom/relation/view_dsl.rb +64 -0
  98. data/lib/rom/relation/wrap.rb +83 -0
  99. data/lib/rom/relation_registry.rb +10 -0
  100. data/lib/rom/schema.rb +437 -0
  101. data/lib/rom/schema/associations_dsl.rb +195 -0
  102. data/lib/rom/schema/attribute.rb +419 -0
  103. data/lib/rom/schema/dsl.rb +164 -0
  104. data/lib/rom/schema/inferrer.rb +66 -0
  105. data/lib/rom/schema_plugin.rb +27 -0
  106. data/lib/rom/setup.rb +68 -0
  107. data/lib/rom/setup/auto_registration.rb +74 -0
  108. data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
  109. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
  110. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
  111. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
  112. data/lib/rom/setup/finalize.rb +103 -0
  113. data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
  114. data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
  115. data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
  116. data/lib/rom/support/configurable.rb +85 -0
  117. data/lib/rom/support/memoizable.rb +58 -0
  118. data/lib/rom/support/notifications.rb +103 -0
  119. data/lib/rom/transaction.rb +24 -0
  120. data/lib/rom/types.rb +26 -0
  121. data/lib/rom/version.rb +5 -0
  122. metadata +289 -0
@@ -0,0 +1,97 @@
1
+ require 'rom/array_dataset'
2
+
3
+ module ROM
4
+ module Memory
5
+ # In-memory dataset
6
+ #
7
+ # @api public
8
+ class Dataset
9
+ include ArrayDataset
10
+
11
+ # Join two datasets
12
+ #
13
+ # @api public
14
+ def join(*args)
15
+ left, right = args.size > 1 ? args : [self, args.first]
16
+
17
+ join_map = left.each_with_object({}) { |tuple, h|
18
+ others = right.to_a.find_all { |t| (tuple.to_a & t.to_a).any? }
19
+ (h[tuple] ||= []).concat(others)
20
+ }
21
+
22
+ tuples = left.flat_map { |tuple|
23
+ join_map[tuple].map { |other| tuple.merge(other) }
24
+ }
25
+
26
+ self.class.new(tuples, options)
27
+ end
28
+
29
+ # Restrict a dataset
30
+ #
31
+ # @api public
32
+ def restrict(criteria = nil)
33
+ return find_all { |tuple| yield(tuple) } unless criteria
34
+ find_all do |tuple|
35
+ criteria.all? do |k, v|
36
+ case v
37
+ when Array then v.include?(tuple[k])
38
+ when Regexp then tuple[k].match(v)
39
+ else tuple[k].eql?(v)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # Project a dataset
46
+ #
47
+ # @api public
48
+ def project(*names)
49
+ map { |tuple| tuple.reject { |key| !names.include?(key) } }
50
+ end
51
+
52
+ # Sort a dataset
53
+ #
54
+ # @param [Array<Symbol>] names
55
+ # Names of fields to order tuples by
56
+ #
57
+ # @option [Boolean] :nils_first (false)
58
+ # Whether `nil` values should be placed before others
59
+ #
60
+ # @api public
61
+ def order(*fields)
62
+ nils_first = fields.pop[:nils_first] if fields.last.is_a?(Hash)
63
+
64
+ sort do |a, b|
65
+ fields # finds the first difference between selected fields of tuples
66
+ .map { |n| __compare__ a[n], b[n], nils_first }
67
+ .detect(-> { 0 }) { |r| r != 0 }
68
+ end
69
+ end
70
+
71
+ # Insert tuple into a dataset
72
+ #
73
+ # @api public
74
+ def insert(tuple)
75
+ data << tuple
76
+ self
77
+ end
78
+ alias_method :<<, :insert
79
+
80
+ # Delete tuples from a dataset
81
+ #
82
+ # @api public
83
+ def delete(tuple)
84
+ data.delete(tuple)
85
+ self
86
+ end
87
+
88
+ private
89
+
90
+ # Compares two values, that are either comparable, or can be nils
91
+ def __compare__(a, b, nils_first)
92
+ return a <=> b unless a.nil? ^ b.nil?
93
+ (nils_first ^ b.nil?) ? -1 : 1
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,64 @@
1
+ require 'rom/gateway'
2
+ require 'rom/memory/storage'
3
+ require 'rom/memory/commands'
4
+
5
+ module ROM
6
+ module Memory
7
+ # In-memory gateway interface
8
+ #
9
+ # @example
10
+ # gateway = ROM::Memory::Gateway.new
11
+ # gateway.dataset(:users)
12
+ # gateway[:users].insert(name: 'Jane')
13
+ #
14
+ # @api public
15
+ class Gateway < ROM::Gateway
16
+ adapter :memory
17
+
18
+ # @return [Object] default logger
19
+ #
20
+ # @api public
21
+ attr_reader :logger
22
+
23
+ # @api private
24
+ def initialize
25
+ @connection = Storage.new
26
+ end
27
+
28
+ # Set default logger for the gateway
29
+ #
30
+ # @param [Object] logger object
31
+ #
32
+ # @api public
33
+ def use_logger(logger)
34
+ @logger = logger
35
+ end
36
+
37
+ # Register a dataset in the gateway
38
+ #
39
+ # If dataset already exists it will be returned
40
+ #
41
+ # @return [Dataset]
42
+ #
43
+ # @api public
44
+ def dataset(name)
45
+ self[name] || connection.create_dataset(name)
46
+ end
47
+
48
+ # @see ROM::Gateway#dataset?
49
+ def dataset?(name)
50
+ connection.key?(name)
51
+ end
52
+
53
+ # Return dataset with the given name
54
+ #
55
+ # @param (see ROM::Gateway#[])
56
+ # @return [Memory::Dataset]
57
+ #
58
+ # @api public
59
+ def [](name)
60
+ connection[name]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,62 @@
1
+ require 'rom/memory/types'
2
+ require 'rom/memory/schema'
3
+
4
+ module ROM
5
+ module Memory
6
+ # Relation subclass for memory adapter
7
+ #
8
+ # @example
9
+ # class Users < ROM::Relation[:memory]
10
+ # end
11
+ #
12
+ # @api public
13
+ class Relation < ROM::Relation
14
+ include Enumerable
15
+ include Memory
16
+
17
+ adapter :memory
18
+ schema_class Memory::Schema
19
+
20
+ forward :take, :join, :restrict, :order
21
+
22
+ # Project a relation with provided attribute names
23
+ #
24
+ # @param [*Array] names A list with attribute names
25
+ #
26
+ # @return [Memory::Relation]
27
+ #
28
+ # @api public
29
+ def project(*names)
30
+ schema.project(*names).(self)
31
+ end
32
+
33
+ # Insert tuples into the relation
34
+ #
35
+ # @example
36
+ # users.insert(name: 'Jane')
37
+ #
38
+ # @return [Relation]
39
+ #
40
+ # @api public
41
+ def insert(*args)
42
+ dataset.insert(*args)
43
+ self
44
+ end
45
+ alias_method :<<, :insert
46
+
47
+ # Delete tuples from the relation
48
+ #
49
+ # @example
50
+ # users.insert(name: 'Jane')
51
+ # users.delete(name: 'Jane')
52
+ #
53
+ # @return [Relation]
54
+ #
55
+ # @api public
56
+ def delete(*args)
57
+ dataset.delete(*args)
58
+ self
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ require 'rom/schema'
2
+ require 'rom/memory/associations'
3
+
4
+ module ROM
5
+ module Memory
6
+ class Schema < ROM::Schema
7
+ # @see Schema#call
8
+ # @api public
9
+ def call(relation)
10
+ relation.new(relation.dataset.project(*map(&:name)), schema: self)
11
+ end
12
+
13
+ # @api private
14
+ def finalize_associations!(relations:)
15
+ super do
16
+ associations.map do |definition|
17
+ Memory::Associations.const_get(definition.type).new(definition, relations)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ require 'concurrent/hash'
2
+ require 'concurrent/array'
3
+
4
+ require 'rom/memory/dataset'
5
+
6
+ module ROM
7
+ module Memory
8
+ # In-memory thread-safe data storage
9
+ #
10
+ # @private
11
+ class Storage
12
+ # Dataset registry
13
+ #
14
+ # @return [ThreadSafe::Hash]
15
+ #
16
+ # @api private
17
+ attr_reader :data
18
+
19
+ # @api private
20
+ def initialize
21
+ @data = Concurrent::Hash.new
22
+ end
23
+
24
+ # @return [Dataset]
25
+ #
26
+ # @api private
27
+ def [](name)
28
+ data[name]
29
+ end
30
+
31
+ # Register a new dataset
32
+ #
33
+ # @return [Dataset]
34
+ #
35
+ # @api private
36
+ def create_dataset(name)
37
+ data[name] = Dataset.new(Concurrent::Array.new)
38
+ end
39
+
40
+ # Check if there's dataset under specified key
41
+ #
42
+ # @return [Boolean]
43
+ #
44
+ # @api private
45
+ def key?(name)
46
+ data.key?(name)
47
+ end
48
+
49
+ # Return registered datasets count
50
+ #
51
+ # @return [Integer]
52
+ #
53
+ # @api private
54
+ def size
55
+ data.size
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ require 'rom/types'
2
+
3
+ module ROM
4
+ module Memory
5
+ module Types
6
+ include ROM::Types
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,105 @@
1
+ module ROM
2
+ # Data pipeline common interface
3
+ #
4
+ # @api private
5
+ module Pipeline
6
+ # Common `>>` operator extension
7
+ #
8
+ # @api private
9
+ module Operator
10
+ # Compose two relation with a left-to-right composition
11
+ #
12
+ # @example
13
+ # users.by_name('Jane') >> tasks.for_users
14
+ #
15
+ # @param [Relation] other The right relation
16
+ #
17
+ # @return [Relation::Composite]
18
+ #
19
+ # @api public
20
+ def >>(other)
21
+ composite_class.new(self, other)
22
+ end
23
+
24
+ private
25
+
26
+ # @api private
27
+ def composite_class
28
+ raise NotImplementedError
29
+ end
30
+ end
31
+
32
+ include Operator
33
+
34
+ # Send data through specified mappers
35
+ #
36
+ # @return [Relation::Composite]
37
+ #
38
+ # @api public
39
+ def map_with(*names)
40
+ [self, *names.map { |name| mappers[name] }]
41
+ .reduce { |a, e| composite_class.new(a, e) }
42
+ end
43
+
44
+ # Forwards messages to the left side of a pipeline
45
+ #
46
+ # @api private
47
+ module Proxy
48
+ # @api private
49
+ def respond_to_missing?(name, include_private = false)
50
+ left.respond_to?(name) || super
51
+ end
52
+
53
+ private
54
+
55
+ # Check if response from method missing should be decorated
56
+ #
57
+ # @api private
58
+ def decorate?(response)
59
+ response.is_a?(left.class)
60
+ end
61
+
62
+ # @api private
63
+ def method_missing(name, *args, &block)
64
+ if left.respond_to?(name)
65
+ response = left.__send__(name, *args, &block)
66
+
67
+ if decorate?(response)
68
+ self.class.new(response, right)
69
+ else
70
+ response
71
+ end
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end
77
+
78
+ # Base composite class with left-to-right pipeline behavior
79
+ #
80
+ # @api private
81
+ class Composite
82
+ include Dry::Equalizer(:left, :right)
83
+ include Proxy
84
+
85
+ # @api private
86
+ attr_reader :left
87
+
88
+ # @api private
89
+ attr_reader :right
90
+
91
+ # @api private
92
+ def initialize(left, right)
93
+ @left = left
94
+ @right = right
95
+ end
96
+
97
+ # Compose this composite with another object
98
+ #
99
+ # @api public
100
+ def >>(other)
101
+ self.class.new(self, other)
102
+ end
103
+ end
104
+ end
105
+ end
data/lib/rom/plugin.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'rom/constants'
2
+ require 'rom/plugin_base'
3
+ require 'rom/support/configurable'
4
+
5
+ module ROM
6
+ # Plugin is a simple object used to store plugin configurations
7
+ #
8
+ # @private
9
+ class Plugin < PluginBase
10
+ include Configurable
11
+
12
+ # Apply this plugin to the provided class
13
+ #
14
+ # @param [Class] klass
15
+ #
16
+ # @api private
17
+ def apply_to(klass, options = EMPTY_HASH)
18
+ if options.any?
19
+ klass.send(:include, mod.new(options))
20
+ else
21
+ klass.send(:include, mod)
22
+ end
23
+ end
24
+ end
25
+ end