rom-core 4.0.0.beta3 → 4.0.0.rc1
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/CHANGELOG.md +3 -1
- data/lib/rom/association_set.rb +3 -0
- data/lib/rom/associations/abstract.rb +72 -1
- data/lib/rom/associations/definitions/abstract.rb +22 -6
- data/lib/rom/associations/definitions/many_to_many.rb +3 -0
- data/lib/rom/associations/definitions/many_to_one.rb +1 -0
- data/lib/rom/associations/definitions/one_to_many.rb +1 -0
- data/lib/rom/associations/definitions/one_to_one.rb +1 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +1 -0
- data/lib/rom/associations/many_to_many.rb +44 -0
- data/lib/rom/associations/many_to_one.rb +26 -0
- data/lib/rom/associations/one_to_many.rb +26 -0
- data/lib/rom/associations/one_to_one.rb +3 -0
- data/lib/rom/associations/one_to_one_through.rb +3 -0
- data/lib/rom/attribute.rb +2 -2
- data/lib/rom/auto_curry.rb +11 -0
- data/lib/rom/cache.rb +29 -0
- data/lib/rom/command_compiler.rb +4 -4
- data/lib/rom/command_registry.rb +9 -5
- data/lib/rom/commands/class_interface.rb +7 -7
- data/lib/rom/commands/graph/input_evaluator.rb +33 -3
- data/lib/rom/commands/lazy.rb +4 -0
- data/lib/rom/commands/lazy/create.rb +10 -0
- data/lib/rom/commands/lazy/delete.rb +10 -0
- data/lib/rom/commands/lazy/update.rb +10 -0
- data/lib/rom/configuration.rb +34 -14
- data/lib/rom/configuration_dsl.rb +0 -2
- data/lib/rom/constants.rb +10 -0
- data/lib/rom/container.rb +16 -0
- data/lib/rom/create_container.rb +7 -0
- data/lib/rom/environment.rb +3 -2
- data/lib/rom/gateway.rb +16 -1
- data/lib/rom/global.rb +1 -1
- data/lib/rom/global/plugin_dsl.rb +3 -1
- data/lib/rom/initializer.rb +25 -13
- data/lib/rom/mapper_registry.rb +4 -1
- data/lib/rom/memory/dataset.rb +29 -2
- data/lib/rom/memory/schema.rb +7 -0
- data/lib/rom/plugin_base.rb +1 -1
- data/lib/rom/plugin_registry.rb +2 -2
- data/lib/rom/plugins/command/schema.rb +7 -0
- data/lib/rom/plugins/relation/instrumentation.rb +10 -0
- data/lib/rom/plugins/relation/registry_reader.rb +0 -3
- data/lib/rom/registry.rb +15 -3
- data/lib/rom/relation.rb +38 -23
- data/lib/rom/relation/class_interface.rb +15 -6
- data/lib/rom/relation/combined.rb +7 -2
- data/lib/rom/relation/curried.rb +23 -0
- data/lib/rom/relation/graph.rb +25 -14
- data/lib/rom/relation/loaded.rb +7 -4
- data/lib/rom/relation/materializable.rb +2 -2
- data/lib/rom/relation/view_dsl.rb +2 -1
- data/lib/rom/relation/wrap.rb +14 -0
- data/lib/rom/relation_registry.rb +2 -0
- data/lib/rom/schema.rb +25 -4
- data/lib/rom/schema/associations_dsl.rb +9 -0
- data/lib/rom/schema/dsl.rb +27 -4
- data/lib/rom/setup.rb +20 -7
- data/lib/rom/setup/auto_registration.rb +27 -0
- data/lib/rom/setup/auto_registration_strategies/base.rb +7 -2
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +17 -0
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +11 -0
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +9 -0
- data/lib/rom/setup/finalize/finalize_mappers.rb +4 -2
- data/lib/rom/setup/finalize/finalize_relations.rb +1 -1
- data/lib/rom/support/configurable.rb +19 -0
- data/lib/rom/support/notifications.rb +29 -2
- data/lib/rom/types.rb +53 -8
- data/lib/rom/version.rb +1 -1
- metadata +7 -13
data/lib/rom/relation/loaded.rb
CHANGED
@@ -2,7 +2,9 @@ require 'dry/equalizer'
|
|
2
2
|
|
3
3
|
module ROM
|
4
4
|
class Relation
|
5
|
-
# Materializes a relation and exposes interface to access the data
|
5
|
+
# Materializes a relation and exposes interface to access the data.
|
6
|
+
#
|
7
|
+
# This relation type is returned when a lazy relation is called
|
6
8
|
#
|
7
9
|
# @api public
|
8
10
|
class Loaded
|
@@ -41,8 +43,8 @@ module ROM
|
|
41
43
|
# @yield [Hash]
|
42
44
|
#
|
43
45
|
# @api public
|
44
|
-
def each
|
45
|
-
return to_enum unless
|
46
|
+
def each
|
47
|
+
return to_enum unless block_given?
|
46
48
|
collection.each { |tuple| yield(tuple) }
|
47
49
|
end
|
48
50
|
|
@@ -86,7 +88,8 @@ module ROM
|
|
86
88
|
# @param [Symbol] key The key name
|
87
89
|
#
|
88
90
|
# @return [Array]
|
89
|
-
#
|
91
|
+
#
|
92
|
+
# @raise KeyError when provided key doesn't exist in any of the tuples
|
90
93
|
#
|
91
94
|
# @api public
|
92
95
|
def pluck(key)
|
@@ -4,7 +4,8 @@ module ROM
|
|
4
4
|
#
|
5
5
|
# This is used to establish pre-defined relation views with explicit schemas.
|
6
6
|
# Such views can be used to compose relations together, even from multiple
|
7
|
-
# adapters.
|
7
|
+
# adapters. In advanced adapters like rom-sql using view DSL is not required though,
|
8
|
+
# as relation schemas are dynamic and they always represent current tuple structure.
|
8
9
|
#
|
9
10
|
# @api public
|
10
11
|
class ViewDSL
|
data/lib/rom/relation/wrap.rb
CHANGED
@@ -7,13 +7,23 @@ module ROM
|
|
7
7
|
#
|
8
8
|
# @api public
|
9
9
|
class Wrap < Graph
|
10
|
+
# Wrap more relations
|
11
|
+
#
|
12
|
+
# @see Relation#wrap
|
13
|
+
#
|
14
|
+
# @return [Wrap]
|
15
|
+
#
|
10
16
|
# @api public
|
11
17
|
def wrap(*args)
|
12
18
|
self.class.new(root, nodes + root.wrap(*args).nodes)
|
13
19
|
end
|
14
20
|
|
21
|
+
# Materialize a wrap
|
22
|
+
#
|
15
23
|
# @see Relation#call
|
16
24
|
#
|
25
|
+
# @return [Loaded]
|
26
|
+
#
|
17
27
|
# @api public
|
18
28
|
def call(*args)
|
19
29
|
if auto_map?
|
@@ -23,6 +33,10 @@ module ROM
|
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
36
|
+
# Return an adapter-specific relation representing a wrap
|
37
|
+
#
|
38
|
+
# @abstract
|
39
|
+
#
|
26
40
|
# @api private
|
27
41
|
def relation
|
28
42
|
raise NotImplementedError
|
data/lib/rom/schema.rb
CHANGED
@@ -85,6 +85,10 @@ module ROM
|
|
85
85
|
# @api private
|
86
86
|
option :relations, default: -> { EMPTY_HASH }
|
87
87
|
|
88
|
+
# @!attribute [r] canonical
|
89
|
+
# @return [Symbol] The canonical schema which is carried in all schema instances
|
90
|
+
option :canonical, default: -> { self }
|
91
|
+
|
88
92
|
# @api private
|
89
93
|
option :attr_class, default: -> { Attribute }
|
90
94
|
|
@@ -137,7 +141,7 @@ module ROM
|
|
137
141
|
#
|
138
142
|
# Default implementation is a no-op and it simply returns back untouched relation
|
139
143
|
#
|
140
|
-
# @param [Relation]
|
144
|
+
# @param [Relation] relation
|
141
145
|
#
|
142
146
|
# @return [Relation]
|
143
147
|
#
|
@@ -293,7 +297,7 @@ module ROM
|
|
293
297
|
#
|
294
298
|
# This returns a new schema instance
|
295
299
|
#
|
296
|
-
# @param [
|
300
|
+
# @param [Array<Attribute>] new_attributes
|
297
301
|
#
|
298
302
|
# @return [Schema]
|
299
303
|
#
|
@@ -304,8 +308,6 @@ module ROM
|
|
304
308
|
|
305
309
|
# Return a new schema with uniq attributes
|
306
310
|
#
|
307
|
-
# @param [*Array<Attribute>]
|
308
|
-
#
|
309
311
|
# @return [Schema]
|
310
312
|
#
|
311
313
|
# @api public
|
@@ -328,6 +330,19 @@ module ROM
|
|
328
330
|
! attributes.detect { |attr| attr.name == name }.nil?
|
329
331
|
end
|
330
332
|
|
333
|
+
# Return if a schema is canonical
|
334
|
+
#
|
335
|
+
# @return [Boolean]
|
336
|
+
#
|
337
|
+
# @api public
|
338
|
+
def canonical?
|
339
|
+
self.equal?(canonical)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Finalize a schema
|
343
|
+
#
|
344
|
+
# @return [self]
|
345
|
+
#
|
331
346
|
# @api private
|
332
347
|
def finalize!(**opts)
|
333
348
|
return self if frozen?
|
@@ -352,6 +367,12 @@ module ROM
|
|
352
367
|
self
|
353
368
|
end
|
354
369
|
|
370
|
+
# Finalize associations defined in a schema
|
371
|
+
#
|
372
|
+
# @param [RelationRegistry] relations
|
373
|
+
#
|
374
|
+
# @return [self]
|
375
|
+
#
|
355
376
|
# @api private
|
356
377
|
def finalize_associations!(relations:)
|
357
378
|
set!(:associations, yield) if associations.any?
|
@@ -30,16 +30,25 @@ module ROM
|
|
30
30
|
# @example using relation identifier
|
31
31
|
# has_many :tasks
|
32
32
|
#
|
33
|
+
# @example setting custom foreign key name
|
34
|
+
# has_many :tasks, foreign_key: :assignee_id
|
35
|
+
#
|
33
36
|
# @example with a :through option
|
34
37
|
# # this establishes many-to-many association
|
35
38
|
# has_many :tasks, through: :users_tasks
|
36
39
|
#
|
40
|
+
# @example using a custom view which overrides default one
|
41
|
+
# has_many :posts, view: :published, override: true
|
42
|
+
#
|
37
43
|
# @example using aliased association with a custom view
|
38
44
|
# has_many :posts, as: :published_posts, view: :published
|
39
45
|
#
|
40
46
|
# @example using custom target relation
|
41
47
|
# has_many :user_posts, relation: :posts
|
42
48
|
#
|
49
|
+
# @example using custom target relation and an alias
|
50
|
+
# has_many :user_posts, relation: :posts, as: :published, view: :published
|
51
|
+
#
|
43
52
|
# @param [Symbol] target The target relation identifier
|
44
53
|
# @param [Hash] options A hash with additional options
|
45
54
|
#
|
data/lib/rom/schema/dsl.rb
CHANGED
@@ -5,10 +5,9 @@ require 'rom/attribute'
|
|
5
5
|
require 'rom/schema/associations_dsl'
|
6
6
|
|
7
7
|
module ROM
|
8
|
-
# Relation schema
|
9
|
-
#
|
10
|
-
# @api public
|
11
8
|
class Schema
|
9
|
+
# Schema DSL exposed as `schema { .. }` in relation classes
|
10
|
+
#
|
12
11
|
# @api public
|
13
12
|
class DSL < BasicObject
|
14
13
|
KERNEL_METHODS = %i(extend method).freeze
|
@@ -16,17 +15,41 @@ module ROM
|
|
16
15
|
|
17
16
|
extend Initializer
|
18
17
|
|
18
|
+
# @!attribute [r] relation
|
19
|
+
# @return [Relation::Name] The name of the schema's relation
|
19
20
|
param :relation
|
20
21
|
|
22
|
+
# @!attribute [r] inferrer
|
23
|
+
# @return [Inferrer] Optional attribute inferrer
|
21
24
|
option :inferrer, default: -> { DEFAULT_INFERRER }
|
22
25
|
|
26
|
+
# @!attribute [r] schema_class
|
27
|
+
# @return [Class] Schema class that should be instantiated
|
23
28
|
option :schema_class, default: -> { Schema }
|
24
29
|
|
30
|
+
# @!attribute [r] attr_class
|
31
|
+
# @return [Class] Attribute class that should be used
|
25
32
|
option :attr_class, default: -> { Attribute }
|
26
33
|
|
34
|
+
# @!attribute [r] adapter
|
35
|
+
# @return [Symbol] The adapter identifier used in gateways
|
27
36
|
option :adapter, default: -> { :default }
|
28
37
|
|
29
|
-
|
38
|
+
# @!attribute [r] attributes
|
39
|
+
# @return [Hash] A hash with attributes defined by the DSL
|
40
|
+
attr_reader :attributes
|
41
|
+
|
42
|
+
# @!attribute [r] plugins
|
43
|
+
# @return [Hash] A hash with schema plugins enabled in a schema
|
44
|
+
attr_reader :plugins
|
45
|
+
|
46
|
+
# @!attribute [r] definition
|
47
|
+
# @return [Proc] Definition block passed to DSL
|
48
|
+
attr_reader :definition
|
49
|
+
|
50
|
+
# @!attribute [r] associations_dsl
|
51
|
+
# @return [AssociationDSL] Associations defined within a block
|
52
|
+
attr_reader :associations_dsl
|
30
53
|
|
31
54
|
# @api private
|
32
55
|
def initialize(*, &block)
|
data/lib/rom/setup.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'rom/setup/auto_registration'
|
2
2
|
|
3
3
|
module ROM
|
4
|
+
# Setup objects collect component classes during setup/finalization process
|
5
|
+
#
|
6
|
+
# @api public
|
4
7
|
class Setup
|
5
8
|
# @return [Array] registered relation subclasses
|
6
9
|
#
|
@@ -32,6 +35,23 @@ module ROM
|
|
32
35
|
@notifications = notifications
|
33
36
|
end
|
34
37
|
|
38
|
+
# Enable auto-registration for a given setup object
|
39
|
+
#
|
40
|
+
# @param [String, Pathname] directory The root path to components
|
41
|
+
# @param [Hash] options
|
42
|
+
# @option options [Boolean, String] :namespace Enable/disable namespace or provide a custom namespace name
|
43
|
+
#
|
44
|
+
# @return [Setup]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def auto_registration(directory, options = {})
|
48
|
+
auto_registration = AutoRegistration.new(directory, options)
|
49
|
+
auto_registration.relations.map { |r| register_relation(r) }
|
50
|
+
auto_registration.commands.map { |r| register_command(r) }
|
51
|
+
auto_registration.mappers.map { |r| register_mapper(r) }
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
35
55
|
# Relation sub-classes are being registered with this method during setup
|
36
56
|
#
|
37
57
|
# @api private
|
@@ -57,12 +77,5 @@ module ROM
|
|
57
77
|
def register_plugin(plugin)
|
58
78
|
plugins << plugin
|
59
79
|
end
|
60
|
-
|
61
|
-
def auto_registration(directory, options = {})
|
62
|
-
auto_registration = AutoRegistration.new(directory, options)
|
63
|
-
auto_registration.relations.map { |r| register_relation(r) }
|
64
|
-
auto_registration.commands.map { |r| register_command(r) }
|
65
|
-
auto_registration.mappers.map { |r| register_mapper(r) }
|
66
|
-
end
|
67
80
|
end
|
68
81
|
end
|
@@ -9,23 +9,38 @@ require 'rom/setup/auto_registration_strategies/with_namespace'
|
|
9
9
|
require 'rom/setup/auto_registration_strategies/custom_namespace'
|
10
10
|
|
11
11
|
module ROM
|
12
|
+
# AutoRegistration is used to load component files automatically from the provided directory path
|
13
|
+
#
|
14
|
+
# @api public
|
12
15
|
class AutoRegistration
|
13
16
|
extend Initializer
|
14
17
|
|
15
18
|
NamespaceType = Types::Strict::Bool | Types::Strict::String
|
19
|
+
|
16
20
|
PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname))
|
21
|
+
|
17
22
|
DEFAULT_MAPPING = {
|
18
23
|
relations: :relations,
|
19
24
|
mappers: :mappers,
|
20
25
|
commands: :commands
|
21
26
|
}.freeze
|
22
27
|
|
28
|
+
# @!attribute [r] directory
|
29
|
+
# @return [Pathname] The root path
|
23
30
|
param :directory, type: PathnameType
|
24
31
|
|
32
|
+
# @!attribute [r] namespace
|
33
|
+
# @return [Boolean,String]
|
34
|
+
# The name of the top level namespace or true/false which
|
35
|
+
# enables/disables default top level namespace inferred from the dir name
|
25
36
|
option :namespace, type: NamespaceType, default: -> { true }
|
26
37
|
|
38
|
+
# @!attribute [r] component_dirs
|
39
|
+
# @return [Hash] component => dir-name map
|
27
40
|
option :component_dirs, type: Types::Strict::Hash, default: -> { DEFAULT_MAPPING }
|
28
41
|
|
42
|
+
# @!attribute [r] globs
|
43
|
+
# @return [Hash] File globbing functions for each component dir
|
29
44
|
option :globs, default: -> {
|
30
45
|
Hash[
|
31
46
|
component_dirs.map { |component, path|
|
@@ -34,20 +49,32 @@ module ROM
|
|
34
49
|
]
|
35
50
|
}
|
36
51
|
|
52
|
+
# Load relation files
|
53
|
+
#
|
54
|
+
# @api private
|
37
55
|
def relations
|
38
56
|
load_entities(:relations)
|
39
57
|
end
|
40
58
|
|
59
|
+
# Load command files
|
60
|
+
#
|
61
|
+
# @api private
|
41
62
|
def commands
|
42
63
|
load_entities(:commands)
|
43
64
|
end
|
44
65
|
|
66
|
+
# Load mapper files
|
67
|
+
#
|
68
|
+
# @api private
|
45
69
|
def mappers
|
46
70
|
load_entities(:mappers)
|
47
71
|
end
|
48
72
|
|
49
73
|
private
|
50
74
|
|
75
|
+
# Load given component files
|
76
|
+
#
|
77
|
+
# @api private
|
51
78
|
def load_entities(entity)
|
52
79
|
Dir[globs[entity]].map do |file|
|
53
80
|
require file
|
@@ -3,14 +3,19 @@ require 'rom/initializer'
|
|
3
3
|
|
4
4
|
module ROM
|
5
5
|
module AutoRegistrationStrategies
|
6
|
+
# Base class for registration strategies
|
7
|
+
#
|
8
|
+
# @api private
|
6
9
|
class Base
|
7
10
|
extend Initializer
|
8
11
|
|
9
12
|
PathnameType = Types.Definition(Pathname).constrained(type: Pathname)
|
10
13
|
|
11
|
-
option :file, type: Types::Strict::String
|
12
|
-
|
13
14
|
EXTENSION_REGEX = /\.rb\z/
|
15
|
+
|
16
|
+
# @!attribute [r] file
|
17
|
+
# @return [String] Name of a component file
|
18
|
+
option :file, type: Types::Strict::String
|
14
19
|
end
|
15
20
|
end
|
16
21
|
end
|
@@ -6,10 +6,22 @@ require 'rom/setup/auto_registration_strategies/base'
|
|
6
6
|
|
7
7
|
module ROM
|
8
8
|
module AutoRegistrationStrategies
|
9
|
+
# Custom namespace strategy loads components and assumes they are defined
|
10
|
+
# within the provided namespace
|
11
|
+
#
|
12
|
+
# @api private
|
9
13
|
class CustomNamespace < Base
|
14
|
+
# @!attribute [r] directory
|
15
|
+
# @return [Pathname] The path to dir with components
|
10
16
|
option :directory, type: PathnameType
|
17
|
+
|
18
|
+
# @!attribute [r] namespace
|
19
|
+
# @return [String] Name of a namespace
|
11
20
|
option :namespace, type: Types::Strict::String
|
12
21
|
|
22
|
+
# Loads components
|
23
|
+
#
|
24
|
+
# @api private
|
13
25
|
def call
|
14
26
|
potential = []
|
15
27
|
attempted = []
|
@@ -37,24 +49,29 @@ module ROM
|
|
37
49
|
|
38
50
|
private
|
39
51
|
|
52
|
+
# @api private
|
40
53
|
def name_error_message(attempted)
|
41
54
|
"required file does not define expected constant name; either " \
|
42
55
|
"register your constant explicitly of try following the path" \
|
43
56
|
"naming convention like:\n\n\t- #{attempted.join("\n\t- ")}\n"
|
44
57
|
end
|
45
58
|
|
59
|
+
# @api private
|
46
60
|
def filename
|
47
61
|
Pathname(file).basename('.rb')
|
48
62
|
end
|
49
63
|
|
64
|
+
# @api private
|
50
65
|
def ns_const
|
51
66
|
@namespace_constant ||= Dry::Core::Inflector.constantize(namespace)
|
52
67
|
end
|
53
68
|
|
69
|
+
# @api private
|
54
70
|
def path_arr
|
55
71
|
file_path << filename
|
56
72
|
end
|
57
73
|
|
74
|
+
# @api private
|
58
75
|
def file_path
|
59
76
|
File.dirname(file).split("/") - directory.to_s.split("/")
|
60
77
|
end
|