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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -0
  3. data/Rakefile +19 -0
  4. metadata +25 -44
  5. data/lib/realm-core.rb +0 -6
  6. data/lib/realm.rb +0 -22
  7. data/lib/realm/action_handler.rb +0 -84
  8. data/lib/realm/action_handler/result.rb +0 -32
  9. data/lib/realm/builder.rb +0 -93
  10. data/lib/realm/command_handler.rb +0 -23
  11. data/lib/realm/config.rb +0 -57
  12. data/lib/realm/container.rb +0 -68
  13. data/lib/realm/context.rb +0 -37
  14. data/lib/realm/dependency.rb +0 -24
  15. data/lib/realm/dispatcher.rb +0 -74
  16. data/lib/realm/domain_resolver.rb +0 -59
  17. data/lib/realm/error.rb +0 -62
  18. data/lib/realm/event.rb +0 -56
  19. data/lib/realm/event_factory.rb +0 -53
  20. data/lib/realm/event_handler.rb +0 -102
  21. data/lib/realm/event_router.rb +0 -100
  22. data/lib/realm/event_router/gateway.rb +0 -50
  23. data/lib/realm/event_router/internal_loop_gateway.rb +0 -50
  24. data/lib/realm/health_status.rb +0 -46
  25. data/lib/realm/mixins/aggregate_member.rb +0 -25
  26. data/lib/realm/mixins/context_injection.rb +0 -49
  27. data/lib/realm/mixins/controller.rb +0 -53
  28. data/lib/realm/mixins/decorator.rb +0 -33
  29. data/lib/realm/mixins/dependency_injection.rb +0 -52
  30. data/lib/realm/mixins/reactive.rb +0 -32
  31. data/lib/realm/mixins/repository_helper.rb +0 -43
  32. data/lib/realm/multi_worker.rb +0 -30
  33. data/lib/realm/persistence.rb +0 -54
  34. data/lib/realm/persistence/repository_query_handler_adapter.rb +0 -24
  35. data/lib/realm/plugin.rb +0 -20
  36. data/lib/realm/query_handler.rb +0 -8
  37. data/lib/realm/runtime.rb +0 -61
  38. data/lib/realm/runtime/session.rb +0 -33
  39. 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
@@ -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
@@ -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
@@ -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