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