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,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