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,120 @@
1
+ require 'rom/lint/linter'
2
+
3
+ module ROM
4
+ module Lint
5
+ # Ensures that a [ROM::Gateway] extension provides datasets through the
6
+ # expected methods
7
+ #
8
+ # @api public
9
+ class Gateway < ROM::Lint::Linter
10
+ # The gateway identifier e.g. +:memory+
11
+ #
12
+ # @api public
13
+ attr_reader :identifier
14
+
15
+ # The gateway class
16
+ #
17
+ # @api public
18
+ attr_reader :gateway
19
+
20
+ # The optional URI
21
+ #
22
+ # @api public
23
+ attr_reader :uri
24
+
25
+ # Gateway instance used in lint tests
26
+ #
27
+ # @api private
28
+ attr_reader :gateway_instance
29
+
30
+ # Create a gateway linter
31
+ #
32
+ # @param [Symbol] identifier
33
+ # @param [Class] gateway
34
+ # @param [String] uri optional
35
+ def initialize(identifier, gateway, uri = nil)
36
+ @identifier = identifier
37
+ @gateway = gateway
38
+ @uri = uri
39
+ @gateway_instance = setup_gateway_instance
40
+ end
41
+
42
+ # Lint: Ensure that +gateway+ setups up its instance
43
+ #
44
+ # @api public
45
+ def lint_gateway_setup
46
+ return if gateway_instance.instance_of? gateway
47
+
48
+ complain <<-STRING
49
+ #{gateway}.setup must return a gateway instance but
50
+ returned #{gateway_instance.inspect}
51
+ STRING
52
+ end
53
+
54
+ # Lint: Ensure that +gateway_instance+ responds to +[]+
55
+ #
56
+ # @api public
57
+ def lint_dataset_reader
58
+ return if gateway_instance.respond_to? :[]
59
+
60
+ complain "#{gateway_instance} must respond to []"
61
+ end
62
+
63
+ # Lint: Ensure that +gateway_instance+ responds to +dataset?+
64
+ #
65
+ # @api public
66
+ def lint_dataset_predicate
67
+ return if gateway_instance.respond_to? :dataset?
68
+
69
+ complain "#{gateway_instance} must respond to dataset?"
70
+ end
71
+
72
+ # Lint: Ensure +gateway_instance+ supports +transaction+ interface
73
+ #
74
+ # @api public
75
+ def lint_transaction_support
76
+ result = gateway_instance.transaction { 1 }
77
+
78
+ if result != 1
79
+ complain "#{gateway_instance} must return the result of a transaction block"
80
+ end
81
+
82
+ gateway_instance.transaction do |t|
83
+ t.rollback!
84
+
85
+ complain "#{gateway_instance} must interrupt a transaction on rollback"
86
+ end
87
+ end
88
+
89
+ # Lint: Ensure +gateway_instance+ returns adapter name
90
+ def lint_adapter_reader
91
+ if gateway_instance.adapter != identifier
92
+ complain "#{gateway_instance} must have the adapter identifier set to #{identifier.inspect}"
93
+ end
94
+ rescue MissingAdapterIdentifierError
95
+ complain "#{gateway_instance} is missing the adapter identifier"
96
+ end
97
+
98
+ private
99
+
100
+ # Setup gateway instance
101
+ #
102
+ # @api private
103
+ def setup_gateway_instance
104
+ if uri
105
+ ROM::Gateway.setup(identifier, uri)
106
+ else
107
+ ROM::Gateway.setup(identifier)
108
+ end
109
+ end
110
+
111
+ # Run Gateway#disconnect
112
+ #
113
+ # @api private
114
+ def after_lint
115
+ super
116
+ gateway_instance.disconnect
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,78 @@
1
+ module ROM
2
+ module Lint
3
+ # Base class for building linters that check source code
4
+ #
5
+ # Linters are used by authors of ROM adapters to verify that their
6
+ # integration complies with the ROM api.
7
+ #
8
+ # Most of the time, authors won't need to construct linters directly
9
+ # because the provided test helpers will automatically run when required
10
+ # in tests and specs.
11
+ #
12
+ # @example
13
+ # require 'rom/lint/spec'
14
+ #
15
+ #
16
+ # @api public
17
+ class Linter
18
+ # A failure raised by +complain+
19
+ Failure = Class.new(StandardError)
20
+
21
+ # Iterate over all lint methods
22
+ #
23
+ # @yield [String, ROM::Lint]
24
+ #
25
+ # @api public
26
+ def self.each_lint
27
+ return to_enum unless block_given?
28
+ lints.each { |lint| yield lint, self }
29
+ end
30
+
31
+ # Run a lint method
32
+ #
33
+ # @param [String] name
34
+ #
35
+ # @raise [ROM::Lint::Linter::Failure] if linting fails
36
+ #
37
+ # @api public
38
+ def lint(name)
39
+ before_lint
40
+ public_send name
41
+ after_lint
42
+ true # for assertions
43
+ end
44
+
45
+ private
46
+
47
+ # Return a list a lint methods
48
+ #
49
+ # @return [String]
50
+ #
51
+ # @api private
52
+ def self.lints
53
+ public_instance_methods(true).grep(/^lint_/).map(&:to_s)
54
+ end
55
+
56
+ # Raise a failure if a lint verification fails
57
+ #
58
+ # @raise [ROM::Lint::Linter::Failure]
59
+ #
60
+ # @api private
61
+ def complain(*args)
62
+ raise Failure, *args
63
+ end
64
+
65
+ # Hook method executed before each lint method run
66
+ #
67
+ # @api private
68
+ def before_lint
69
+ end
70
+
71
+ # Hook method executed after each lint method run
72
+ #
73
+ # @api private
74
+ def after_lint
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,20 @@
1
+ require 'rom/lint/gateway'
2
+ require 'rom/lint/enumerable_dataset'
3
+
4
+ RSpec.shared_examples "a rom gateway" do
5
+ ROM::Lint::Gateway.each_lint do |name, linter|
6
+ it name do
7
+ result = linter.new(identifier, gateway, uri).lint(name)
8
+ expect(result).to be_truthy
9
+ end
10
+ end
11
+ end
12
+
13
+ RSpec.shared_examples "a rom enumerable dataset" do
14
+ ROM::Lint::EnumerableDataset.each_lint do |name, linter|
15
+ it name do
16
+ result = linter.new(dataset, data).lint(name)
17
+ expect(result).to be_truthy
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,98 @@
1
+ require 'rom/lint/gateway'
2
+ require 'rom/lint/enumerable_dataset'
3
+
4
+ module ROM
5
+ module Lint
6
+ # A module that helps to define test methods
7
+ module Test
8
+ # Defines a test method converting lint failures to assertions
9
+ #
10
+ # @param [String] name
11
+ #
12
+ # @api private
13
+ def define_test_method(name, &block)
14
+ define_method "test_#{name}" do
15
+ begin
16
+ instance_eval(&block)
17
+ rescue ROM::Lint::Linter::Failure => f
18
+ raise Minitest::Assertion, f.message
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # This is a simple lint-test for gateway class to ensure the
25
+ # basic interfaces are in place
26
+ #
27
+ # @example
28
+ #
29
+ # class MyGatewayTest < Minitest::Test
30
+ # include ROM::Lint::TestGateway
31
+ #
32
+ # def setup
33
+ # @gateway = MyGateway
34
+ # @uri = "super_db://something"
35
+ # end
36
+ # end
37
+ #
38
+ # @api public
39
+ module TestGateway
40
+ extend ROM::Lint::Test
41
+
42
+ # Returns the gateway identifier e.g. +:memory+
43
+ #
44
+ # @api public
45
+ attr_reader :identifier
46
+
47
+ # Returns the gateway class
48
+ #
49
+ # @api public
50
+ attr_reader :gateway
51
+
52
+ # Returns gateway's URI e.g. "super_db://something"
53
+ #
54
+ # @api public
55
+ attr_reader :uri
56
+
57
+ ROM::Lint::Gateway.each_lint do |name, linter|
58
+ define_test_method name do
59
+ assert linter.new(identifier, gateway, uri).lint(name)
60
+ end
61
+ end
62
+ end
63
+
64
+ # This is a simple lint-test for a gateway dataset class to ensure the
65
+ # basic behavior is correct
66
+ #
67
+ # @example
68
+ #
69
+ # class MyDatasetLintTest < Minitest::Test
70
+ # include ROM::Lint::TestEnumerableDataset
71
+ #
72
+ # def setup
73
+ # @data = [{ name: 'Jane', age: 24 }, { name: 'Joe', age: 25 }]
74
+ # @dataset = MyDataset.new(@data, [:name, :age])
75
+ # end
76
+ # end
77
+ # @api public
78
+ module TestEnumerableDataset
79
+ extend ROM::Lint::Test
80
+
81
+ # Returns the dataset instance
82
+ #
83
+ # @api public
84
+ attr_reader :dataset
85
+
86
+ # Returns the expected data
87
+ #
88
+ # @api public
89
+ attr_reader :data
90
+
91
+ ROM::Lint::EnumerableDataset.each_lint do |name, linter|
92
+ define_test_method name do
93
+ assert linter.new(dataset, data).lint(name)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,24 @@
1
+ require 'rom/struct'
2
+ require 'rom/registry'
3
+ require 'rom/mapper_compiler'
4
+
5
+ module ROM
6
+ # @private
7
+ class MapperRegistry < Registry
8
+ def self.element_not_found_error
9
+ MapperMissingError
10
+ end
11
+
12
+ option :compiler, default: -> { MapperCompiler.new }
13
+
14
+ # @see Registry
15
+ # @api public
16
+ def [](*args)
17
+ if args[0].is_a?(Symbol)
18
+ super
19
+ else
20
+ cache.fetch_or_store(args.hash) { compiler.(*args) }
21
+ end
22
+ end
23
+ end
24
+ end
data/lib/rom/memory.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rom/memory/gateway'
2
+ require 'rom/memory/relation'
3
+
4
+ ROM.register_adapter(:memory, ROM::Memory)
@@ -0,0 +1,4 @@
1
+ require 'rom/memory/associations/many_to_many'
2
+ require 'rom/memory/associations/many_to_one'
3
+ require 'rom/memory/associations/one_to_many'
4
+ require 'rom/memory/associations/one_to_one'
@@ -0,0 +1,10 @@
1
+ require 'rom/associations/many_to_many'
2
+
3
+ module ROM
4
+ module Memory
5
+ module Associations
6
+ class ManyToMany < ROM::Associations::ManyToMany
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'rom/associations/many_to_one'
2
+
3
+ module ROM
4
+ module Memory
5
+ module Associations
6
+ class ManyToOne < ROM::Associations::ManyToOne
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'rom/associations/one_to_many'
2
+
3
+ module ROM
4
+ module Memory
5
+ module Associations
6
+ class OneToMany < ROM::Associations::OneToMany
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'rom/memory/associations/one_to_many'
2
+
3
+ module ROM
4
+ module Memory
5
+ module Associations
6
+ class OneToOne < OneToMany
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+ require 'rom/commands'
2
+
3
+ module ROM
4
+ module Memory
5
+ # Memory adapter commands namespace
6
+ #
7
+ # @api public
8
+ module Commands
9
+ # In-memory create command
10
+ #
11
+ # @api public
12
+ class Create < ROM::Commands::Create
13
+ adapter :memory
14
+ use :schema
15
+
16
+ # @see ROM::Commands::Create#execute
17
+ def execute(tuples)
18
+ Array([tuples]).flatten.map { |tuple|
19
+ attributes = input[tuple]
20
+ relation.insert(attributes.to_h)
21
+ attributes
22
+ }.to_a
23
+ end
24
+ end
25
+
26
+ # In-memory update command
27
+ #
28
+ # @api public
29
+ class Update < ROM::Commands::Update
30
+ adapter :memory
31
+ use :schema
32
+
33
+ # @see ROM::Commands::Update#execute
34
+ def execute(params)
35
+ attributes = input[params]
36
+ relation.map { |tuple| tuple.update(attributes.to_h) }
37
+ end
38
+ end
39
+
40
+ # In-memory delete command
41
+ #
42
+ # @api public
43
+ class Delete < ROM::Commands::Delete
44
+ adapter :memory
45
+
46
+ # @see ROM::Commands::Delete#execute
47
+ def execute
48
+ relation.to_a.map do |tuple|
49
+ source.delete(tuple)
50
+ tuple
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end