rom 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|