realm-core 0.7.1 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -0
- data/Rakefile +19 -0
- metadata +25 -44
- data/lib/realm-core.rb +0 -6
- data/lib/realm.rb +0 -22
- data/lib/realm/action_handler.rb +0 -84
- data/lib/realm/action_handler/result.rb +0 -32
- data/lib/realm/builder.rb +0 -93
- data/lib/realm/command_handler.rb +0 -23
- data/lib/realm/config.rb +0 -57
- data/lib/realm/container.rb +0 -68
- data/lib/realm/context.rb +0 -37
- data/lib/realm/dependency.rb +0 -24
- data/lib/realm/dispatcher.rb +0 -74
- data/lib/realm/domain_resolver.rb +0 -59
- data/lib/realm/error.rb +0 -62
- data/lib/realm/event.rb +0 -56
- data/lib/realm/event_factory.rb +0 -53
- data/lib/realm/event_handler.rb +0 -102
- data/lib/realm/event_router.rb +0 -100
- data/lib/realm/event_router/gateway.rb +0 -50
- data/lib/realm/event_router/internal_loop_gateway.rb +0 -50
- data/lib/realm/health_status.rb +0 -46
- data/lib/realm/mixins/aggregate_member.rb +0 -25
- data/lib/realm/mixins/context_injection.rb +0 -49
- data/lib/realm/mixins/controller.rb +0 -53
- data/lib/realm/mixins/decorator.rb +0 -33
- data/lib/realm/mixins/dependency_injection.rb +0 -52
- data/lib/realm/mixins/reactive.rb +0 -32
- data/lib/realm/mixins/repository_helper.rb +0 -43
- data/lib/realm/multi_worker.rb +0 -30
- data/lib/realm/persistence.rb +0 -54
- data/lib/realm/persistence/repository_query_handler_adapter.rb +0 -24
- data/lib/realm/plugin.rb +0 -20
- data/lib/realm/query_handler.rb +0 -8
- data/lib/realm/runtime.rb +0 -61
- data/lib/realm/runtime/session.rb +0 -33
- data/lib/realm/types.rb +0 -9
data/lib/realm/config.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/string'
|
4
|
-
require 'dry-initializer'
|
5
|
-
|
6
|
-
module Realm
|
7
|
-
class Config
|
8
|
-
extend Dry::Initializer
|
9
|
-
|
10
|
-
option :root_module
|
11
|
-
option :database_url, default: proc {}
|
12
|
-
option :prefix, default: proc {}
|
13
|
-
option :namespace, default: proc { root_module.to_s.underscore }
|
14
|
-
option :domain_module, default: proc { "#{root_module}::Domain" }
|
15
|
-
option :engine_class, default: proc { "#{root_module}::Engine" }
|
16
|
-
option :engine_path, default: proc { engine_class&.to_s&.safe_constantize&.root }
|
17
|
-
option :logger, default: proc {}
|
18
|
-
option :plugins, default: proc { [] }, reader: false
|
19
|
-
option :dependencies, default: proc { {} }
|
20
|
-
option :persistence_gateway, default: proc { database_url && { type: :rom, url: database_url } }, reader: false
|
21
|
-
option :event_gateway, default: proc {}, reader: false
|
22
|
-
option :event_gateways, default: proc {
|
23
|
-
@event_gateway ? { default: { **@event_gateway, default: true } } : {}
|
24
|
-
}
|
25
|
-
|
26
|
-
def plugins
|
27
|
-
Array(@plugins)
|
28
|
-
end
|
29
|
-
|
30
|
-
def persistence_gateway
|
31
|
-
return {} unless @persistence_gateway
|
32
|
-
|
33
|
-
class_path = engine_path && "#{engine_path}/app/persistence/#{namespace}"
|
34
|
-
repos_path = class_path && "#{class_path}/repositories"
|
35
|
-
repos_module = "#{root_module}::Repositories"
|
36
|
-
{
|
37
|
-
root_module: root_module,
|
38
|
-
class_path: class_path,
|
39
|
-
repos_path: repos_path,
|
40
|
-
repos_module: repos_module,
|
41
|
-
migration_path: engine_path && "#{engine_path}/db/migrate",
|
42
|
-
repositories: repositories(repos_path, repos_module),
|
43
|
-
}.merge(@persistence_gateway)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def repositories(repos_path, repos_module)
|
49
|
-
return [] unless repos_path
|
50
|
-
|
51
|
-
Dir[File.join(repos_path, '**', '*.rb')].each_with_object([]) do |filename, all|
|
52
|
-
matches = %r{^#{repos_path}/(.+)\.rb$}.match(filename)
|
53
|
-
all << "#{repos_module}::#{matches[1].camelize}".constantize if matches
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/realm/container.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry-container'
|
4
|
-
require 'active_support/core_ext/string'
|
5
|
-
require 'active_support/core_ext/object/try'
|
6
|
-
require 'realm/error'
|
7
|
-
|
8
|
-
module Realm
|
9
|
-
class Container
|
10
|
-
include Dry::Container::Mixin
|
11
|
-
include Enumerable
|
12
|
-
|
13
|
-
def self.[](object)
|
14
|
-
object.is_a?(Container) ? object : Container.new(object)
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize(hash = {})
|
18
|
-
register_all(hash)
|
19
|
-
end
|
20
|
-
|
21
|
-
def register(key, contents = nil, options = {}, &block)
|
22
|
-
options[:klass] ||= contents.class if contents && !contents.is_a?(::Hash)
|
23
|
-
super(key, contents, options, &block)
|
24
|
-
end
|
25
|
-
|
26
|
-
def register_all(hash)
|
27
|
-
hash.each_pair do |key, value|
|
28
|
-
register(key, value)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def register_factory(klass, *args, as: nil, memoize: true, **kwargs) # rubocop:disable Naming/MethodParameterName
|
33
|
-
register(as || klass, klass: klass, memoize: memoize) do
|
34
|
-
create(klass, *args, **kwargs)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def create(klass, *args, **kwargs)
|
39
|
-
(klass.try(:dependencies) || []).each do |d|
|
40
|
-
fn = -> { resolve_dependable(sanitize_dependable(d.dependable), d.optional?) }
|
41
|
-
kwargs[d.name] = d.lazy? ? fn : fn.call
|
42
|
-
end
|
43
|
-
klass.new(*args, **kwargs)
|
44
|
-
end
|
45
|
-
|
46
|
-
def [](key)
|
47
|
-
resolve(key) if key?(key)
|
48
|
-
end
|
49
|
-
|
50
|
-
def resolve_all(klass)
|
51
|
-
_container.each_with_object([]) do |(_, item), all|
|
52
|
-
all << item.call if item.options[:klass] <= klass
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def sanitize_dependable(dependable)
|
59
|
-
dependable.is_a?(String) && dependable.match(/^[A-Z]/) ? dependable.constantize : dependable
|
60
|
-
end
|
61
|
-
|
62
|
-
def resolve_dependable(dependable, optional)
|
63
|
-
raise DependencyMissing, dependable unless optional || key?(dependable)
|
64
|
-
|
65
|
-
self[dependable]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/lib/realm/context.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'realm/container'
|
4
|
-
|
5
|
-
module Realm
|
6
|
-
class Context
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
def initialize(*containers)
|
10
|
-
@containers = containers.map { |c| Container[c] }
|
11
|
-
end
|
12
|
-
|
13
|
-
def [](name)
|
14
|
-
@containers.each do |container|
|
15
|
-
return container[name] if container.key?(name)
|
16
|
-
end
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def key?(name)
|
21
|
-
@containers.any? { |container| container.key?(name) }
|
22
|
-
end
|
23
|
-
|
24
|
-
def merge(container_like)
|
25
|
-
container_like.blank? ? self : self.class.new(container_like, *@containers)
|
26
|
-
end
|
27
|
-
|
28
|
-
def each(&block)
|
29
|
-
@containers.each { |container| container.each(&block) }
|
30
|
-
end
|
31
|
-
|
32
|
-
# Just for testing
|
33
|
-
def override!(container)
|
34
|
-
@containers.prepend(container)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/realm/dependency.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/string'
|
4
|
-
|
5
|
-
module Realm
|
6
|
-
class Dependency
|
7
|
-
attr_reader :dependable, :name
|
8
|
-
|
9
|
-
def initialize(dependable, as: nil, optional: false, lazy: false) # rubocop:disable Naming/MethodParameterName
|
10
|
-
@dependable = dependable
|
11
|
-
@name = as || dependable.to_s.demodulize.underscore.to_sym
|
12
|
-
@optional = optional
|
13
|
-
@lazy = lazy
|
14
|
-
end
|
15
|
-
|
16
|
-
def optional?
|
17
|
-
@optional
|
18
|
-
end
|
19
|
-
|
20
|
-
def lazy?
|
21
|
-
@lazy
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/realm/dispatcher.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/string'
|
4
|
-
require 'realm/query_handler'
|
5
|
-
require 'realm/command_handler'
|
6
|
-
require 'realm/domain_resolver'
|
7
|
-
require 'realm/error'
|
8
|
-
require 'realm/mixins/dependency_injection'
|
9
|
-
require 'realm/persistence/repository_query_handler_adapter'
|
10
|
-
|
11
|
-
module Realm
|
12
|
-
class Dispatcher
|
13
|
-
include Mixins::DependencyInjection
|
14
|
-
inject DomainResolver
|
15
|
-
|
16
|
-
def initialize(runtime)
|
17
|
-
@runtime = runtime
|
18
|
-
@threads = []
|
19
|
-
end
|
20
|
-
|
21
|
-
def query(identifier, params = {})
|
22
|
-
callable, action = get_callable(QueryHandler, identifier)
|
23
|
-
callable, action = get_repo_adapter(identifier) unless callable
|
24
|
-
raise QueryHandlerMissing, identifier unless callable
|
25
|
-
|
26
|
-
dispatch(callable, action, params)
|
27
|
-
end
|
28
|
-
|
29
|
-
def run(identifier, params = {})
|
30
|
-
callable, action = get_callable(CommandHandler, identifier)
|
31
|
-
raise CommandHandlerMissing, identifier unless callable
|
32
|
-
|
33
|
-
dispatch(callable, action, params)
|
34
|
-
end
|
35
|
-
|
36
|
-
def run_as_job(identifier, params = {})
|
37
|
-
callable, action = get_callable(CommandHandler, identifier)
|
38
|
-
raise CommandHandlerMissing, identifier unless callable
|
39
|
-
|
40
|
-
@threads.delete_if(&:stop?)
|
41
|
-
@threads << Thread.new do # TODO: back by SQS
|
42
|
-
result = dispatch(callable, action, params)
|
43
|
-
yield result if block_given?
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Blocks until all jobs are finished. Useful mainly in tests.
|
48
|
-
def wait_for_jobs
|
49
|
-
@threads.each(&:join)
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
attr_reader :runtime
|
55
|
-
|
56
|
-
def dispatch(callable, action, params)
|
57
|
-
arguments = { action: action, params: params, runtime: runtime }.compact
|
58
|
-
callable.(**arguments)
|
59
|
-
end
|
60
|
-
|
61
|
-
def get_callable(type, identifier)
|
62
|
-
return [identifier, nil] if identifier.respond_to?(:call)
|
63
|
-
|
64
|
-
domain_resolver.get_handler_with_action(type, identifier)
|
65
|
-
end
|
66
|
-
|
67
|
-
def get_repo_adapter(identifier)
|
68
|
-
parts = identifier.to_s.split('.')
|
69
|
-
return [nil, nil] unless parts.size == 2 && runtime&.context&.key?("#{parts[0]}_repo")
|
70
|
-
|
71
|
-
[Persistence::RepositoryQueryHandlerAdapter.new(runtime.context["#{parts[0]}_repo"]), parts[1].to_sym]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/string'
|
4
|
-
require 'realm/command_handler'
|
5
|
-
require 'realm/query_handler'
|
6
|
-
require 'realm/event_handler'
|
7
|
-
require 'realm/event'
|
8
|
-
|
9
|
-
module Realm
|
10
|
-
class DomainResolver
|
11
|
-
DOMAIN_CLASS_TYPES = [CommandHandler, QueryHandler, EventHandler].freeze
|
12
|
-
|
13
|
-
def initialize(domain_module = nil)
|
14
|
-
# nil domain resolver is useful in tests
|
15
|
-
@domain_module = domain_module
|
16
|
-
@index = DOMAIN_CLASS_TYPES.map { |t| [t, {}] }.to_h
|
17
|
-
scan(domain_module) if domain_module
|
18
|
-
end
|
19
|
-
|
20
|
-
def get_handler_with_action(type, identifier)
|
21
|
-
handlers = @index[type]
|
22
|
-
return [handlers[identifier], :handle] if handlers.key?(identifier)
|
23
|
-
|
24
|
-
# The last part of the identifier can action method name inside the handler
|
25
|
-
parts = identifier.split('.')
|
26
|
-
handler_part = parts[..-2].join('.')
|
27
|
-
action = parts[-1]
|
28
|
-
return [handlers[handler_part], action.to_sym] if handlers.key?(handler_part)
|
29
|
-
|
30
|
-
[nil, nil]
|
31
|
-
end
|
32
|
-
|
33
|
-
def all_event_handlers
|
34
|
-
@index[EventHandler].values
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def scan(root_module)
|
40
|
-
root_module_str = root_module.to_s
|
41
|
-
root_module.constants.each do |const_sym|
|
42
|
-
const = root_module.const_get(const_sym)
|
43
|
-
next unless const.is_a?(Module) && !(const < Event) && const.to_s.start_with?(root_module_str)
|
44
|
-
|
45
|
-
type = DOMAIN_CLASS_TYPES.find { |t| const < t }
|
46
|
-
next scan(const) unless type
|
47
|
-
|
48
|
-
register(type, const)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def register(type, const)
|
53
|
-
# Remove domain module prefix and handler type suffixes
|
54
|
-
operation_type = type.to_s.demodulize.sub('Handler', '')
|
55
|
-
identifier = const.to_s.gsub(/(^#{@domain_module})|((#{operation_type})?Handlers?)/, '')
|
56
|
-
@index[type][identifier.underscore.gsub(%r{(^/+)|(/+$)}, '').gsub(%r{/+}, '.')] = const
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/realm/error.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Realm
|
4
|
-
class Error < StandardError
|
5
|
-
def self.[](default_msg)
|
6
|
-
Class.new(Realm::Error) do
|
7
|
-
define_method(:initialize) do |msg = default_msg|
|
8
|
-
super(msg)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class QueryHandlerMissing < Error
|
15
|
-
def initialize(query_name, msg: "Cannot find handler for query '#{query_name}'")
|
16
|
-
super(msg)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class CommandHandlerMissing < Error
|
21
|
-
def initialize(command_name, msg: "Cannot find handler for command '#{command_name}'")
|
22
|
-
super(msg)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class CannotHandleAction < Error
|
27
|
-
def initialize(handler, action, msg: "#{handler} cannot handle action '#{action}'")
|
28
|
-
super(msg)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class DependencyMissing < Error
|
33
|
-
def initialize(dependency_name, msg: "Dependency '#{dependency_name}' missing in container")
|
34
|
-
super(msg)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class EventClassMissing < Error
|
39
|
-
def initialize(identifier, events_module, msg: "Cannot find event class for #{identifier} in #{events_module}")
|
40
|
-
super(msg)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class InvalidParams < Error
|
45
|
-
def initialize(validation_result, msg: "Validation failed: #{validation_result.errors.to_h}")
|
46
|
-
@validation_result = validation_result
|
47
|
-
super(msg)
|
48
|
-
end
|
49
|
-
|
50
|
-
def params
|
51
|
-
@validation_result.to_h
|
52
|
-
end
|
53
|
-
|
54
|
-
def messages
|
55
|
-
@validation_result.errors.to_h
|
56
|
-
end
|
57
|
-
|
58
|
-
def full_messages
|
59
|
-
@validation_result.errors(full: true).to_h
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
data/lib/realm/event.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'securerandom'
|
4
|
-
require 'active_support/core_ext/hash'
|
5
|
-
require 'active_support/core_ext/string'
|
6
|
-
require 'dry-struct'
|
7
|
-
require 'realm/types'
|
8
|
-
|
9
|
-
module Realm
|
10
|
-
class Event < Dry::Struct
|
11
|
-
T = Realm::Types
|
12
|
-
|
13
|
-
transform_keys(&:to_sym)
|
14
|
-
|
15
|
-
attribute :head do
|
16
|
-
attribute :id, T::Strict::String
|
17
|
-
attribute :triggered_at, T::JSON::Time
|
18
|
-
attribute? :version, T::Coercible::String
|
19
|
-
attribute? :origin, T::Strict::String
|
20
|
-
attribute :correlation_id, T::Strict::String
|
21
|
-
attribute? :cause_event_id, T::Strict::String
|
22
|
-
attribute? :cause, T::Strict::String
|
23
|
-
end
|
24
|
-
|
25
|
-
class << self
|
26
|
-
def new(attributes = {})
|
27
|
-
head = {
|
28
|
-
id: SecureRandom.uuid,
|
29
|
-
correlation_id: SecureRandom.uuid,
|
30
|
-
triggered_at: Time.now,
|
31
|
-
version: 1, # until we need breaking change (anything except adding attribute) all events are version 1
|
32
|
-
}.merge(attributes.fetch(:head, {}))
|
33
|
-
body = attributes[:body] || attributes.except(:head)
|
34
|
-
super({ head: head }.merge(body.empty? ? {} : { body: body }))
|
35
|
-
end
|
36
|
-
|
37
|
-
def type
|
38
|
-
@type ||= name.demodulize.sub('Event', '').underscore
|
39
|
-
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
def body_struct(&block)
|
44
|
-
attribute(:body, &block)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def type
|
49
|
-
self.class.type
|
50
|
-
end
|
51
|
-
|
52
|
-
def to_json(*args)
|
53
|
-
JSON.generate(to_h, *args)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|