realm-core 0.7.1 → 0.7.3

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.
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