rom-core 4.0.0.beta2 → 4.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -1
- data/lib/rom/attribute.rb +427 -0
- data/lib/rom/auto_curry.rb +1 -1
- data/lib/rom/command.rb +9 -0
- data/lib/rom/command_proxy.rb +10 -2
- data/lib/rom/constants.rb +8 -0
- data/lib/rom/core.rb +0 -2
- data/lib/rom/gateway.rb +0 -11
- data/lib/rom/plugins/command/schema.rb +2 -4
- data/lib/rom/relation/class_interface.rb +13 -2
- data/lib/rom/relation/combined.rb +10 -4
- data/lib/rom/relation/commands.rb +28 -8
- data/lib/rom/relation/composite.rb +1 -3
- data/lib/rom/relation/curried.rb +10 -14
- data/lib/rom/relation/graph.rb +8 -0
- data/lib/rom/relation/materializable.rb +0 -7
- data/lib/rom/relation/name.rb +2 -2
- data/lib/rom/relation/wrap.rb +8 -27
- data/lib/rom/relation.rb +78 -15
- data/lib/rom/schema/dsl.rb +4 -2
- data/lib/rom/schema/inferrer.rb +4 -1
- data/lib/rom/schema.rb +13 -12
- data/lib/rom/setup/finalize/finalize_mappers.rb +5 -4
- data/lib/rom/setup/finalize/finalize_relations.rb +7 -10
- data/lib/rom/support/notifications.rb +92 -0
- data/lib/rom/version.rb +1 -1
- metadata +10 -5
- data/lib/rom/plugins/configuration/configuration_dsl.rb +0 -21
- data/lib/rom/schema/attribute.rb +0 -419
data/lib/rom/relation/curried.rb
CHANGED
@@ -20,7 +20,7 @@ module ROM
|
|
20
20
|
param :relation
|
21
21
|
|
22
22
|
option :view, type: Types::Strict::Symbol
|
23
|
-
option :arity, type: Types::Strict::Int
|
23
|
+
option :arity, type: Types::Strict::Int
|
24
24
|
option :curry_args, default: -> { EMPTY_ARRAY }
|
25
25
|
|
26
26
|
# Load relation if args match the arity
|
@@ -29,22 +29,18 @@ module ROM
|
|
29
29
|
#
|
30
30
|
# @api public
|
31
31
|
def call(*args)
|
32
|
-
|
33
|
-
all_args = curry_args + args
|
32
|
+
all_args = curry_args + args
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
if all_args.empty?
|
35
|
+
raise ArgumentError, "curried #{relation.class}##{view} relation was called without any arguments"
|
36
|
+
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
else
|
44
|
-
__new__(relation, curry_args: all_args)
|
45
|
-
end
|
38
|
+
if args.empty?
|
39
|
+
self
|
40
|
+
elsif arity == all_args.size
|
41
|
+
Loaded.new(relation.__send__(view, *all_args))
|
46
42
|
else
|
47
|
-
|
43
|
+
__new__(relation, curry_args: all_args)
|
48
44
|
end
|
49
45
|
end
|
50
46
|
alias_method :[], :call
|
data/lib/rom/relation/graph.rb
CHANGED
@@ -6,6 +6,7 @@ require 'rom/relation/loaded'
|
|
6
6
|
require 'rom/relation/composite'
|
7
7
|
require 'rom/relation/materializable'
|
8
8
|
require 'rom/pipeline'
|
9
|
+
require 'rom/support/memoizable'
|
9
10
|
|
10
11
|
module ROM
|
11
12
|
class Relation
|
@@ -15,6 +16,8 @@ module ROM
|
|
15
16
|
class Graph
|
16
17
|
extend Initializer
|
17
18
|
|
19
|
+
include Memoizable
|
20
|
+
|
18
21
|
param :root
|
19
22
|
|
20
23
|
param :nodes
|
@@ -70,6 +73,11 @@ module ROM
|
|
70
73
|
mappers[to_ast]
|
71
74
|
end
|
72
75
|
|
76
|
+
# @api private
|
77
|
+
memoize def to_ast
|
78
|
+
[:relation, [name.relation, attr_ast + nodes.map(&:to_ast), meta_ast]]
|
79
|
+
end
|
80
|
+
|
73
81
|
private
|
74
82
|
|
75
83
|
# @api private
|
data/lib/rom/relation/name.rb
CHANGED
@@ -71,7 +71,7 @@ module ROM
|
|
71
71
|
|
72
72
|
# @api private
|
73
73
|
def aliased?
|
74
|
-
|
74
|
+
aliaz && aliaz != relation
|
75
75
|
end
|
76
76
|
|
77
77
|
# Return relation name
|
@@ -80,7 +80,7 @@ module ROM
|
|
80
80
|
#
|
81
81
|
# @api private
|
82
82
|
def to_s
|
83
|
-
if
|
83
|
+
if aliased?
|
84
84
|
"#{relation} on #{dataset} as #{aliaz}"
|
85
85
|
elsif relation == dataset
|
86
86
|
relation.to_s
|
data/lib/rom/relation/wrap.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rom/relation/graph'
|
2
|
+
require 'rom/relation/combined'
|
2
3
|
|
3
4
|
module ROM
|
4
5
|
class Relation
|
@@ -6,18 +7,6 @@ module ROM
|
|
6
7
|
#
|
7
8
|
# @api public
|
8
9
|
class Wrap < Graph
|
9
|
-
extend Initializer
|
10
|
-
|
11
|
-
include Materializable
|
12
|
-
include Pipeline
|
13
|
-
include Pipeline::Proxy
|
14
|
-
|
15
|
-
param :root
|
16
|
-
param :nodes
|
17
|
-
|
18
|
-
alias_method :left, :root
|
19
|
-
alias_method :right, :nodes
|
20
|
-
|
21
10
|
# @api public
|
22
11
|
def wrap(*args)
|
23
12
|
self.class.new(root, nodes + root.wrap(*args).nodes)
|
@@ -39,21 +28,6 @@ module ROM
|
|
39
28
|
raise NotImplementedError
|
40
29
|
end
|
41
30
|
|
42
|
-
# @api private
|
43
|
-
def to_ast
|
44
|
-
@__ast__ ||= [:relation, [name.relation, attr_ast + nodes_ast, meta_ast]]
|
45
|
-
end
|
46
|
-
|
47
|
-
# @api private
|
48
|
-
def attr_ast
|
49
|
-
root.attr_ast
|
50
|
-
end
|
51
|
-
|
52
|
-
# @api private
|
53
|
-
def nodes_ast
|
54
|
-
nodes.map(&:to_ast)
|
55
|
-
end
|
56
|
-
|
57
31
|
# Return if this is a wrap relation
|
58
32
|
#
|
59
33
|
# @return [true]
|
@@ -62,6 +36,13 @@ module ROM
|
|
62
36
|
def wrap?
|
63
37
|
true
|
64
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @api private
|
43
|
+
def decorate?(other)
|
44
|
+
super || other.is_a?(Combined)
|
45
|
+
end
|
65
46
|
end
|
66
47
|
end
|
67
48
|
end
|
data/lib/rom/relation.rb
CHANGED
@@ -14,6 +14,7 @@ require 'rom/command_registry'
|
|
14
14
|
|
15
15
|
require 'rom/relation/loaded'
|
16
16
|
require 'rom/relation/curried'
|
17
|
+
require 'rom/relation/commands'
|
17
18
|
require 'rom/relation/composite'
|
18
19
|
require 'rom/relation/combined'
|
19
20
|
require 'rom/relation/wrap'
|
@@ -55,9 +56,69 @@ module ROM
|
|
55
56
|
|
56
57
|
extend Dry::Core::ClassAttributes
|
57
58
|
|
58
|
-
defines :adapter, :
|
59
|
+
defines :adapter, :schema_opts, :schema_class,
|
59
60
|
:schema_attr_class, :schema_inferrer, :schema_dsl,
|
60
|
-
:wrap_class
|
61
|
+
:wrap_class
|
62
|
+
|
63
|
+
# @!method self.gateway
|
64
|
+
# Manage the gateway
|
65
|
+
#
|
66
|
+
# @overload gateway
|
67
|
+
# Return the gateway key that the relation is associated with
|
68
|
+
# @return [Symbol]
|
69
|
+
#
|
70
|
+
# @overload gateway(gateway_key)
|
71
|
+
# Link the relation to a gateway. Change this setting if the
|
72
|
+
# relation is defined on a non-default gateway
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# class Users < ROM::Relation[:sql]
|
76
|
+
# gateway :custom
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# @param [Symbol] gateway_key
|
80
|
+
defines :gateway
|
81
|
+
|
82
|
+
# @!method self.auto_map
|
83
|
+
# Whether or not a relation and its compositions should be auto-mapped
|
84
|
+
#
|
85
|
+
# @overload auto_map
|
86
|
+
# Return auto_map setting value
|
87
|
+
# @return [Boolean]
|
88
|
+
#
|
89
|
+
# @overload auto_map(value)
|
90
|
+
# Set auto_map value
|
91
|
+
defines :auto_map
|
92
|
+
|
93
|
+
# @!method self.auto_struct
|
94
|
+
# Whether or not tuples should be auto-mapped to structs
|
95
|
+
#
|
96
|
+
# @overload auto_struct
|
97
|
+
# Return auto_struct setting value
|
98
|
+
# @return [Boolean]
|
99
|
+
#
|
100
|
+
# @overload auto_struct(value)
|
101
|
+
# Set auto_struct value
|
102
|
+
defines :auto_struct
|
103
|
+
|
104
|
+
# @!method self.struct_namespace
|
105
|
+
# Get or set a namespace for auto-generated struct classes.
|
106
|
+
# By default, new struct classes are created within ROM::Struct
|
107
|
+
#
|
108
|
+
# @example using custom namespace
|
109
|
+
# class Users < ROM::Relation[:sql]
|
110
|
+
# struct_namespace Entities
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# users.by_pk(1).one! # => #<Entities::User id=1 name="Jane Doe">
|
114
|
+
#
|
115
|
+
# @overload struct_namespace
|
116
|
+
# @return [Module] Default struct namespace
|
117
|
+
#
|
118
|
+
# @overload struct_namespace(namespace)
|
119
|
+
# @param [Module] namespace
|
120
|
+
#
|
121
|
+
defines :struct_namespace
|
61
122
|
|
62
123
|
gateway :default
|
63
124
|
|
@@ -67,7 +128,7 @@ module ROM
|
|
67
128
|
|
68
129
|
schema_opts EMPTY_HASH
|
69
130
|
schema_dsl Schema::DSL
|
70
|
-
schema_attr_class
|
131
|
+
schema_attr_class Attribute
|
71
132
|
schema_class Schema
|
72
133
|
schema_inferrer Schema::DEFAULT_INFERRER
|
73
134
|
|
@@ -109,12 +170,12 @@ module ROM
|
|
109
170
|
# @!attribute [r] auto_map
|
110
171
|
# @return [TrueClass,FalseClass] Whether or not a relation and its compositions should be auto-mapped
|
111
172
|
# @api private
|
112
|
-
option :auto_map,
|
173
|
+
option :auto_map, default: -> { self.class.auto_map }
|
113
174
|
|
114
175
|
# @!attribute [r] auto_struct
|
115
176
|
# @return [TrueClass,FalseClass] Whether or not tuples should be auto-mapped to structs
|
116
177
|
# @api private
|
117
|
-
option :auto_struct,
|
178
|
+
option :auto_struct, default: -> { self.class.auto_struct }
|
118
179
|
|
119
180
|
# @!attribute [r] struct_namespace
|
120
181
|
# @return [Module] Custom struct namespace
|
@@ -146,7 +207,7 @@ module ROM
|
|
146
207
|
# tasks_with_users[:title, :tasks]
|
147
208
|
# # => #<ROM::SQL::Attribute[String] primary_key=false name=:title source=ROM::Relation::Name(tasks)>
|
148
209
|
#
|
149
|
-
# @return [
|
210
|
+
# @return [Attribute]
|
150
211
|
#
|
151
212
|
# @api public
|
152
213
|
def [](name)
|
@@ -176,7 +237,7 @@ module ROM
|
|
176
237
|
#
|
177
238
|
# @overload combine(*associations)
|
178
239
|
# Composes relations using configured associations
|
179
|
-
|
240
|
+
#
|
180
241
|
# @example
|
181
242
|
# users.combine(:tasks, :posts)
|
182
243
|
# @param *associations [Array<Symbol>] A list of association names
|
@@ -334,17 +395,19 @@ module ROM
|
|
334
395
|
#
|
335
396
|
# @api public
|
336
397
|
def new(dataset, new_opts = EMPTY_HASH)
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
398
|
+
opts =
|
399
|
+
if new_opts.empty?
|
400
|
+
options
|
401
|
+
elsif new_opts.key?(:schema)
|
402
|
+
options.merge(new_opts).reject { |k, _| k == :input_schema || k == :output_schema }
|
403
|
+
else
|
404
|
+
options.merge(new_opts)
|
405
|
+
end
|
344
406
|
|
345
407
|
self.class.new(dataset, opts)
|
346
408
|
end
|
347
409
|
|
410
|
+
undef_method :with
|
348
411
|
# Returns a new instance with the same dataset but new options
|
349
412
|
#
|
350
413
|
# @example
|
@@ -417,7 +480,7 @@ module ROM
|
|
417
480
|
# Map tuples to the provided custom model class
|
418
481
|
#
|
419
482
|
# @example
|
420
|
-
# users.
|
483
|
+
# users.map_with(MyUserModel)
|
421
484
|
#
|
422
485
|
# @param [Class>] model Your custom model class
|
423
486
|
#
|
data/lib/rom/schema/dsl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'dry/equalizer'
|
2
2
|
|
3
3
|
require 'rom/types'
|
4
|
-
require 'rom/
|
4
|
+
require 'rom/attribute'
|
5
5
|
require 'rom/schema/associations_dsl'
|
6
6
|
|
7
7
|
module ROM
|
@@ -45,7 +45,7 @@ module ROM
|
|
45
45
|
# @api public
|
46
46
|
def attribute(name, type, options = EMPTY_HASH)
|
47
47
|
if attributes.key?(name)
|
48
|
-
::Kernel.raise ::ROM::
|
48
|
+
::Kernel.raise ::ROM::AttributeAlreadyDefinedError,
|
49
49
|
"Attribute #{ name.inspect } already defined"
|
50
50
|
end
|
51
51
|
|
@@ -93,6 +93,8 @@ module ROM
|
|
93
93
|
def build_type(name, type, options = EMPTY_HASH)
|
94
94
|
if options[:read]
|
95
95
|
type.meta(name: name, source: relation, read: options[:read])
|
96
|
+
elsif type.optional? && !type.meta[:read] && type.right.meta[:read]
|
97
|
+
type.meta(name: name, source: relation, read: type.right.meta[:read].optional)
|
96
98
|
else
|
97
99
|
type.meta(name: name, source: relation)
|
98
100
|
end
|
data/lib/rom/schema/inferrer.rb
CHANGED
@@ -11,7 +11,10 @@ module ROM
|
|
11
11
|
|
12
12
|
MissingAttributesError = Class.new(StandardError) do
|
13
13
|
def initialize(name, attributes)
|
14
|
-
super(
|
14
|
+
super(
|
15
|
+
"Following attributes in #{Relation::Name[name].relation.inspect} schema cannot "\
|
16
|
+
"be inferred and have to be defined explicitly: #{attributes.map(&:inspect).join(', ')}"
|
17
|
+
)
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
data/lib/rom/schema.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'dry/equalizer'
|
2
2
|
|
3
|
-
require 'rom/
|
3
|
+
require 'rom/constants'
|
4
|
+
require 'rom/attribute'
|
4
5
|
require 'rom/schema/dsl'
|
5
6
|
require 'rom/schema/inferrer'
|
6
7
|
require 'rom/association_set'
|
@@ -39,8 +40,10 @@ module ROM
|
|
39
40
|
registry = event[:registry]
|
40
41
|
|
41
42
|
registry.each do |_, relation|
|
42
|
-
relation.schema.
|
43
|
-
|
43
|
+
unless relation.schema.frozen?
|
44
|
+
relation.schema.finalize_associations!(relations: registry)
|
45
|
+
relation.schema.finalize!
|
46
|
+
end
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
@@ -58,8 +61,6 @@ module ROM
|
|
58
61
|
|
59
62
|
DEFAULT_INFERRER = Inferrer.new(enabled: false).freeze
|
60
63
|
|
61
|
-
AttributeAlreadyDefinedError = Class.new(StandardError)
|
62
|
-
|
63
64
|
extend Initializer
|
64
65
|
|
65
66
|
include Dry::Equalizer(:name, :attributes, :associations)
|
@@ -101,7 +102,7 @@ module ROM
|
|
101
102
|
# Define a relation schema from plain rom types
|
102
103
|
#
|
103
104
|
# Resulting schema will decorate plain rom types with adapter-specific types
|
104
|
-
# By default `
|
105
|
+
# By default `Attribute` will be used
|
105
106
|
#
|
106
107
|
# @param [Relation::Name, Symbol] name The schema name, typically ROM::Relation::Name
|
107
108
|
#
|
@@ -147,7 +148,7 @@ module ROM
|
|
147
148
|
|
148
149
|
# Iterate over schema's attributes
|
149
150
|
#
|
150
|
-
# @yield [
|
151
|
+
# @yield [Attribute]
|
151
152
|
#
|
152
153
|
# @api public
|
153
154
|
def each(&block)
|
@@ -197,7 +198,7 @@ module ROM
|
|
197
198
|
|
198
199
|
# Project a schema to include only specified attributes
|
199
200
|
#
|
200
|
-
# @param [*Array<Symbol,
|
201
|
+
# @param [*Array<Symbol, Attribute>] names Attribute names
|
201
202
|
#
|
202
203
|
# @return [Schema]
|
203
204
|
#
|
@@ -260,7 +261,7 @@ module ROM
|
|
260
261
|
|
261
262
|
# Return FK attribute for a given relation name
|
262
263
|
#
|
263
|
-
# @return [
|
264
|
+
# @return [Attribute]
|
264
265
|
#
|
265
266
|
# @api public
|
266
267
|
def foreign_key(relation)
|
@@ -269,7 +270,7 @@ module ROM
|
|
269
270
|
|
270
271
|
# Return primary key attributes
|
271
272
|
#
|
272
|
-
# @return [Array<
|
273
|
+
# @return [Array<Attribute>]
|
273
274
|
#
|
274
275
|
# @api public
|
275
276
|
def primary_key
|
@@ -292,7 +293,7 @@ module ROM
|
|
292
293
|
#
|
293
294
|
# This returns a new schema instance
|
294
295
|
#
|
295
|
-
# @param [*Array<
|
296
|
+
# @param [*Array<Attribute>]
|
296
297
|
#
|
297
298
|
# @return [Schema]
|
298
299
|
#
|
@@ -303,7 +304,7 @@ module ROM
|
|
303
304
|
|
304
305
|
# Return a new schema with uniq attributes
|
305
306
|
#
|
306
|
-
# @param [*Array<
|
307
|
+
# @param [*Array<Attribute>]
|
307
308
|
#
|
308
309
|
# @return [Schema]
|
309
310
|
#
|
@@ -36,13 +36,14 @@ module ROM
|
|
36
36
|
private
|
37
37
|
|
38
38
|
def check_duplicate_registered_mappers
|
39
|
-
|
40
|
-
|
39
|
+
mapper_relation_register = mapper_classes.map {|mapper_class| [mapper_class.relation, mapper_class.register_as].compact }
|
40
|
+
return if mapper_relation_register.uniq.count == mapper_classes.count
|
41
|
+
mapper_relation_register.select { |relation_register_as| mapper_relation_register.count(relation_register_as) > 1 }
|
41
42
|
.uniq
|
42
43
|
.each do |duplicated_mappers|
|
43
44
|
raise MapperAlreadyDefinedError,
|
44
|
-
"Mapper with `register_as #{duplicated_mappers.inspect}` registered more " \
|
45
|
-
"than once"
|
45
|
+
"Mapper with `register_as #{duplicated_mappers.last.inspect}` registered more " \
|
46
|
+
"than once for relation #{duplicated_mappers.first.inspect}"
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
@@ -71,19 +71,17 @@ module ROM
|
|
71
71
|
# where klass' gateway points to non-existant repo
|
72
72
|
gateway = @gateways.fetch(klass.gateway)
|
73
73
|
|
74
|
-
|
75
|
-
plugins = schema_plugins
|
74
|
+
plugins = schema_plugins
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
klass.set_schema!(resolved_schema)
|
76
|
+
schema = klass.schema_proc.call do
|
77
|
+
plugins.each { |plugin| app_plugin(plugin) }
|
82
78
|
end
|
83
79
|
|
80
|
+
klass.set_schema!(schema) if klass.schema.nil?
|
81
|
+
|
84
82
|
notifications.trigger(
|
85
83
|
'configuration.relations.schema.allocated',
|
86
|
-
schema:
|
84
|
+
schema: schema, gateway: gateway, registry: registry
|
87
85
|
)
|
88
86
|
|
89
87
|
relation_plugins.each do |plugin|
|
@@ -92,10 +90,9 @@ module ROM
|
|
92
90
|
|
93
91
|
notifications.trigger(
|
94
92
|
'configuration.relations.schema.set',
|
95
|
-
schema:
|
93
|
+
schema: schema, relation: klass, adapter: klass.adapter
|
96
94
|
)
|
97
95
|
|
98
|
-
schema = klass.schema
|
99
96
|
rel_key = schema.name.to_sym
|
100
97
|
dataset = gateway.dataset(schema.name.dataset).instance_exec(klass, &klass.dataset)
|
101
98
|
|
@@ -3,16 +3,61 @@ require 'dry/equalizer'
|
|
3
3
|
require 'rom/constants'
|
4
4
|
|
5
5
|
module ROM
|
6
|
+
# Notification subsystem
|
7
|
+
#
|
8
|
+
# This is an abstract event bus that implements a simple pub/sub protocol.
|
9
|
+
# The Notifications module is used in the setup process to decouple
|
10
|
+
# different modules from each other.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class Setup
|
14
|
+
# extend ROM::Notifications
|
15
|
+
#
|
16
|
+
# register_event('setup.before_setup')
|
17
|
+
# register_event('setup.after_setup')
|
18
|
+
#
|
19
|
+
# def initialize
|
20
|
+
# @bus = Notifications.event_bus(:setup)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def setup
|
24
|
+
# @bus.trigger('setup.before_setup', at: Time.now)
|
25
|
+
# # ...
|
26
|
+
# @bus.trigger('setup.after_setup', at: Time.now)
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# class Plugin
|
31
|
+
# extend ROM::Notifications::Listener
|
32
|
+
#
|
33
|
+
# subscribe('setup.after_setup') do |event|
|
34
|
+
# puts "Loaded at #{event.at.iso8601}"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
6
38
|
module Notifications
|
7
39
|
LISTENERS_HASH = Hash.new { |h, k| h[k] = [] }
|
8
40
|
|
9
41
|
module Publisher
|
42
|
+
# Subscribe to events.
|
43
|
+
# If the query parameter is provided, filters events by payload.
|
44
|
+
#
|
45
|
+
# @param [String] event_id The event key
|
46
|
+
# @param [Hash] query An optional event filter
|
47
|
+
# @yield [block] The callback
|
48
|
+
# @return [Object] self
|
49
|
+
#
|
10
50
|
# @api public
|
11
51
|
def subscribe(event_id, query = EMPTY_HASH, &block)
|
12
52
|
listeners[event_id] << [block, query]
|
13
53
|
self
|
14
54
|
end
|
15
55
|
|
56
|
+
# Trigger an event
|
57
|
+
#
|
58
|
+
# @param [String] event_id The event key
|
59
|
+
# @param [Hash] payload An optional payload
|
60
|
+
#
|
16
61
|
# @api public
|
17
62
|
def trigger(event_id, payload = EMPTY_HASH)
|
18
63
|
event = events[event_id]
|
@@ -23,20 +68,40 @@ module ROM
|
|
23
68
|
end
|
24
69
|
end
|
25
70
|
|
71
|
+
# Event object
|
72
|
+
#
|
73
|
+
# @api public
|
26
74
|
class Event
|
27
75
|
include Dry::Equalizer(:id, :payload)
|
28
76
|
|
77
|
+
# @attr_reader [String] id Event ID
|
29
78
|
attr_reader :id
|
30
79
|
|
80
|
+
# @api public
|
31
81
|
def initialize(id, payload = EMPTY_HASH)
|
32
82
|
@id = id
|
33
83
|
@payload = payload
|
34
84
|
end
|
35
85
|
|
86
|
+
# Get data from the payload
|
87
|
+
#
|
88
|
+
# @param [String,Symbol] name
|
89
|
+
#
|
90
|
+
# @api public
|
36
91
|
def [](name)
|
37
92
|
@payload.fetch(name)
|
38
93
|
end
|
39
94
|
|
95
|
+
# Get or set a payload
|
96
|
+
#
|
97
|
+
# @overload
|
98
|
+
# @return [Hash] payload
|
99
|
+
#
|
100
|
+
# @overload payload(data)
|
101
|
+
# @param [Hash] data A new payload
|
102
|
+
# @return [Event] A copy of the event with the provided payload
|
103
|
+
#
|
104
|
+
# @api public
|
40
105
|
def payload(data = nil)
|
41
106
|
if data
|
42
107
|
self.class.new(id, @payload.merge(data))
|
@@ -45,10 +110,17 @@ module ROM
|
|
45
110
|
end
|
46
111
|
end
|
47
112
|
|
113
|
+
# Trigger the event
|
114
|
+
#
|
115
|
+
# @param [#call] listener
|
116
|
+
# @param [Hash] query
|
117
|
+
#
|
118
|
+
# @api private
|
48
119
|
def trigger(listener, query = EMPTY_HASH)
|
49
120
|
listener.(self) if trigger?(query)
|
50
121
|
end
|
51
122
|
|
123
|
+
# @api private
|
52
124
|
def trigger?(query)
|
53
125
|
query.empty? || query.all? { |key, value| @payload[key] == value }
|
54
126
|
end
|
@@ -56,6 +128,11 @@ module ROM
|
|
56
128
|
|
57
129
|
extend Publisher
|
58
130
|
|
131
|
+
# Register an event
|
132
|
+
#
|
133
|
+
# @param [String] id A unique event key
|
134
|
+
# @param [Hash] info
|
135
|
+
#
|
59
136
|
# @api public
|
60
137
|
def register_event(id, info = EMPTY_HASH)
|
61
138
|
Notifications.events[id] = Event.new(id, info)
|
@@ -71,6 +148,11 @@ module ROM
|
|
71
148
|
@__listeners__ ||= LISTENERS_HASH.dup
|
72
149
|
end
|
73
150
|
|
151
|
+
# Build an event bus
|
152
|
+
#
|
153
|
+
# @param [Symbol] id Bus key
|
154
|
+
# @return [Notifications::EventBus] A new bus
|
155
|
+
#
|
74
156
|
# @api public
|
75
157
|
def self.event_bus(id)
|
76
158
|
EventBus.new(id, events: events.dup, listeners: listeners.dup)
|
@@ -78,12 +160,22 @@ module ROM
|
|
78
160
|
|
79
161
|
# @api public
|
80
162
|
module Listener
|
163
|
+
# Subscribe to events
|
164
|
+
#
|
165
|
+
# @param [String] event_id The event key
|
166
|
+
# @param [Hash] query An optional event filter
|
167
|
+
# @return [Object] self
|
168
|
+
#
|
81
169
|
# @api public
|
82
170
|
def subscribe(event_id, query = EMPTY_HASH, &block)
|
83
171
|
Notifications.listeners[event_id] << [block, query]
|
84
172
|
end
|
85
173
|
end
|
86
174
|
|
175
|
+
# Event bus
|
176
|
+
#
|
177
|
+
# An event bus stores listeners (callbacks) and events
|
178
|
+
#
|
87
179
|
# @api public
|
88
180
|
class EventBus
|
89
181
|
include Publisher
|
data/lib/rom/version.rb
CHANGED