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
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'dry/equalizer'
|
2
|
+
require 'concurrent/map'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
class Relation
|
6
|
+
# Relation name container
|
7
|
+
#
|
8
|
+
# This is a simple struct with two fields.
|
9
|
+
# It handles both relation registration name (i.e. Symbol) and dataset name.
|
10
|
+
# The reason we need it is a simplification of passing around these two objects.
|
11
|
+
# It is quite common to have a dataset named differently from a relation
|
12
|
+
# built on top if you are dealing with a legacy DB and often you need both
|
13
|
+
# to support things such as associations (rom-sql as an example).
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
class Name
|
17
|
+
include Dry::Equalizer(:relation, :dataset)
|
18
|
+
|
19
|
+
# Coerce an object to a Name instance
|
20
|
+
#
|
21
|
+
# @return [ROM::Relation::Name]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def self.[](*args)
|
25
|
+
cache.fetch_or_store(args.hash) do
|
26
|
+
relation, dataset = args
|
27
|
+
|
28
|
+
if relation.is_a?(Name)
|
29
|
+
relation
|
30
|
+
else
|
31
|
+
new(relation, dataset)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def self.cache
|
38
|
+
@cache ||= Concurrent::Map.new
|
39
|
+
end
|
40
|
+
|
41
|
+
# Relation registration name
|
42
|
+
#
|
43
|
+
# @return [Symbol]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
attr_reader :relation
|
47
|
+
|
48
|
+
# Underlying dataset name
|
49
|
+
#
|
50
|
+
# @return [Symbol]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
attr_reader :dataset
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def initialize(relation, dataset = nil)
|
57
|
+
@relation = relation
|
58
|
+
@dataset = dataset || relation
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return relation name
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def to_s
|
67
|
+
if relation == dataset
|
68
|
+
relation
|
69
|
+
else
|
70
|
+
"#{relation} on #{dataset}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Alias for registration key implicitly called by ROM::Registry
|
75
|
+
#
|
76
|
+
# @return [Symbol]
|
77
|
+
#
|
78
|
+
# @api private
|
79
|
+
def to_sym
|
80
|
+
relation
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return inspected relation
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def inspect
|
89
|
+
"#{self.class.name}(#{to_s})"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Build a new name. Useful for Curried and other relation proxies
|
93
|
+
#
|
94
|
+
# @return [ROM::Relation::Name]
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
def with(relation)
|
98
|
+
self.class[relation, dataset]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/rom/schema.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
|
3
|
+
require 'rom/support/constants'
|
4
|
+
require 'rom/schema/dsl'
|
5
|
+
require 'rom/association_set'
|
6
|
+
|
7
|
+
module ROM
|
8
|
+
# Relation schema
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Schema
|
12
|
+
EMPTY_ASSOCIATION_SET = AssociationSet.new(EMPTY_HASH).freeze
|
13
|
+
|
14
|
+
include Dry::Equalizer(:name, :attributes, :associations)
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
# @!attribute [r] name
|
18
|
+
# @return [Symbol] The name of this schema
|
19
|
+
attr_reader :name
|
20
|
+
|
21
|
+
# @!attribute [r] attributes
|
22
|
+
# @return [Hash] The hash with schema attribute types
|
23
|
+
attr_reader :attributes
|
24
|
+
|
25
|
+
# @!attribute [r] associations
|
26
|
+
# @return [AssociationSet] Optional association set (this is adapter-specific)
|
27
|
+
attr_reader :associations
|
28
|
+
|
29
|
+
# @!attribute [r] inferrer
|
30
|
+
# @return [#call] An optional inferrer object used in `finalize!`
|
31
|
+
attr_reader :inferrer
|
32
|
+
|
33
|
+
# @!attribute [r] primary_key
|
34
|
+
# @return [Array<Dry::Types::Definition] Primary key array
|
35
|
+
attr_reader :primary_key
|
36
|
+
|
37
|
+
alias_method :to_h, :attributes
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
def initialize(name, attributes, inferrer: nil, associations: EMPTY_ASSOCIATION_SET)
|
41
|
+
@name = name
|
42
|
+
@attributes = attributes
|
43
|
+
@associations = associations
|
44
|
+
@inferrer = inferrer
|
45
|
+
end
|
46
|
+
|
47
|
+
# Iterate over schema's attributes
|
48
|
+
#
|
49
|
+
# @yield [Dry::Data::Type]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def each(&block)
|
53
|
+
attributes.each_value(&block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return attribute
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def [](name)
|
60
|
+
attributes.fetch(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return FK attribute for a given relation name
|
64
|
+
#
|
65
|
+
# @return [Dry::Types::Definition]
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def foreign_key(relation)
|
69
|
+
detect { |attr| attr.meta[:foreign_key] && attr.meta[:relation] == relation }
|
70
|
+
end
|
71
|
+
|
72
|
+
# This hook is called when relation is being build during container finalization
|
73
|
+
#
|
74
|
+
# When block is provided it'll be called just before freezing the instance
|
75
|
+
# so that additional ivars can be set
|
76
|
+
#
|
77
|
+
# @return [self]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def finalize!(gateway = nil, &block)
|
81
|
+
@attributes = inferrer.call(name.dataset, gateway) if inferrer
|
82
|
+
@primary_key = select { |attr| attr.meta[:primary_key] == true }
|
83
|
+
block.call if block
|
84
|
+
freeze
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
require 'rom/types'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
# Relation schema
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
class Schema
|
9
|
+
include Dry::Equalizer(:name, :attributes)
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :name, :attributes, :inferrer
|
13
|
+
|
14
|
+
# @api public
|
15
|
+
class DSL < BasicObject
|
16
|
+
attr_reader :name, :attributes, :inferrer
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def initialize(name, inferrer, &block)
|
20
|
+
@name = name
|
21
|
+
@inferrer = inferrer
|
22
|
+
@attributes = nil
|
23
|
+
|
24
|
+
if block
|
25
|
+
instance_exec(&block)
|
26
|
+
elsif inferrer.nil?
|
27
|
+
raise ArgumentError,
|
28
|
+
'You must pass a block to define a schema or set an inferrer for automatic inferring'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Defines a relation attribute with its type
|
33
|
+
#
|
34
|
+
# @see Relation.schema
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def attribute(name, type)
|
38
|
+
@attributes ||= {}
|
39
|
+
@attributes[name] = type.meta(name: name)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Specify which key(s) should be the primary key
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def primary_key(*names)
|
46
|
+
names.each do |name|
|
47
|
+
attributes[name] = attributes[name].meta(primary_key: true)
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# @api private
|
53
|
+
def call
|
54
|
+
Schema.new(name, attributes, inferrer: inferrer && inferrer.new(self))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -14,9 +14,9 @@ module ROM
|
|
14
14
|
|
15
15
|
def initialize(directory, options = EMPTY_HASH)
|
16
16
|
super
|
17
|
-
@directory = directory
|
17
|
+
@directory = Pathname(directory)
|
18
18
|
@globs = Hash[[:relations, :commands, :mappers].map { |name|
|
19
|
-
[name,
|
19
|
+
[name, @directory.join("#{name}/**/*.rb")]
|
20
20
|
}]
|
21
21
|
end
|
22
22
|
|
data/lib/rom/setup/finalize.rb
CHANGED
@@ -6,9 +6,9 @@ require 'rom/command_registry'
|
|
6
6
|
require 'rom/mapper_registry'
|
7
7
|
|
8
8
|
require 'rom/container'
|
9
|
-
require 'rom/setup/finalize/
|
10
|
-
require 'rom/setup/finalize/
|
11
|
-
require 'rom/setup/finalize/
|
9
|
+
require 'rom/setup/finalize/finalize_commands'
|
10
|
+
require 'rom/setup/finalize/finalize_relations'
|
11
|
+
require 'rom/setup/finalize/finalize_mappers'
|
12
12
|
|
13
13
|
# temporary
|
14
14
|
require 'rom/configuration_dsl/relation'
|
@@ -57,7 +57,7 @@ module ROM
|
|
57
57
|
#
|
58
58
|
# @api private
|
59
59
|
def run!
|
60
|
-
|
60
|
+
infer_relations
|
61
61
|
|
62
62
|
relations = load_relations
|
63
63
|
mappers = load_mappers
|
@@ -112,7 +112,7 @@ module ROM
|
|
112
112
|
# Relations explicitly defined are being skipped
|
113
113
|
#
|
114
114
|
# @api private
|
115
|
-
def
|
115
|
+
def infer_relations
|
116
116
|
datasets.each do |gateway, schema|
|
117
117
|
schema.each do |name|
|
118
118
|
if infer_relation?(gateway, name)
|
@@ -31,37 +31,17 @@ module ROM
|
|
31
31
|
gateway = @gateways[relation.class.gateway]
|
32
32
|
gateway.extend_command_class(klass, relation.dataset)
|
33
33
|
|
34
|
-
klass.
|
34
|
+
klass.extend_for_relation(relation) if klass.restrictable
|
35
35
|
|
36
36
|
(h[rel_name] ||= {})[name] = klass.build(relation)
|
37
37
|
end
|
38
38
|
|
39
39
|
commands = registry.each_with_object({}) do |(name, rel_commands), h|
|
40
|
-
h[name] = CommandRegistry.new(rel_commands)
|
40
|
+
h[name] = CommandRegistry.new(name, rel_commands)
|
41
41
|
end
|
42
42
|
|
43
43
|
Registry.new(commands)
|
44
44
|
end
|
45
|
-
|
46
|
-
# @api private
|
47
|
-
def relation_methods_mod(relation_class)
|
48
|
-
mod = Module.new
|
49
|
-
relation_class.view_methods.each do |meth|
|
50
|
-
mod.module_eval <<-RUBY
|
51
|
-
def #{meth}(*args)
|
52
|
-
response = relation.public_send(:#{meth}, *args)
|
53
|
-
|
54
|
-
if response.is_a?(relation.class)
|
55
|
-
new(response)
|
56
|
-
else
|
57
|
-
response
|
58
|
-
end
|
59
|
-
end
|
60
|
-
RUBY
|
61
|
-
end
|
62
|
-
|
63
|
-
mod
|
64
|
-
end
|
65
45
|
end
|
66
46
|
end
|
67
47
|
end
|
File without changes
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rom/relation_registry'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
class Finalize
|
5
|
+
class FinalizeRelations
|
6
|
+
# Build relation registry of specified descendant classes
|
7
|
+
#
|
8
|
+
# This is used by the setup
|
9
|
+
#
|
10
|
+
# @param [Hash] gateways
|
11
|
+
# @param [Array] relation_classes a list of relation descendants
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def initialize(gateways, relation_classes)
|
15
|
+
@gateways = gateways
|
16
|
+
@relation_classes = relation_classes
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
def run!
|
23
|
+
RelationRegistry.new do |registry, relations|
|
24
|
+
@relation_classes.each do |klass|
|
25
|
+
relation = build_relation(klass, registry)
|
26
|
+
|
27
|
+
key = relation.name.to_sym
|
28
|
+
|
29
|
+
if registry.key?(key)
|
30
|
+
raise RelationAlreadyDefinedError,
|
31
|
+
"Relation with `register_as #{key.inspect}` registered more " \
|
32
|
+
"than once"
|
33
|
+
end
|
34
|
+
|
35
|
+
relations[key] = relation
|
36
|
+
end
|
37
|
+
|
38
|
+
relations.each_value do |relation|
|
39
|
+
relation.class.finalize(registry, relation)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [ROM::Relation]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def build_relation(klass, registry)
|
48
|
+
# TODO: raise a meaningful error here and add spec covering the case
|
49
|
+
# where klass' gateway points to non-existant repo
|
50
|
+
gateway = @gateways.fetch(klass.gateway)
|
51
|
+
ds_proc = klass.dataset_proc || -> _ { self }
|
52
|
+
|
53
|
+
klass.schema.finalize!(gateway) if klass.schema
|
54
|
+
dataset = gateway.dataset(klass.dataset).instance_exec(klass, &ds_proc)
|
55
|
+
|
56
|
+
klass.new(dataset, __registry__: registry)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/rom/types.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'dry-types'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Types
|
5
|
+
include Dry::Types.module
|
6
|
+
|
7
|
+
def self.included(other)
|
8
|
+
other.extend(Methods)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
module Methods
|
13
|
+
def ForeignKey(relation, type = Types::Int)
|
14
|
+
type.meta(foreign_key: true, relation: relation)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rom/version.rb
CHANGED
data/log/.gitkeep
ADDED
File without changes
|
data/rom.gemspec
CHANGED
@@ -15,9 +15,11 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.test_files = `git ls-files -- {spec}/*`.split("\n")
|
16
16
|
gem.license = 'MIT'
|
17
17
|
|
18
|
+
gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
18
19
|
gem.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
19
|
-
gem.add_runtime_dependency '
|
20
|
-
gem.add_runtime_dependency 'rom-
|
20
|
+
gem.add_runtime_dependency 'dry-types', '~> 0.8'
|
21
|
+
gem.add_runtime_dependency 'rom-support', '~> 2.0'
|
22
|
+
gem.add_runtime_dependency 'rom-mapper', '~> 0.4.0'
|
21
23
|
|
22
24
|
gem.add_development_dependency 'rake', '~> 10.3'
|
23
25
|
gem.add_development_dependency 'rspec', '~> 3.3'
|