hermes_messenger_of_the_gods 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +9 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +13 -0
  8. data/README.md +464 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/fly_hermes +57 -0
  13. data/hermes_messenger_of_the_gods.gemspec +38 -0
  14. data/lib/hermes_messenger_of_the_gods.rb +57 -0
  15. data/lib/hermes_messenger_of_the_gods/concerns/base.rb +42 -0
  16. data/lib/hermes_messenger_of_the_gods/concerns/grpc_protobuf.rb +62 -0
  17. data/lib/hermes_messenger_of_the_gods/concerns/message.rb +194 -0
  18. data/lib/hermes_messenger_of_the_gods/concerns/mono_message.rb +52 -0
  19. data/lib/hermes_messenger_of_the_gods/concerns/worker.rb +173 -0
  20. data/lib/hermes_messenger_of_the_gods/configuration.rb +29 -0
  21. data/lib/hermes_messenger_of_the_gods/endpoint_builder.rb +41 -0
  22. data/lib/hermes_messenger_of_the_gods/endpoints.rb +3 -0
  23. data/lib/hermes_messenger_of_the_gods/endpoints/base.rb +113 -0
  24. data/lib/hermes_messenger_of_the_gods/endpoints/sns.rb +21 -0
  25. data/lib/hermes_messenger_of_the_gods/endpoints/sqs.rb +114 -0
  26. data/lib/hermes_messenger_of_the_gods/exceptions.rb +27 -0
  27. data/lib/hermes_messenger_of_the_gods/logging_helpers.rb +30 -0
  28. data/lib/hermes_messenger_of_the_gods/output/basic.rb +63 -0
  29. data/lib/hermes_messenger_of_the_gods/status_server.rb +48 -0
  30. data/lib/hermes_messenger_of_the_gods/testing/array_endpoint.rb +46 -0
  31. data/lib/hermes_messenger_of_the_gods/testing/dispatch_matcher.rb +52 -0
  32. data/lib/hermes_messenger_of_the_gods/testing/rspec_helpers.rb +64 -0
  33. data/lib/hermes_messenger_of_the_gods/version.rb +3 -0
  34. data/packageGems.sh +13 -0
  35. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.bundlecache +0 -0
  36. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.circleci/config.yml +32 -0
  37. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.gitignore +11 -0
  38. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.rspec +3 -0
  39. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.rubocop.yml +42 -0
  40. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.ruby-version +1 -0
  41. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/.travis.yml +5 -0
  42. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/Gemfile +6 -0
  43. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/Gemfile.lock +75 -0
  44. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/Makefile +1 -0
  45. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/README.md +78 -0
  46. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/Rakefile +6 -0
  47. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/bundle +105 -0
  48. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/coderay +29 -0
  49. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/console +14 -0
  50. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/grpc_tools_ruby_protoc +29 -0
  51. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/grpc_tools_ruby_protoc_plugin +29 -0
  52. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/htmldiff +29 -0
  53. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/ldiff +29 -0
  54. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/pry +29 -0
  55. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/rake +29 -0
  56. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/rspec +29 -0
  57. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/rubocop +29 -0
  58. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/ruby-parse +29 -0
  59. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/ruby-rewrite +29 -0
  60. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/bin/setup +8 -0
  61. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/lib/protobuf3_fixer.rb +123 -0
  62. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/lib/protobuf3_fixer/encoder.rb +63 -0
  63. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/lib/protobuf3_fixer/generation_helpers.rb +23 -0
  64. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/lib/protobuf3_fixer/reflector.rb +66 -0
  65. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/lib/protobuf3_fixer/version.rb +3 -0
  66. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/protobuf3_fixer.gemspec +54 -0
  67. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/bundle +105 -0
  68. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/coderay +29 -0
  69. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/grpc_tools_ruby_protoc +29 -0
  70. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/grpc_tools_ruby_protoc_plugin +29 -0
  71. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/htmldiff +29 -0
  72. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/ldiff +29 -0
  73. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/pry +29 -0
  74. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/rake +29 -0
  75. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/rspec +29 -0
  76. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/rubocop +29 -0
  77. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/ruby-parse +29 -0
  78. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/rubocop/ruby-rewrite +29 -0
  79. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/build_from_hash_spec.rb +20 -0
  80. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/encoding/encoding_options_spec.rb +23 -0
  81. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/extra_fields/json_decode_of_superset_spec.rb +54 -0
  82. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/generation_helpers_spec.rb +37 -0
  83. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/protobuf3_fixer_spec.rb +5 -0
  84. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/spec_helper.rb +17 -0
  85. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/support/compiled_protobuffs/source/superset_pb.rb +44 -0
  86. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/support/compiled_protobuffs/source/timestamp_pb.rb +27 -0
  87. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/support/protobuffs/source/superset.proto +40 -0
  88. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/support/protobuffs/source/timestamp.proto +18 -0
  89. data/vendor/cache/Protobuf3Fixer-5f9f1a2d2da9/spec/well_known_type_fixes/timestamp_spec.rb +79 -0
  90. data/vendor/cache/activemodel-6.0.3.1.gem +0 -0
  91. data/vendor/cache/activesupport-6.0.3.1.gem +0 -0
  92. data/vendor/cache/addressable-2.6.0.gem +0 -0
  93. data/vendor/cache/aws-eventstream-1.1.0.gem +0 -0
  94. data/vendor/cache/aws-partitions-1.329.0.gem +0 -0
  95. data/vendor/cache/aws-sdk-core-3.99.2.gem +0 -0
  96. data/vendor/cache/aws-sdk-sns-1.25.1.gem +0 -0
  97. data/vendor/cache/aws-sdk-sqs-1.27.1.gem +0 -0
  98. data/vendor/cache/aws-sigv4-1.1.4.gem +0 -0
  99. data/vendor/cache/codeclimate-test-reporter-1.0.9.gem +0 -0
  100. data/vendor/cache/coderay-1.1.2.gem +0 -0
  101. data/vendor/cache/concurrent-ruby-1.1.6.gem +0 -0
  102. data/vendor/cache/crack-0.4.3.gem +0 -0
  103. data/vendor/cache/diff-lcs-1.3.gem +0 -0
  104. data/vendor/cache/docile-1.1.5.gem +0 -0
  105. data/vendor/cache/google-protobuf-3.12.2-universal-darwin.gem +0 -0
  106. data/vendor/cache/google-protobuf-3.12.2-x86_64-linux.gem +0 -0
  107. data/vendor/cache/google-protobuf-3.12.2.gem +0 -0
  108. data/vendor/cache/googleapis-common-protos-types-1.0.2.gem +0 -0
  109. data/vendor/cache/grpc-1.18.0-universal-darwin.gem +0 -0
  110. data/vendor/cache/grpc-1.18.0-x86_64-linux.gem +0 -0
  111. data/vendor/cache/grpc-1.18.0.gem +0 -0
  112. data/vendor/cache/hashdiff-0.3.8.gem +0 -0
  113. data/vendor/cache/i18n-1.8.3.gem +0 -0
  114. data/vendor/cache/jmespath-1.4.0.gem +0 -0
  115. data/vendor/cache/json-2.1.0.gem +0 -0
  116. data/vendor/cache/memory_profiler-0.9.12.gem +0 -0
  117. data/vendor/cache/method_source-0.9.2.gem +0 -0
  118. data/vendor/cache/minitest-5.14.1.gem +0 -0
  119. data/vendor/cache/pry-0.12.2.gem +0 -0
  120. data/vendor/cache/public_suffix-3.0.3.gem +0 -0
  121. data/vendor/cache/rake-10.5.0.gem +0 -0
  122. data/vendor/cache/rspec-3.8.0.gem +0 -0
  123. data/vendor/cache/rspec-core-3.8.0.gem +0 -0
  124. data/vendor/cache/rspec-expectations-3.8.2.gem +0 -0
  125. data/vendor/cache/rspec-mocks-3.8.0.gem +0 -0
  126. data/vendor/cache/rspec-support-3.8.0.gem +0 -0
  127. data/vendor/cache/safe_yaml-1.0.4.gem +0 -0
  128. data/vendor/cache/simplecov-0.13.0.gem +0 -0
  129. data/vendor/cache/simplecov-html-0.10.2.gem +0 -0
  130. data/vendor/cache/thor-1.0.1.gem +0 -0
  131. data/vendor/cache/thread_safe-0.3.6.gem +0 -0
  132. data/vendor/cache/timecop-0.9.1.gem +0 -0
  133. data/vendor/cache/tzinfo-1.2.7.gem +0 -0
  134. data/vendor/cache/webmock-3.5.1.gem +0 -0
  135. data/vendor/cache/zeitwerk-2.3.0.gem +0 -0
  136. metadata +362 -0
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hermes_messenger_of_the_gods"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'bundler/setup'
5
+ require 'hermes_messenger_of_the_gods'
6
+
7
+ class FlyHermes < Thor
8
+ desc 'start', 'Starts a worker'
9
+ method_option :worker, type: :string
10
+ method_option :pool, type: :array
11
+ method_option :require, type: :string
12
+ method_option :name, type: :string
13
+
14
+ def start
15
+ if options[:require]
16
+ require options[:require]
17
+ else
18
+ require File.expand_path('config/environment.rb')
19
+ end
20
+
21
+ pools = []
22
+ if options[:worker]
23
+ pools << [options[:worker].constantize, 1]
24
+ elsif options[:pool]
25
+
26
+ options[:pool].each do |pool|
27
+ pool_klass, pool_size = pool.split('--', 2)
28
+ pool_size = pool_size.to_i if pool_size
29
+ raise "Pool size must be > 0" if pool_size.nil? or pool_size < 1
30
+
31
+ pools << [
32
+ pool_klass.constantize,
33
+ pool_size
34
+ ]
35
+ end
36
+ end
37
+
38
+ if pools.empty?
39
+ raise "No workers specified. --worker or --pool is required"
40
+ end
41
+
42
+ pools.each do |worker_klass, count|
43
+ count.times do |i|
44
+ fork do
45
+ worker = worker_klass.build_worker
46
+ worker.name = options[:name] if options[:name]
47
+ worker.name += "::#{i}"
48
+ worker.work_off
49
+ end
50
+ end
51
+ end
52
+
53
+ Process.waitall
54
+ end
55
+ end
56
+
57
+ FlyHermes.start(ARGV)
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'hermes_messenger_of_the_gods/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'hermes_messenger_of_the_gods'
9
+ spec.version = HermesMessengerOfTheGods::VERSION
10
+ spec.authors = ['Brian Malinconico', 'Stevie Vines']
11
+ spec.email = ['bmalinconico@terminus.com', 'stevievines@gmail.com']
12
+ spec.metadata['allowed_push_host'] = "https://www.rubygems.org"
13
+
14
+ spec.summary = 'Create and receive messages like a god!'
15
+ spec.description = 'Things'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_dependency 'activemodel', '> 4.2.3'
25
+ spec.add_dependency 'activesupport', '> 4.2.3'
26
+ spec.add_dependency 'aws-sdk-sns', '~> 1.0'
27
+ spec.add_dependency 'aws-sdk-sqs', '~> 1.0'
28
+ spec.add_dependency 'protobuf3_fixer'
29
+ spec.add_dependency 'thor'
30
+
31
+ spec.add_development_dependency 'bundler', '~> 2.0'
32
+ spec.add_development_dependency 'memory_profiler'
33
+ spec.add_development_dependency 'pry'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+ spec.add_development_dependency 'simplecov', '~> 1.14'
37
+ spec.add_development_dependency 'timecop'
38
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hermes_messenger_of_the_gods/version'
4
+
5
+ require 'hermes_messenger_of_the_gods/exceptions'
6
+ require 'hermes_messenger_of_the_gods/configuration'
7
+
8
+ require 'hermes_messenger_of_the_gods/logging_helpers'
9
+ require 'hermes_messenger_of_the_gods/endpoint_builder'
10
+
11
+ require 'hermes_messenger_of_the_gods/concerns/base'
12
+ require 'hermes_messenger_of_the_gods/concerns/message'
13
+ require 'hermes_messenger_of_the_gods/concerns/worker'
14
+ require 'hermes_messenger_of_the_gods/concerns/mono_message'
15
+ require 'hermes_messenger_of_the_gods/concerns/grpc_protobuf'
16
+
17
+ require 'hermes_messenger_of_the_gods/endpoints'
18
+
19
+ require 'hermes_messenger_of_the_gods/output/basic'
20
+
21
+ module HermesMessengerOfTheGods
22
+ class << self
23
+ def configuration
24
+ @configuration ||= Configuration.new
25
+ end
26
+
27
+ def configure
28
+ yield(configuration) if block_given?
29
+ configuration
30
+ end
31
+ alias config configure
32
+
33
+ def async_dispatches_in_progress
34
+ @async_dispatches_in_progress ||= 0
35
+ end
36
+
37
+ def increment_async_dispatches_in_progress
38
+ monitor.synchronize do
39
+ self.async_dispatches_in_progress += 1
40
+ end
41
+ end
42
+
43
+ def decrement_async_dispatches_in_progress
44
+ monitor.synchronize do
45
+ self.async_dispatches_in_progress -= 1
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ attr_writer :async_dispatches_in_progress
52
+
53
+ def monitor
54
+ @monitor ||= Monitor.new
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ module HermesMessengerOfTheGods
7
+ module Concerns
8
+ module Base
9
+ extend ActiveSupport::Concern
10
+
11
+ include EndpointBuilder::Helpers
12
+ include LoggingHelpers
13
+
14
+ included do
15
+ def instrument(name, payload = {}, &blk)
16
+ name = [
17
+ 'hermes_messenger_of_the_gods',
18
+ notification_prefix,
19
+ name
20
+ ].reject(&:blank?).join('.')
21
+
22
+ payload[_instrument_key] ||= self if _instrument_key
23
+ ActiveSupport::Notifications.instrument(name, payload, &blk)
24
+ end
25
+
26
+ def notification_prefix
27
+ _instrument_key
28
+ end
29
+
30
+ def _instrument_key
31
+ if is_a?(HermesMessengerOfTheGods::Concerns::Worker)
32
+ :worker
33
+ elsif is_a?(HermesMessengerOfTheGods::Concerns::Message)
34
+ :message
35
+ elsif is_a?(HermesMessengerOfTheGods::Endpoints::Base)
36
+ :endpoint
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'protobuf3_fixer'
4
+ module HermesMessengerOfTheGods
5
+ module Concerns
6
+ module GrpcProtobuf
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_protobuf_class
11
+
12
+ alias_method :_pre_grpc_protobuff_init, :initialize
13
+ def initialize(protobuf, options = {})
14
+ unless protobuf.is_a?(self.class._protobuf_class)
15
+ raise "#{self.class} requires a Protobuf for initialization"
16
+ end
17
+
18
+ _pre_grpc_protobuff_init(protobuf.to_h.merge!(options))
19
+ end
20
+
21
+ def _build_for_transmission
22
+ # Hermes expects a hash
23
+ JSON.parse(serialize_with_defaults)
24
+ end
25
+
26
+ def serialize_with_defaults
27
+ # Use the protobuf class' encode_json to generate defaults for any missing keys
28
+ self.class._protobuf_class.encode_json(message_as_proto, emit_defaults: true)
29
+ end
30
+
31
+ def message_as_proto
32
+ # Build a proto with the incoming hash
33
+ self.class._protobuf_class.new(to_message)
34
+ end
35
+
36
+ end
37
+
38
+ class_methods do
39
+ def build(message)
40
+ new(protobuf_class.new(message))
41
+ end
42
+
43
+ def from_message(attrs = {}, &blk)
44
+ # Hermes provides a hash, but we want to leverage the protobuf's built in deserialization, so dump it to json
45
+ pb = _protobuf_class.decode_json(JSON.dump(attrs))
46
+ new(pb, &blk)
47
+ end
48
+
49
+ def protobuf_class
50
+ _protobuf_class
51
+ end
52
+
53
+ def protobuf_class=(klass)
54
+ self._protobuf_class = klass
55
+ klass.descriptor.each do |f|
56
+ attr_accessor f.name.to_sym
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+ require 'active_support'
5
+ require 'active_support/core_ext/hash'
6
+
7
+ require 'hermes_messenger_of_the_gods/concerns/base'
8
+
9
+ module HermesMessengerOfTheGods
10
+ module Concerns
11
+ module Message
12
+ extend ActiveSupport::Concern
13
+
14
+ include ActiveModel::Model
15
+ include HermesMessengerOfTheGods::Concerns::Base
16
+
17
+ attr_accessor :targeted_endpoints
18
+
19
+ def original_message
20
+ @original_message
21
+ end
22
+
23
+ def initialize(options = {})
24
+ @monitor = Monitor.new
25
+ self.targeted_endpoints = options.delete(:endpoints)
26
+ super
27
+ end
28
+
29
+ def validate!
30
+ raise HermesMessengerOfTheGods::ValidationError, self unless valid?
31
+ end
32
+
33
+ def dispatch
34
+ dispatch!
35
+ rescue StandardError
36
+ false
37
+ end
38
+
39
+ def dispatch_async
40
+ HermesMessengerOfTheGods.increment_async_dispatches_in_progress
41
+ Thread.new do
42
+ dispatch
43
+ ensure
44
+ HermesMessengerOfTheGods.decrement_async_dispatches_in_progress
45
+ end
46
+ end
47
+
48
+ def dispatch_async!
49
+ HermesMessengerOfTheGods.increment_async_dispatches_in_progress
50
+ Thread.new do
51
+ dispatch!
52
+ ensure
53
+ HermesMessengerOfTheGods.decrement_async_dispatches_in_progress
54
+ end
55
+ end
56
+
57
+ def dispatch!(endpoint_args: {})
58
+ run_callbacks :dispatch do
59
+ validate!
60
+
61
+ endpoints.collect do |ep_name, endpoint|
62
+ next if targeted_endpoints && !targeted_endpoints.include?(ep_name)
63
+
64
+ Thread.new do
65
+ endpoint.dispatch!(self, endpoint_args) unless HermesMessengerOfTheGods.config.stub_dispatch
66
+ register_success(ep_name, endpoint.result)
67
+ rescue StandardError => e
68
+ register_failure(ep_name, e)
69
+ ensure
70
+ endpoint.teardown
71
+ end
72
+ end.compact.map(&:join)
73
+
74
+ unless dispatch_errors.empty?
75
+ klass = if successes.empty?
76
+ HermesMessengerOfTheGods::MessageDispatchTotalFailure
77
+ else
78
+ HermesMessengerOfTheGods::MessageDispatchPartialFailure
79
+ end
80
+
81
+ ex = klass.new
82
+ ex.exceptions = dispatch_errors
83
+ raise ex
84
+ end
85
+ end
86
+
87
+ true
88
+ end
89
+
90
+ def register_failure(ep_name, error)
91
+ @monitor.synchronize do
92
+ instrument(:dispatch_failure, endpoint_name: ep_name, exception: error)
93
+ dispatch_errors[ep_name] = error
94
+ end
95
+ end
96
+
97
+ def register_success(ep_name, return_value)
98
+ @monitor.synchronize do
99
+ successes[ep_name] = return_value
100
+ end
101
+ end
102
+
103
+ def dispatch_errors
104
+ @dispatch_errors ||= {}
105
+ end
106
+
107
+ def successes
108
+ @successes ||= {}
109
+ end
110
+
111
+ def _build_for_transmission
112
+ to_message
113
+ end
114
+
115
+ def to_message
116
+ attributes
117
+ end
118
+
119
+ def attributes
120
+ (self.class._defined_attributes || []).each.with_object({}) do |attr, hsh|
121
+ hsh[attr] = send(attr)
122
+ end
123
+ end
124
+
125
+ def inspect
126
+ "<#{self.class}: #{attributes}>"
127
+ end
128
+
129
+ included do
130
+ class_attribute :_defined_attributes, :_endpoints, :_circuit_breaker_errors, :_max_consecutive_failures
131
+
132
+ define_model_callbacks :dispatch
133
+
134
+ around_dispatch { |_, blk| instrument(:dispatch, &blk) }
135
+ end
136
+
137
+ class_methods do
138
+ def from_message(attrs = {}, &blk)
139
+ attrs = attrs.slice(*_defined_attributes.map(&:to_s))
140
+ new(attrs, &blk)
141
+ end
142
+
143
+ def attr_accessor(*args)
144
+ self._defined_attributes ||= []
145
+ self._defined_attributes += args
146
+ super
147
+ end
148
+
149
+ def endpoints
150
+ _endpoints
151
+ end
152
+
153
+ def circuit_breaker_errors
154
+ _circuit_breaker_errors
155
+ end
156
+
157
+
158
+ def max_consecutive_failures
159
+ _max_consecutive_failures
160
+ end
161
+
162
+ def max_consecutive_failures=(val)
163
+ raise 'Expected an Integer' unless val.is_a?(Integer) || val.is_a?(NilClass)
164
+ self._max_consecutive_failures = val
165
+ end
166
+
167
+ def circuit_breaker_errors=(val)
168
+ val ||= {}
169
+ raise 'Expected a hash' unless val.is_a?(Hash)
170
+
171
+ val.each do |err, actions|
172
+ val[err][:sleep] ||= 0
173
+ val[err][:fatal] ||= false
174
+ end
175
+
176
+ val.each do |err, actions|
177
+ raise ':sleep must be a number' unless actions[:sleep].is_a?(Numeric)
178
+ raise ':fatal must be a boolean' unless [true, false].include?(actions[:fatal])
179
+ end
180
+
181
+ self._circuit_breaker_errors = val
182
+ end
183
+
184
+ def endpoints=(val)
185
+ unless val.is_a?(Hash)
186
+ raise 'Endpoints expects a hash {endpoint_name: endpoint_handler}'
187
+ end
188
+
189
+ self._endpoints = val
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end