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,53 @@
1
+ require 'rom/pipeline'
2
+
3
+ module ROM
4
+ module Commands
5
+ # Composite command that consists of left and right commands
6
+ #
7
+ # @api public
8
+ class Composite < Pipeline::Composite
9
+ # Calls the composite command
10
+ #
11
+ # Right command is called with a result from the left one
12
+ #
13
+ # @return [Object]
14
+ #
15
+ # @api public
16
+ def call(*args)
17
+ response = left.call(*args)
18
+
19
+ if response.nil? || (many? && response.size == 0)
20
+ return one? ? nil : EMPTY_ARRAY
21
+ end
22
+
23
+ if one? && !graph?
24
+ if right.is_a?(Command) || right.is_a?(Commands::Composite)
25
+ right.call([response].first)
26
+ else
27
+ right.call([response]).first
28
+ end
29
+ elsif one? && graph?
30
+ right.call(response).first
31
+ else
32
+ right.call(response)
33
+ end
34
+ end
35
+ alias_method :[], :call
36
+
37
+ # @api private
38
+ def graph?
39
+ left.is_a?(Graph)
40
+ end
41
+
42
+ # @api private
43
+ def result
44
+ left.result
45
+ end
46
+
47
+ # @api private
48
+ def decorate?(response)
49
+ super || response.is_a?(Graph)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ require 'rom/command'
2
+
3
+ module ROM
4
+ module Commands
5
+ # Create command
6
+ #
7
+ # This command inserts a new tuple into a relation
8
+ #
9
+ # @abstract
10
+ class Create < Command
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'rom/command'
2
+
3
+ module ROM
4
+ module Commands
5
+ # Delete command
6
+ #
7
+ # This command removes tuples from its target relation
8
+ #
9
+ # @abstract
10
+ class Delete < Command
11
+ restrictable true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ require 'rom/initializer'
2
+ require 'rom/pipeline'
3
+ require 'rom/commands/graph/class_interface'
4
+
5
+ module ROM
6
+ module Commands
7
+ # Command graph
8
+ #
9
+ # @api private
10
+ class Graph
11
+ extend Initializer
12
+ include Dry::Equalizer(:root, :nodes)
13
+
14
+ extend ClassInterface
15
+
16
+ include Pipeline
17
+ include Pipeline::Proxy
18
+
19
+ # @attr_reader [Command] root The root command
20
+ param :root
21
+
22
+ # @attr_reader [Array<Command>] nodes The child commands
23
+ param :nodes
24
+
25
+ alias_method :left, :root
26
+ alias_method :right, :nodes
27
+
28
+ # @attr_reader [Symbol] root's relation name
29
+ option :name, default: -> { root.name }
30
+
31
+ option :mappers, default: -> { MapperRegistry.new }
32
+
33
+ # Calls root and all nodes with the result from root
34
+ #
35
+ # Graph results are mappable through `combine` operation in mapper DSL
36
+ #
37
+ # @example
38
+ # create_user = rom.commands[:users].create
39
+ # create_task = rom.commands[:tasks].create
40
+ #
41
+ # command = create_user
42
+ # .with(name: 'Jane')
43
+ # .combine(create_task.with(title: 'Task'))
44
+ #
45
+ # command.call
46
+ #
47
+ # @return [Array] nested array with command results
48
+ #
49
+ # @api public
50
+ def call(*args)
51
+ left = root.call(*args)
52
+
53
+ right = nodes.map { |node|
54
+ response =
55
+ if node.lazy?
56
+ node.call(args.first, left)
57
+ else
58
+ node.call(left)
59
+ end
60
+
61
+ if node.one? && !node.graph?
62
+ [response]
63
+ else
64
+ response
65
+ end
66
+ }
67
+
68
+ if one?
69
+ [[left], right]
70
+ else
71
+ [left, right]
72
+ end
73
+ end
74
+
75
+ # @api private
76
+ def graph?
77
+ true
78
+ end
79
+
80
+ private
81
+
82
+ # @api public
83
+ def composite_class
84
+ Command::Composite
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,62 @@
1
+ require 'rom/commands/graph/input_evaluator'
2
+
3
+ module ROM
4
+ module Commands
5
+ class Graph
6
+ # Class methods for command Graph
7
+ #
8
+ # @api private
9
+ module ClassInterface
10
+ # Build a command graph recursively
11
+ #
12
+ # This is used by `Container#command` when array with options is passed in
13
+ #
14
+ # @param [Registry] registry The command registry from container
15
+ # @param [Array] options The options array
16
+ # @param [Array] path The path for input evaluator proc
17
+ #
18
+ # @return [Graph]
19
+ #
20
+ # @api private
21
+ def build(registry, options, path = EMPTY_ARRAY)
22
+ options.reduce { |spec, other| build_command(registry, spec, other, path) }
23
+ end
24
+
25
+ # @api private
26
+ def build_command(registry, spec, other, path)
27
+ cmd_opts, nodes = other
28
+
29
+ key, relation =
30
+ if spec.is_a?(Hash)
31
+ spec.to_a.first
32
+ else
33
+ [spec, spec]
34
+ end
35
+
36
+ name, opts =
37
+ if cmd_opts.is_a?(Hash)
38
+ cmd_opts.to_a.first
39
+ else
40
+ [cmd_opts]
41
+ end
42
+
43
+ command = registry[relation][name]
44
+ tuple_path = Array[*path] << key
45
+ input_proc = InputEvaluator.build(tuple_path, nodes)
46
+
47
+ command = command.with(input_proc, opts)
48
+
49
+ if nodes
50
+ if nodes.all? { |node| node.is_a?(Array) }
51
+ command.combine(*nodes.map { |node| build(registry, node, tuple_path) })
52
+ else
53
+ command.combine(build(registry, nodes, tuple_path))
54
+ end
55
+ else
56
+ command
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,62 @@
1
+ module ROM
2
+ module Commands
3
+ class Graph
4
+ class InputEvaluator
5
+ include Dry::Equalizer(:tuple_path, :excluded_keys)
6
+
7
+ attr_reader :tuple_path
8
+
9
+ attr_reader :excluded_keys
10
+
11
+ attr_reader :exclude_proc
12
+
13
+ def self.build(tuple_path, nodes)
14
+ new(tuple_path, extract_excluded_keys(nodes))
15
+ end
16
+
17
+ def self.extract_excluded_keys(nodes)
18
+ return unless nodes
19
+
20
+ nodes
21
+ .map { |item| item.is_a?(Array) && item.size > 1 ? item.first : item }
22
+ .compact
23
+ .map { |item| item.is_a?(Hash) ? item.keys.first : item }
24
+ .reject { |item| item.is_a?(Array) }
25
+ end
26
+
27
+ def self.exclude_proc(excluded_keys)
28
+ -> input { input.reject { |k, _| excluded_keys.include?(k) } }
29
+ end
30
+
31
+ def initialize(tuple_path, excluded_keys)
32
+ @tuple_path = tuple_path
33
+ @excluded_keys = excluded_keys
34
+ @exclude_proc = self.class.exclude_proc(excluded_keys)
35
+ end
36
+
37
+ def call(*args)
38
+ input, index = args
39
+
40
+ value =
41
+ begin
42
+ if index
43
+ tuple_path[0..tuple_path.size-2]
44
+ .reduce(input) { |a, e| a.fetch(e) }
45
+ .at(index)[tuple_path.last]
46
+ else
47
+ tuple_path.reduce(input) { |a, e| a.fetch(e) }
48
+ end
49
+ rescue KeyError => e
50
+ raise KeyMissing, e.message
51
+ end
52
+
53
+ if excluded_keys
54
+ value.is_a?(Array) ? value.map(&exclude_proc) : exclude_proc[value]
55
+ else
56
+ value
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,99 @@
1
+ require 'rom/commands/composite'
2
+ require 'rom/commands/graph'
3
+
4
+ module ROM
5
+ module Commands
6
+ # Lazy command wraps another command and evaluates its input when called
7
+ #
8
+ # @api private
9
+ class Lazy
10
+ include Dry::Equalizer(:command, :evaluator)
11
+
12
+ # @attr_reader [Command] command The wrapped command
13
+ attr_reader :command
14
+
15
+ alias_method :unwrap, :command
16
+
17
+ # @attr_reader [Proc] evaluator The proc that will evaluate the input
18
+ attr_reader :evaluator
19
+
20
+ attr_reader :command_proc
21
+
22
+ # @api private
23
+ def self.[](command)
24
+ case command
25
+ when Commands::Create then Lazy::Create
26
+ when Commands::Update then Lazy::Update
27
+ when Commands::Delete then Lazy::Delete
28
+ else
29
+ self
30
+ end
31
+ end
32
+
33
+ # @api private
34
+ def initialize(command, evaluator, command_proc = nil)
35
+ @command = command
36
+ @evaluator = evaluator
37
+ @command_proc = command_proc || proc { |*| command }
38
+ end
39
+
40
+ # Evaluate command's input using the input proc and pass to command
41
+ #
42
+ # @return [Array,Hash]
43
+ #
44
+ # @api public
45
+ def call(*args)
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # Compose a lazy command with another one
50
+ #
51
+ # @see Commands::Abstract#>>
52
+ #
53
+ # @return [Composite]
54
+ #
55
+ # @api public
56
+ def >>(other)
57
+ Composite.new(self, other)
58
+ end
59
+
60
+ # @see Abstract#combine
61
+ #
62
+ # @api public
63
+ def combine(*others)
64
+ Graph.new(self, others)
65
+ end
66
+
67
+ # @api private
68
+ def lazy?
69
+ true
70
+ end
71
+
72
+ # @api private
73
+ def respond_to_missing?(name, include_private = false)
74
+ super || command.respond_to?(name)
75
+ end
76
+
77
+ private
78
+
79
+ # @api private
80
+ def method_missing(name, *args, &block)
81
+ if command.respond_to?(name)
82
+ response = command.public_send(name, *args, &block)
83
+
84
+ if response.instance_of?(command.class)
85
+ self.class.new(response, evaluator, command_proc)
86
+ else
87
+ response
88
+ end
89
+ else
90
+ super
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ require 'rom/commands/lazy/create'
98
+ require 'rom/commands/lazy/update'
99
+ require 'rom/commands/lazy/delete'
@@ -0,0 +1,23 @@
1
+ module ROM
2
+ module Commands
3
+ class Lazy
4
+ class Create < Lazy
5
+ def call(*args)
6
+ first = args.first
7
+ last = args.last
8
+ size = args.size
9
+
10
+ if size > 1 && last.is_a?(Array)
11
+ last.map.with_index do |parent, index|
12
+ children = evaluator.call(first, index)
13
+ command_proc[command, parent, children].call(children, parent)
14
+ end.reduce(:concat)
15
+ else
16
+ input = evaluator.call(first)
17
+ command.call(input, *args[1..size-1])
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module ROM
2
+ module Commands
3
+ class Lazy
4
+ class Delete < Lazy
5
+ def call(*args)
6
+ first = args.first
7
+ last = args.last
8
+ size = args.size
9
+
10
+ if size > 1 && last.is_a?(Array)
11
+ raise NotImplementedError
12
+ else
13
+ input = evaluator.call(first)
14
+
15
+ if input.is_a?(Array)
16
+ input.map do |item|
17
+ command_proc[command, *(size > 1 ? [last, item] : [input])].call
18
+ end
19
+ else
20
+ command_proc[command, input].call
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end