rom 1.0.0 → 2.0.0
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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.travis.yml +5 -3
- data/CHANGELOG.md +38 -0
- data/Gemfile +2 -14
- data/README.md +11 -17
- data/lib/rom.rb +2 -0
- data/lib/rom/association_set.rb +26 -0
- data/lib/rom/command.rb +50 -45
- data/lib/rom/command_registry.rb +26 -3
- data/lib/rom/commands/class_interface.rb +52 -19
- data/lib/rom/commands/composite.rb +5 -0
- data/lib/rom/commands/delete.rb +1 -5
- data/lib/rom/commands/graph.rb +11 -0
- data/lib/rom/commands/lazy.rb +2 -0
- data/lib/rom/commands/update.rb +1 -5
- data/lib/rom/configuration.rb +2 -0
- data/lib/rom/container.rb +3 -3
- data/lib/rom/global.rb +1 -23
- data/lib/rom/memory/commands.rb +2 -0
- data/lib/rom/memory/relation.rb +3 -0
- data/lib/rom/memory/storage.rb +4 -7
- data/lib/rom/memory/types.rb +9 -0
- data/lib/rom/pipeline.rb +26 -12
- data/lib/rom/plugin_registry.rb +2 -2
- data/lib/rom/plugins/command/schema.rb +26 -0
- data/lib/rom/plugins/configuration/configuration_dsl.rb +2 -1
- data/lib/rom/plugins/relation/key_inference.rb +18 -3
- data/lib/rom/plugins/relation/registry_reader.rb +3 -1
- data/lib/rom/plugins/relation/view.rb +11 -6
- data/lib/rom/relation.rb +76 -16
- data/lib/rom/relation/class_interface.rb +44 -3
- data/lib/rom/relation/curried.rb +13 -4
- data/lib/rom/relation/graph.rb +15 -5
- data/lib/rom/relation/loaded.rb +42 -6
- data/lib/rom/relation/name.rb +102 -0
- data/lib/rom/relation_registry.rb +5 -0
- data/lib/rom/schema.rb +87 -0
- data/lib/rom/schema/dsl.rb +58 -0
- data/lib/rom/setup/auto_registration.rb +2 -2
- data/lib/rom/setup/finalize.rb +5 -5
- data/lib/rom/setup/finalize/{commands.rb → finalize_commands.rb} +2 -22
- data/lib/rom/setup/finalize/{mappers.rb → finalize_mappers.rb} +0 -0
- data/lib/rom/setup/finalize/finalize_relations.rb +60 -0
- data/lib/rom/types.rb +18 -0
- data/lib/rom/version.rb +1 -1
- data/log/.gitkeep +0 -0
- data/rom.gemspec +4 -2
- data/spec/integration/command_registry_spec.rb +13 -0
- data/spec/integration/commands/delete_spec.rb +0 -17
- data/spec/integration/commands/graph_builder_spec.rb +1 -1
- data/spec/integration/commands/graph_spec.rb +1 -1
- data/spec/integration/commands/update_spec.rb +0 -19
- data/spec/integration/commands_spec.rb +10 -3
- data/spec/integration/multi_repo_spec.rb +1 -1
- data/spec/integration/relations/default_dataset_spec.rb +27 -4
- data/spec/integration/setup_spec.rb +1 -4
- data/spec/shared/command_behavior.rb +17 -7
- data/spec/shared/container.rb +2 -2
- data/spec/shared/gateway_only.rb +1 -1
- data/spec/spec_helper.rb +5 -6
- data/spec/unit/rom/association_set_spec.rb +23 -0
- data/spec/unit/rom/auto_registration_spec.rb +1 -1
- data/spec/unit/rom/commands/lazy_spec.rb +8 -0
- data/spec/unit/rom/commands_spec.rb +45 -7
- data/spec/unit/rom/configurable_spec.rb +1 -1
- data/spec/unit/rom/container_spec.rb +6 -0
- data/spec/unit/rom/create_container_spec.rb +1 -1
- data/spec/unit/rom/environment_spec.rb +1 -1
- data/spec/unit/rom/memory/commands_spec.rb +43 -0
- data/spec/unit/rom/plugins/relation/key_inference_spec.rb +70 -12
- data/spec/unit/rom/plugins/relation/view_spec.rb +4 -0
- data/spec/unit/rom/relation/graph_spec.rb +10 -0
- data/spec/unit/rom/relation/lazy_spec.rb +3 -3
- data/spec/unit/rom/relation/loaded_spec.rb +15 -0
- data/spec/unit/rom/relation/name_spec.rb +51 -0
- data/spec/unit/rom/relation/schema_spec.rb +117 -0
- data/spec/unit/rom/relation_spec.rb +37 -7
- data/spec/unit/rom/schema_spec.rb +10 -0
- metadata +51 -12
- data/lib/rom/setup/finalize/relations.rb +0 -53
- data/spec/unit/rom/global_spec.rb +0 -18
- data/spec/unit/rom/registry_spec.rb +0 -38
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rom/support/class_builder'
|
2
|
+
|
1
3
|
module ROM
|
2
4
|
# Base command class with factory class-level interface and setup-related logic
|
3
5
|
#
|
@@ -52,6 +54,28 @@ module ROM
|
|
52
54
|
new(relation, self.options.merge(options))
|
53
55
|
end
|
54
56
|
|
57
|
+
# Create a command class with a specific type
|
58
|
+
#
|
59
|
+
# @param [Symbol] command name
|
60
|
+
# @param [Class] parent class
|
61
|
+
#
|
62
|
+
# @yield [Class] create class
|
63
|
+
#
|
64
|
+
# @return [Class, Object] return result of the block if it was provided
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def create_class(name, type, &block)
|
68
|
+
klass = ClassBuilder
|
69
|
+
.new(name: "#{Inflector.classify(type)}[:#{name}]", parent: type)
|
70
|
+
.call
|
71
|
+
|
72
|
+
if block
|
73
|
+
yield(klass)
|
74
|
+
else
|
75
|
+
klass
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
55
79
|
# Use a configured plugin in this relation
|
56
80
|
#
|
57
81
|
# @example
|
@@ -70,25 +94,15 @@ module ROM
|
|
70
94
|
ROM.plugin_registry.commands.fetch(plugin, adapter).apply_to(self)
|
71
95
|
end
|
72
96
|
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
if response.is_a?(relation.class)
|
83
|
-
new(response)
|
84
|
-
else
|
85
|
-
response
|
86
|
-
end
|
87
|
-
end
|
88
|
-
RUBY
|
89
|
-
end
|
90
|
-
|
91
|
-
mod
|
97
|
+
# Extend a command class with relation view methods
|
98
|
+
#
|
99
|
+
# @param [Relation]
|
100
|
+
#
|
101
|
+
# @return [Class]
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
def extend_for_relation(relation)
|
105
|
+
include(relation_methods_mod(relation.class))
|
92
106
|
end
|
93
107
|
|
94
108
|
# Return default name of the command class based on its name
|
@@ -110,6 +124,25 @@ module ROM
|
|
110
124
|
def options
|
111
125
|
{ input: input, validator: validator, result: result }
|
112
126
|
end
|
127
|
+
|
128
|
+
# @api private
|
129
|
+
def relation_methods_mod(relation_class)
|
130
|
+
Module.new do
|
131
|
+
relation_class.view_methods.each do |meth|
|
132
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
133
|
+
def #{meth}(*args)
|
134
|
+
response = relation.public_send(:#{meth}, *args)
|
135
|
+
|
136
|
+
if response.is_a?(relation.class)
|
137
|
+
new(response)
|
138
|
+
else
|
139
|
+
response
|
140
|
+
end
|
141
|
+
end
|
142
|
+
RUBY
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
113
146
|
end
|
114
147
|
end
|
115
148
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rom/pipeline'
|
2
|
+
require 'rom/support/constants'
|
2
3
|
|
3
4
|
module ROM
|
4
5
|
module Commands
|
@@ -16,6 +17,10 @@ module ROM
|
|
16
17
|
def call(*args)
|
17
18
|
response = left.call(*args)
|
18
19
|
|
20
|
+
if response.nil? || (many? && response.size == 0)
|
21
|
+
return one? ? nil : EMPTY_ARRAY
|
22
|
+
end
|
23
|
+
|
19
24
|
if one? && !graph?
|
20
25
|
if right.is_a?(Command) || right.is_a?(Commands::Composite)
|
21
26
|
right.call([response].first)
|
data/lib/rom/commands/delete.rb
CHANGED
data/lib/rom/commands/graph.rb
CHANGED
@@ -22,6 +22,9 @@ module ROM
|
|
22
22
|
# @attr_reader [Array<Command>] nodes The child commands
|
23
23
|
attr_reader :nodes
|
24
24
|
|
25
|
+
# @attr_reader [Symbol] root's relation name
|
26
|
+
attr_reader :name
|
27
|
+
|
25
28
|
alias_method :left, :root
|
26
29
|
alias_method :right, :nodes
|
27
30
|
|
@@ -32,6 +35,7 @@ module ROM
|
|
32
35
|
super
|
33
36
|
@root = root
|
34
37
|
@nodes = nodes
|
38
|
+
@name = root.name
|
35
39
|
end
|
36
40
|
|
37
41
|
# Calls root and all nodes with the result from root
|
@@ -87,6 +91,13 @@ module ROM
|
|
87
91
|
def graph?
|
88
92
|
true
|
89
93
|
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# @api public
|
98
|
+
def composite_class
|
99
|
+
Command::Composite
|
100
|
+
end
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
data/lib/rom/commands/lazy.rb
CHANGED
data/lib/rom/commands/update.rb
CHANGED
data/lib/rom/configuration.rb
CHANGED
data/lib/rom/container.rb
CHANGED
@@ -110,7 +110,7 @@ module ROM
|
|
110
110
|
name = graph.name
|
111
111
|
|
112
112
|
if mappers.key?(name)
|
113
|
-
graph.with(mappers: mappers[
|
113
|
+
graph.with(mappers: mappers[name])
|
114
114
|
else
|
115
115
|
graph
|
116
116
|
end
|
@@ -120,9 +120,9 @@ module ROM
|
|
120
120
|
raise ArgumentError, "#{self.class}#command accepts a symbol or an array"
|
121
121
|
end
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
def disconnect
|
125
|
-
gateways.
|
125
|
+
gateways.each_value(&:disconnect)
|
126
126
|
end
|
127
127
|
end
|
128
128
|
end
|
data/lib/rom/global.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require 'rom/plugin_registry'
|
2
3
|
require 'rom/global/plugin_dsl'
|
3
4
|
require 'rom/support/deprecations'
|
@@ -13,7 +14,6 @@ module ROM
|
|
13
14
|
def self.extended(rom)
|
14
15
|
super
|
15
16
|
|
16
|
-
rom.instance_variable_set('@env', nil)
|
17
17
|
rom.instance_variable_set('@adapters', {})
|
18
18
|
rom.instance_variable_set('@plugin_registry', PluginRegistry.new)
|
19
19
|
end
|
@@ -56,27 +56,5 @@ module ROM
|
|
56
56
|
adapters[identifier] = adapter
|
57
57
|
self
|
58
58
|
end
|
59
|
-
|
60
|
-
def env=(container)
|
61
|
-
@env = container
|
62
|
-
end
|
63
|
-
|
64
|
-
def env
|
65
|
-
if @env.nil?
|
66
|
-
ROM::Deprecations.announce(:env, %q{
|
67
|
-
ROM.env is no longer automatically populated with your container.
|
68
|
-
If possible, refactor your code to remove the dependency on global ROM state. If it is not
|
69
|
-
possible—or as a temporary solution—you can assign your container to `ROM.env` upon
|
70
|
-
creation:
|
71
|
-
|
72
|
-
ROM.env = ROM.container(:memory) do |rom|
|
73
|
-
...
|
74
|
-
end
|
75
|
-
})
|
76
|
-
nil
|
77
|
-
else
|
78
|
-
@env
|
79
|
-
end
|
80
|
-
end
|
81
59
|
end
|
82
60
|
end
|
data/lib/rom/memory/commands.rb
CHANGED
@@ -11,6 +11,7 @@ module ROM
|
|
11
11
|
# @api public
|
12
12
|
class Create < ROM::Commands::Create
|
13
13
|
adapter :memory
|
14
|
+
use :schema
|
14
15
|
|
15
16
|
# @see ROM::Commands::Create#execute
|
16
17
|
def execute(tuples)
|
@@ -28,6 +29,7 @@ module ROM
|
|
28
29
|
# @api public
|
29
30
|
class Update < ROM::Commands::Update
|
30
31
|
adapter :memory
|
32
|
+
use :schema
|
31
33
|
|
32
34
|
# @see ROM::Commands::Update#execute
|
33
35
|
def execute(params)
|
data/lib/rom/memory/relation.rb
CHANGED
data/lib/rom/memory/storage.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
rescue LoadError
|
4
|
-
raise LoadError, 'Please install the `thread_safe` gem.'
|
5
|
-
end
|
1
|
+
require 'concurrent/hash'
|
2
|
+
require 'concurrent/array'
|
6
3
|
|
7
4
|
require 'rom/memory/dataset'
|
8
5
|
|
@@ -21,7 +18,7 @@ module ROM
|
|
21
18
|
|
22
19
|
# @api private
|
23
20
|
def initialize
|
24
|
-
@data =
|
21
|
+
@data = Concurrent::Hash.new
|
25
22
|
end
|
26
23
|
|
27
24
|
# @return [Dataset]
|
@@ -37,7 +34,7 @@ module ROM
|
|
37
34
|
#
|
38
35
|
# @api private
|
39
36
|
def create_dataset(name)
|
40
|
-
data[name] = Dataset.new(
|
37
|
+
data[name] = Dataset.new(Concurrent::Array.new)
|
41
38
|
end
|
42
39
|
|
43
40
|
# Check if there's dataset under specified key
|
data/lib/rom/pipeline.rb
CHANGED
@@ -3,20 +3,34 @@ module ROM
|
|
3
3
|
#
|
4
4
|
# @api private
|
5
5
|
module Pipeline
|
6
|
-
#
|
6
|
+
# Common `>>` operator extension
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Relation::Composite
|
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
|
18
30
|
end
|
19
31
|
|
32
|
+
include Operator
|
33
|
+
|
20
34
|
# Send data through specified mappers
|
21
35
|
#
|
22
36
|
# @return [Relation::Composite]
|
@@ -24,7 +38,7 @@ module ROM
|
|
24
38
|
# @api public
|
25
39
|
def map_with(*names)
|
26
40
|
[self, *names.map { |name| mappers[name] }]
|
27
|
-
.reduce { |a, e|
|
41
|
+
.reduce { |a, e| composite_class.new(a, e) }
|
28
42
|
end
|
29
43
|
alias_method :as, :map_with
|
30
44
|
|
data/lib/rom/plugin_registry.rb
CHANGED
@@ -11,7 +11,7 @@ module ROM
|
|
11
11
|
#
|
12
12
|
# @api private
|
13
13
|
attr_reader :configuration
|
14
|
-
|
14
|
+
|
15
15
|
# Internal registry for command plugins
|
16
16
|
#
|
17
17
|
# @return [InternalPluginRegistry]
|
@@ -158,7 +158,7 @@ module ROM
|
|
158
158
|
# Return the plugin for a given adapter
|
159
159
|
#
|
160
160
|
# @param [Symbol] name The name of the plugin
|
161
|
-
# @param [Symbol]
|
161
|
+
# @param [Symbol] adapter_name (:default) The name of the adapter used
|
162
162
|
#
|
163
163
|
# @raises [UnknownPluginError] if no plugin is found with the given name
|
164
164
|
#
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ROM
|
2
|
+
module Plugins
|
3
|
+
module Command
|
4
|
+
# @api private
|
5
|
+
module Schema
|
6
|
+
def self.included(klass)
|
7
|
+
super
|
8
|
+
klass.extend(ClassInterface)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
module ClassInterface
|
13
|
+
# @see Command.build
|
14
|
+
# @api public
|
15
|
+
def build(relation, options = {})
|
16
|
+
if options.key?(:input) || !relation.schema?
|
17
|
+
super
|
18
|
+
else
|
19
|
+
super(relation, options.merge(input: relation.schema_hash))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rom/configuration_dsl'
|
2
|
+
require 'rom/support/deprecations'
|
2
3
|
|
3
4
|
module ROM
|
4
5
|
module ConfigurationPlugins
|
@@ -9,7 +10,7 @@ module ROM
|
|
9
10
|
|
10
11
|
# @api private
|
11
12
|
def self.apply(configuration, options = {})
|
12
|
-
|
13
|
+
ROM::Deprecations.announce(:macros, "Calling `use(:macros)` is no longer necessary. Macros are enabled by default.")
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -9,13 +9,28 @@ module ROM
|
|
9
9
|
# @return [Symbol]
|
10
10
|
#
|
11
11
|
# @api private
|
12
|
-
def foreign_key
|
13
|
-
|
12
|
+
def foreign_key(other = nil)
|
13
|
+
if other
|
14
|
+
if schema
|
15
|
+
rel_name = other.respond_to?(:to_sym) ?
|
16
|
+
ROM::Relation::Name[other.to_sym] : other.base_name
|
17
|
+
|
18
|
+
key = schema.foreign_key(rel_name.dataset)
|
19
|
+
key ? key.meta[:name] : __registry__[rel_name].foreign_key
|
20
|
+
else
|
21
|
+
relation = other.respond_to?(:to_sym) ?
|
22
|
+
__registry__[other] : other
|
23
|
+
|
24
|
+
relation.foreign_key
|
25
|
+
end
|
26
|
+
else
|
27
|
+
:"#{Inflector.singularize(name.dataset)}_id"
|
28
|
+
end
|
14
29
|
end
|
15
30
|
|
16
31
|
# Return base name which defaults to name attribute
|
17
32
|
#
|
18
|
-
# @return [
|
33
|
+
# @return [ROM::Relation::Name]
|
19
34
|
#
|
20
35
|
# @api private
|
21
36
|
def base_name
|