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,34 @@
1
+ module ROM
2
+ module Commands
3
+ class Lazy
4
+ class Update < 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
+
14
+ children.map do |child|
15
+ command_proc[command, parent, child].call(child, parent)
16
+ end
17
+ end.reduce(:concat)
18
+ else
19
+ input = evaluator.call(first)
20
+
21
+ if input.is_a?(Array)
22
+ input.map.with_index do |item, index|
23
+ command_proc[command, last, item].call(item, *args[1..size-1])
24
+ end
25
+ else
26
+ command_proc[command, *(size > 1 ? [last, input] : [input])]
27
+ .call(input, *args[1..size-1])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,96 @@
1
+ module ROM
2
+ module Commands
3
+ # Abstract result class for success and error results
4
+ #
5
+ # @api public
6
+ class Result
7
+ # Return command execution result
8
+ #
9
+ # @api public
10
+ attr_reader :value
11
+
12
+ # Return potential command execution result error
13
+ #
14
+ # @api public
15
+ attr_reader :error
16
+
17
+ # Coerce result to an array
18
+ #
19
+ # @abstract
20
+ #
21
+ # @api public
22
+ def to_ary
23
+ raise NotImplementedError
24
+ end
25
+ alias_method :to_a, :to_ary
26
+
27
+ # Return true if command successful
28
+ #
29
+ # @api public
30
+ def success?
31
+ is_a?(Success)
32
+ end
33
+
34
+ # Return true if command failed
35
+ #
36
+ # @api public
37
+ def failure?
38
+ is_a?(Failure)
39
+ end
40
+
41
+ # Success result has a value and no error
42
+ #
43
+ # @api public
44
+ class Success < Result
45
+ # @api private
46
+ def initialize(value)
47
+ @value = value.is_a?(self.class) ? value.value : value
48
+ end
49
+
50
+ # Call next command on continuation
51
+ #
52
+ # @api public
53
+ def >(other)
54
+ other.call(value)
55
+ end
56
+
57
+ # Return the value
58
+ #
59
+ # @return [Array]
60
+ #
61
+ # @api public
62
+ def to_ary
63
+ value.to_ary
64
+ end
65
+ end
66
+
67
+ # Failure result has an error and no value
68
+ #
69
+ # @api public
70
+ class Failure < Result
71
+ # @api private
72
+ def initialize(error)
73
+ @error = error
74
+ end
75
+
76
+ # Do not call next command on continuation
77
+ #
78
+ # @return [self]
79
+ #
80
+ # @api public
81
+ def >(_other)
82
+ self
83
+ end
84
+
85
+ # Return the error
86
+ #
87
+ # @return [Array<CommandError>]
88
+ #
89
+ # @api public
90
+ def to_ary
91
+ error
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,14 @@
1
+ require 'rom/command'
2
+
3
+ module ROM
4
+ module Commands
5
+ # Update command
6
+ #
7
+ # This command updates all tuples in its relation with new attributes
8
+ #
9
+ # @abstract
10
+ class Update < Command
11
+ restrictable true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,114 @@
1
+ require 'forwardable'
2
+
3
+ require 'rom/environment'
4
+ require 'rom/setup'
5
+ require 'rom/configuration_dsl'
6
+ require 'rom/support/notifications'
7
+
8
+ module ROM
9
+ class Configuration
10
+ extend Forwardable
11
+ extend Notifications
12
+
13
+ register_event('configuration.relations.class.ready')
14
+ register_event('configuration.relations.object.registered')
15
+ register_event('configuration.relations.registry.created')
16
+ register_event('configuration.relations.schema.allocated')
17
+ register_event('configuration.relations.schema.set')
18
+ register_event('configuration.relations.dataset.allocated')
19
+ register_event('configuration.commands.class.before_build')
20
+
21
+ include ROM::ConfigurationDSL
22
+
23
+ NoDefaultAdapterError = Class.new(StandardError)
24
+
25
+ attr_reader :environment, :setup, :notifications
26
+
27
+ def_delegators :@setup, :register_relation, :register_command, :register_mapper, :register_plugin,
28
+ :command_classes, :mapper_classes,
29
+ :auto_registration
30
+
31
+ def_delegators :@environment, :gateways, :gateways_map, :configure, :config
32
+
33
+ # @api public
34
+ def initialize(*args, &block)
35
+ @environment = Environment.new(*args)
36
+ @notifications = Notifications.event_bus(:configuration)
37
+ @setup = Setup.new(notifications)
38
+
39
+ use :mappers # enable mappers by default
40
+
41
+ block.call(self) unless block.nil?
42
+ end
43
+
44
+ # Apply a plugin to the configuration
45
+ #
46
+ # @param [Mixed] The plugin identifier, usually a Symbol
47
+ # @param [Hash] Plugin options
48
+ #
49
+ # @api public
50
+ def use(plugin, options = {})
51
+ if plugin.is_a?(Array)
52
+ plugin.each { |p| use(p) }
53
+ elsif plugin.is_a?(Hash)
54
+ plugin.to_a.each { |p| use(*p) }
55
+ else
56
+ ROM.plugin_registry.configuration.fetch(plugin).apply_to(self, options)
57
+ end
58
+
59
+ self
60
+ end
61
+
62
+ # Return gateway identified by name
63
+ #
64
+ # @return [Gateway]
65
+ #
66
+ # @api private
67
+ def [](name)
68
+ gateways.fetch(name)
69
+ end
70
+
71
+ # Returns gateway if method is a name of a registered gateway
72
+ #
73
+ # @return [Gateway]
74
+ #
75
+ # @api private
76
+ def method_missing(name, *)
77
+ gateways.fetch(name) { super }
78
+ end
79
+
80
+ # Hook for respond_to? used internally
81
+ #
82
+ # @api private
83
+ def respond_to?(name, include_all = false)
84
+ gateways.key?(name) || super
85
+ end
86
+
87
+ # @api private
88
+ def default_gateway
89
+ @default_gateway ||= gateways[:default]
90
+ end
91
+
92
+ # @api private
93
+ def adapter_for_gateway(gateway)
94
+ ROM.adapters.select do |key, value|
95
+ value.const_defined?(:Gateway) && gateway.kind_of?(value.const_get(:Gateway))
96
+ end.keys.first
97
+ end
98
+
99
+ # @api private
100
+ def relation_classes(gateway = nil)
101
+ if gateway
102
+ gw_name = gateway.is_a?(Symbol) ? gateway : gateways_map[gateway]
103
+ setup.relation_classes.select { |rel| rel.gateway == gw_name }
104
+ else
105
+ setup.relation_classes
106
+ end
107
+ end
108
+
109
+ # @api private
110
+ def default_adapter
111
+ @default_adapter ||= adapter_for_gateway(default_gateway) || ROM.adapters.keys.first
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,87 @@
1
+ require 'rom/configuration_dsl/relation'
2
+ require 'rom/configuration_dsl/command_dsl'
3
+
4
+ module ROM
5
+ # This extends Configuration class with the DSL methods
6
+ #
7
+ # @api public
8
+ module ConfigurationDSL
9
+ # Relation definition DSL
10
+ #
11
+ # @example
12
+ #
13
+ # setup.relation(:users) do
14
+ # def names
15
+ # project(:name)
16
+ # end
17
+ # end
18
+ #
19
+ # @api public
20
+ def relation(name, options = EMPTY_HASH, &block)
21
+ klass_opts = { adapter: default_adapter }.merge(options)
22
+ klass = Relation.build_class(name, klass_opts)
23
+ klass.schema_opts(dataset: name, relation: name)
24
+ klass.class_eval(&block) if block
25
+ register_relation(klass)
26
+ klass
27
+ end
28
+
29
+ # Command definition DSL
30
+ #
31
+ # @example
32
+ #
33
+ # setup.commands(:users) do
34
+ # define(:create) do
35
+ # input NewUserParams
36
+ # result :one
37
+ # end
38
+ #
39
+ # define(:update) do
40
+ # input UserParams
41
+ # result :many
42
+ # end
43
+ #
44
+ # define(:delete) do
45
+ # result :many
46
+ # end
47
+ # end
48
+ #
49
+ # @api public
50
+ def commands(name, &block)
51
+ register_command(*CommandDSL.new(name, default_adapter, &block).command_classes)
52
+ end
53
+
54
+ # Configures a plugin for a specific adapter to be enabled for all relations
55
+ #
56
+ # @example
57
+ # config = ROM::Configuration.new(:sql, 'sqlite::memory')
58
+ #
59
+ # config.plugin(:sql, relations: :instrumentation) do |p|
60
+ # p.notifications = MyNotificationsBackend
61
+ # end
62
+ #
63
+ # config.plugin(:sql, relations: :pagination)
64
+ #
65
+ # @param [Symbol] adapter The adapter identifier
66
+ # @param [Hash<Symbol=>Symbol>] spec Component identifier => plugin identifier
67
+ #
68
+ # @return [Plugin]
69
+ #
70
+ # @api public
71
+ def plugin(adapter, spec, &block)
72
+ type, name = spec.flatten(1)
73
+ plugin = plugin_registry.send(type).adapter(adapter).fetch(name) { plugin_registry.send(type).fetch(name) }
74
+
75
+ if block
76
+ register_plugin(plugin.configure(&block))
77
+ else
78
+ register_plugin(plugin)
79
+ end
80
+ end
81
+
82
+ # @api private
83
+ def plugin_registry
84
+ ROM.plugin_registry
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,41 @@
1
+ require 'dry/core/inflector'
2
+ require 'dry/core/class_builder'
3
+
4
+ module ROM
5
+ module ConfigurationDSL
6
+ # Setup DSL-specific command extensions
7
+ #
8
+ # @private
9
+ class Command
10
+ # Generate a command subclass
11
+ #
12
+ # This is used by Setup#commands DSL and its `define` block
13
+ #
14
+ # @api private
15
+ def self.build_class(name, relation, options = EMPTY_HASH, &block)
16
+ type = options.fetch(:type) { name }
17
+ command_type = Dry::Core::Inflector.classify(type)
18
+ adapter = options.fetch(:adapter)
19
+ parent = ROM::Command.adapter_namespace(adapter).const_get(command_type)
20
+ class_name = generate_class_name(adapter, command_type, relation)
21
+
22
+ Dry::Core::ClassBuilder.new(name: class_name, parent: parent).call do |klass|
23
+ klass.register_as(name)
24
+ klass.relation(relation)
25
+ klass.class_eval(&block) if block
26
+ end
27
+ end
28
+
29
+ # Create a command subclass name based on adapter, type and relation
30
+ #
31
+ # @api private
32
+ def self.generate_class_name(adapter, command_type, relation)
33
+ pieces = ['ROM']
34
+ pieces << Dry::Core::Inflector.classify(adapter)
35
+ pieces << 'Commands'
36
+ pieces << "#{command_type}[#{Dry::Core::Inflector.classify(relation)}s]"
37
+ pieces.join('::')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require 'rom/configuration_dsl/command'
2
+
3
+ module ROM
4
+ module ConfigurationDSL
5
+ # Command `define` DSL used by Setup#commands
6
+ #
7
+ # @private
8
+ class CommandDSL
9
+ attr_reader :relation, :adapter, :command_classes
10
+
11
+ # @api private
12
+ def initialize(relation, adapter = nil, &block)
13
+ @relation = relation
14
+ @adapter = adapter
15
+ @command_classes = []
16
+ instance_exec(&block)
17
+ end
18
+
19
+ # Define a command class
20
+ #
21
+ # @param [Symbol] name of the command
22
+ # @param [Hash] options
23
+ # @option options [Symbol] :type The type of the command
24
+ #
25
+ # @return [Class] generated class
26
+ #
27
+ # @api public
28
+ def define(name, options = EMPTY_HASH, &block)
29
+ @command_classes << Command.build_class(
30
+ name, relation, { adapter: adapter }.merge(options), &block
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ require 'dry/core/class_builder'
2
+ require 'dry/core/inflector'
3
+
4
+ module ROM
5
+ module ConfigurationDSL
6
+ # Setup DSL-specific relation extensions
7
+ #
8
+ # @private
9
+ class Relation
10
+ # Generate a relation subclass
11
+ #
12
+ # This is used by Setup#relation DSL
13
+ #
14
+ # @api private
15
+ def self.build_class(name, options = EMPTY_HASH)
16
+ class_name = "ROM::Relation[#{Dry::Core::Inflector.camelize(name)}]"
17
+ adapter = options.fetch(:adapter)
18
+
19
+ Dry::Core::ClassBuilder.new(name: class_name, parent: ROM::Relation[adapter]).call do |klass|
20
+ klass.gateway(options.fetch(:gateway, :default))
21
+ klass.schema(name) { }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end