rom-core 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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