harmoniser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6daf41d7ec196c5f279bca1e4d9ee1339f17a81508fb4432359b32595c364a07
4
+ data.tar.gz: 1cab012d3c64c10590f032b768e5acd267980980e988d02b3f7297a138d8477a
5
+ SHA512:
6
+ metadata.gz: 3ec7c8e929076ca5725f91d1755ba5548b99e87e8d7c3b6f9d63d029cf6fd76c8419fef99c25b46a525dc49f0d1af011f71a210bf3602d75787fbba7941c5cb1
7
+ data.tar.gz: 554b5e2c30444280133ec848124e3b1bb88fbb994b3a2056efdbcfaa5817783530b6d25642217db43eaeb25a5cc80652395e7b1203ebe2c37767e07b8a024578
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "harmoniser"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/harmoniser ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ require "harmoniser/cli"
3
+
4
+ $stdout.sync = true
5
+ begin
6
+ Harmoniser::CLI.instance.call
7
+ rescue => e
8
+ warn(e.message)
9
+ warn(e.backtrace.join("\n"))
10
+ exit(1)
11
+ end
data/bin/setup ADDED
@@ -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,25 @@
1
+ module Harmoniser
2
+ module Channelable
3
+ MUTEX = Mutex.new
4
+ private_constant :MUTEX
5
+
6
+ module ClassMethods
7
+ def harmoniser_channel
8
+ MUTEX.synchronize do
9
+ @harmoniser_channel ||= create_channel
10
+ end
11
+ end
12
+
13
+ def create_channel
14
+ connection = Harmoniser.connection
15
+ connection.create_channel
16
+ end
17
+ end
18
+
19
+ class << self
20
+ def included(base)
21
+ base.extend(ClassMethods)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,70 @@
1
+ require "singleton"
2
+ require "harmoniser"
3
+ require "harmoniser/parser"
4
+ require "harmoniser/launcher"
5
+
6
+ module Harmoniser
7
+ class CLI
8
+ include Singleton
9
+
10
+ SIGNAL_HANDLERS = {
11
+ "INT" => lambda { |cli, signal| raise Interrupt },
12
+ "TERM" => lambda { |cli, signal| raise Interrupt }
13
+ }
14
+ SIGNAL_HANDLERS.default = lambda { |cli, signal| cli.logger.info("Default signal handler executed since there is no handler defined: signal = `#{signal}`") }
15
+
16
+ attr_reader :logger
17
+
18
+ def initialize
19
+ @configuration = Harmoniser.default_configuration
20
+ @logger = Harmoniser.logger
21
+ end
22
+
23
+ def call
24
+ parse_options
25
+ define_signals
26
+ run
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :configuration
32
+
33
+ def parse_options
34
+ options = Parser.new(logger: @logger).call(ARGV)
35
+ configuration.options_with(**options)
36
+ end
37
+
38
+ def define_signals
39
+ @read_io, @write_io = IO.pipe
40
+
41
+ ["INT", "TERM"].each do |sig|
42
+ Signal.trap(sig) do
43
+ @write_io.puts(sig)
44
+ end
45
+ end
46
+ end
47
+
48
+ def run
49
+ launcher = Launcher.new
50
+ launcher.start
51
+
52
+ while @read_io.wait_readable
53
+ signal = @read_io.gets.strip
54
+ handle_signal(signal)
55
+ end
56
+ rescue Interrupt
57
+ logger.info("Shutting down!")
58
+ launcher.stop
59
+ @write_io.close
60
+ @read_io.close
61
+ logger.info("Bye!")
62
+ exit(0)
63
+ end
64
+
65
+ def handle_signal(signal)
66
+ logger.info("Signal received: signal = `#{signal}`")
67
+ SIGNAL_HANDLERS[signal].call(self, signal)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,25 @@
1
+ require "forwardable"
2
+ require "harmoniser/configuration"
3
+
4
+ module Harmoniser
5
+ module Configurable
6
+ extend Forwardable
7
+
8
+ def configure
9
+ @configuration ||= Configuration.new
10
+ yield(@configuration)
11
+ end
12
+
13
+ def configuration
14
+ raise NoMethodError.new("Please, configure first") unless @configuration
15
+
16
+ @configuration
17
+ end
18
+
19
+ def default_configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def_delegators :configuration, :logger, :connection
24
+ end
25
+ end
@@ -0,0 +1,77 @@
1
+ require "forwardable"
2
+ require "logger"
3
+ require "harmoniser/connection"
4
+ require "harmoniser/topology"
5
+ require "harmoniser/options"
6
+
7
+ module Harmoniser
8
+ class Configuration
9
+ extend Forwardable
10
+
11
+ DEFAULT_CONNECTION_OPTS = {
12
+ connection_name: "harmoniser@#{VERSION}",
13
+ host: "127.0.0.1",
14
+ password: "guest",
15
+ port: 5672,
16
+ tls_silence_warnings: true,
17
+ username: "guest",
18
+ verify_peer: false,
19
+ vhost: "/"
20
+ }
21
+ MUTEX = Mutex.new
22
+
23
+ attr_reader :connection, :connection_opts, :logger, :options
24
+ def_delegators :options, :environment, :require, :verbose
25
+
26
+ def initialize
27
+ @logger = Logger.new($stdout, progname: "harmoniser@#{VERSION}")
28
+ @options = Options.new(**default_options)
29
+ set_logger_severity
30
+ @connection_opts = DEFAULT_CONNECTION_OPTS.merge({ logger: @logger })
31
+ @topology = Topology.new
32
+ end
33
+
34
+ def define_topology
35
+ raise LocalJumpError, "A block is required for this method" unless block_given?
36
+
37
+ yield(@topology)
38
+ end
39
+
40
+ def connection
41
+ MUTEX.synchronize do
42
+ @connection = Connection.new(connection_opts) unless @connection
43
+ @connection.start unless @connection.open?
44
+ @connection
45
+ end
46
+ end
47
+
48
+ def connection_opts=(opts)
49
+ raise TypeError, "opts must be a Hash object" unless opts.is_a?(Hash)
50
+
51
+ @connection_opts = connection_opts.merge(opts)
52
+ end
53
+
54
+ def options_with(**kwargs)
55
+ @options = options.with(**kwargs)
56
+ set_logger_severity
57
+ end
58
+
59
+ private
60
+
61
+ def default_options
62
+ {
63
+ environment: ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "production")),
64
+ require: ".",
65
+ verbose: false
66
+ }
67
+ end
68
+
69
+ def set_logger_severity
70
+ if @options.production?
71
+ @logger.level = @options.verbose? ? Logger::DEBUG : Logger::INFO
72
+ else
73
+ @logger.level = Logger::DEBUG
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,35 @@
1
+ require "forwardable"
2
+ require "bunny"
3
+
4
+ module Harmoniser
5
+ class Connection
6
+ extend Forwardable
7
+ def_delegators :@bunny, :close, :create_channel, :open?, :start
8
+
9
+ def initialize(opts)
10
+ @bunny = Bunny.new(opts)
11
+ end
12
+
13
+ def to_s
14
+ "<#{self.class.name}>: #{user}@#{host}:#{port}, vhost = #{vhost}"
15
+ end
16
+
17
+ private
18
+
19
+ def host
20
+ @bunny.transport.host
21
+ end
22
+
23
+ def port
24
+ @bunny.transport.port
25
+ end
26
+
27
+ def user
28
+ @bunny.user
29
+ end
30
+
31
+ def vhost
32
+ @bunny.vhost
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module Harmoniser
2
+ module Definition
3
+ Binding = Data.define(:exchange_name, :destination_name, :destination_type, :opts) do
4
+ def queue?
5
+ [:queue, "queue"].include?(destination_type)
6
+ end
7
+
8
+ def exchange?
9
+ [:exchange, "exchange"].include?(destination_type)
10
+ end
11
+ end
12
+
13
+ Consumer = Data.define(:queue_name, :consumer_tag, :no_ack, :exclusive, :arguments)
14
+
15
+ Exchange = Data.define(:name, :type, :opts) do
16
+ def hash
17
+ [self.class, name].hash
18
+ end
19
+
20
+ def eql?(other)
21
+ self.class == other.class && name == other.name
22
+ end
23
+ end
24
+
25
+ Queue = Data.define(:name, :opts) do
26
+ def hash
27
+ [self.class, name].hash
28
+ end
29
+
30
+ def eql?(other)
31
+ self.class == other.class && name == other.name
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ module Harmoniser
2
+ module Includable
3
+ MUTEX = Mutex.new
4
+ private_constant :MUTEX
5
+
6
+ module ClassMethods
7
+ def harmoniser_register_included(klass)
8
+ MUTEX.synchronize do
9
+ @harmoniser_included ||= Set.new
10
+ @harmoniser_included << klass
11
+ end
12
+ end
13
+
14
+ def harmoniser_included
15
+ @harmoniser_included.to_a
16
+ end
17
+ end
18
+
19
+ class << self
20
+ def included(base)
21
+ base.extend(ClassMethods)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ module Harmoniser
2
+ class Launcher
3
+ def initialize(configuration: Harmoniser.configuration, logger: Harmoniser.logger)
4
+ @configuration = configuration
5
+ @logger = logger
6
+ end
7
+
8
+ def start
9
+ boot_app
10
+ start_subscribers
11
+ end
12
+
13
+ def stop
14
+ stop_subscribers
15
+ end
16
+
17
+ private
18
+
19
+ def boot_app
20
+ if File.directory?(@configuration.require)
21
+ load_rails
22
+ else
23
+ load_file
24
+ end
25
+ end
26
+
27
+ # TODO - Frameworks like Rails which have autoload for development/test will not start any subscriber unless the files where subscribers are located are required explicitly. Since we premier production and the eager load ensures that every file is loaded, this approach works
28
+ def start_subscribers
29
+ klasses = Subscriber.harmoniser_included
30
+ klasses.each do |klass|
31
+ klass.harmoniser_subscriber_start
32
+ end
33
+ @logger.info("Subscribers registered to consume messages from queues: klasses = `#{klasses}`")
34
+ end
35
+
36
+ def stop_subscribers
37
+ @logger.info("Connection will be closed: connection = `#{@configuration.connection.to_s}`")
38
+ @configuration.connection.close
39
+ end
40
+
41
+ private
42
+
43
+ def load_rails
44
+ filepath = File.expand_path("#{@configuration.require}/config/environment.rb")
45
+ require filepath
46
+ rescue LoadError
47
+ @logger.warn("Error while requiring file within directory. No subscribers will run for this process: require = `#{@configuration.require}`, filepath = `#{filepath}`")
48
+ end
49
+
50
+ def load_file
51
+ require @configuration.require
52
+ rescue LoadError
53
+ @logger.warn("Error while requiring file. No subscribers will run for this process: require = `#{@configuration.require}`")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ module Harmoniser
2
+ Options = Data.define(:environment, :require, :verbose) do
3
+ def production?
4
+ environment == "production"
5
+ end
6
+
7
+ def verbose?
8
+ !!verbose
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ require "optparse"
2
+ require_relative "version"
3
+
4
+ module Harmoniser
5
+ class Parser
6
+ def initialize(logger:)
7
+ @logger = logger
8
+ @options = {}
9
+ @option_parser = OptionParser.new do |opts|
10
+ opts.banner = "harmoniser [options]"
11
+ opts.on "-e", "--environment ENV", "Application environment" do |arg|
12
+ @options[:environment] = arg
13
+ end
14
+ opts.on "-r", "--require [PATH|DIR]", "File to require or location of Rails application" do |arg|
15
+ @options[:require] = arg
16
+ end
17
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |arg|
18
+ @options[:verbose] = arg
19
+ end
20
+ opts.on "-V", "--version", "Print version and exit" do
21
+ puts "Harmoniser #{Harmoniser::VERSION}"
22
+ exit(0)
23
+ end
24
+ opts.on_tail "-h", "--help", "Show help" do
25
+ puts @option_parser
26
+ exit(0)
27
+ end
28
+ end
29
+ end
30
+
31
+ def call(argv = [])
32
+ @option_parser.parse!(argv)
33
+ @options
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ require "harmoniser/channelable"
2
+ require "harmoniser/definition"
3
+
4
+ module Harmoniser
5
+ module Publisher
6
+ class MissingExchangeDefinition < StandardError ; end
7
+ include Channelable
8
+ MUTEX = Mutex.new
9
+ private_constant :MUTEX
10
+
11
+ module ClassMethods
12
+ def harmoniser_publisher(exchange_name:)
13
+ @harmoniser_exchange_definition = Definition::Exchange.new(
14
+ name: exchange_name,
15
+ type: nil,
16
+ opts: { passive: true }
17
+ )
18
+ end
19
+
20
+ def publish(payload, opts = {})
21
+ raise_missing_exchange_definition unless @harmoniser_exchange_definition
22
+
23
+ MUTEX.synchronize do
24
+ harmoniser_exchange.publish(payload, opts)
25
+ end
26
+ Harmoniser.logger.debug { "Message published: payload = `#{payload}`, opts = `#{opts}`" }
27
+
28
+ harmoniser_exchange
29
+ end
30
+
31
+ private
32
+
33
+ def harmoniser_exchange
34
+ return @harmoniser_exchange if @harmoniser_exchange
35
+
36
+ @harmoniser_exchange ||= Bunny::Exchange.new(
37
+ Publisher.harmoniser_channel,
38
+ @harmoniser_exchange_definition.type,
39
+ @harmoniser_exchange_definition.name,
40
+ @harmoniser_exchange_definition.opts
41
+ )
42
+ end
43
+
44
+ def raise_missing_exchange_definition
45
+ raise MissingExchangeDefinition, "Please, call harmoniser_publisher class method first with the exchange_name that will be used for publications"
46
+ end
47
+ end
48
+
49
+ class << self
50
+ def included(base)
51
+ base.extend(ClassMethods)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,85 @@
1
+ require "harmoniser/channelable"
2
+ require "harmoniser/definition"
3
+ require "harmoniser/includable"
4
+
5
+ module Harmoniser
6
+ module Subscriber
7
+ class MissingConsumerDefinition < StandardError ; end
8
+ include Channelable
9
+ include Includable
10
+ MUTEX = Mutex.new
11
+ private_constant :MUTEX
12
+
13
+ module ClassMethods
14
+ def harmoniser_subscriber(queue_name:, consumer_tag: nil, no_ack: true, exclusive: false, arguments: {})
15
+ @harmoniser_consumer_definition = Definition::Consumer.new(
16
+ queue_name: queue_name,
17
+ consumer_tag: consumer_tag,
18
+ no_ack: no_ack,
19
+ exclusive: exclusive,
20
+ arguments: arguments
21
+ )
22
+ end
23
+
24
+ def harmoniser_subscriber_start
25
+ MUTEX.synchronize do
26
+ @harmoniser_consumer ||= create_consumer
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def create_consumer
33
+ raise_missing_consumer_definition unless @harmoniser_consumer_definition
34
+
35
+ consumer = Bunny::Consumer.new(
36
+ Subscriber.harmoniser_channel,
37
+ @harmoniser_consumer_definition.queue_name,
38
+ @harmoniser_consumer_definition.consumer_tag || Subscriber.harmoniser_channel.generate_consumer_tag,
39
+ @harmoniser_consumer_definition.no_ack,
40
+ @harmoniser_consumer_definition.exclusive,
41
+ @harmoniser_consumer_definition.arguments
42
+ )
43
+ handle_cancellation(consumer)
44
+ handle_delivery(consumer)
45
+ register_consumer(consumer)
46
+ consumer
47
+ end
48
+
49
+ def handle_cancellation(consumer)
50
+ consumer.on_cancellation do |basic_cancel|
51
+ if respond_to?(:on_cancellation)
52
+ on_cancellation(basic_cancel)
53
+ else
54
+ Harmoniser.logger.info("Default on_cancellation handler executed for consumer: consumer_tag = `#{consumer.consumer_tag}`, queue = `#{consumer.queue}`")
55
+ end
56
+ end
57
+ end
58
+
59
+ def handle_delivery(consumer)
60
+ consumer.on_delivery do |delivery_info, properties, payload|
61
+ if respond_to?(:on_delivery)
62
+ on_delivery(delivery_info, properties, payload)
63
+ else
64
+ Harmoniser.logger.info("Default on_delivery handler executed for consumer: consumer_tag = `#{consumer.consumer_tag}`, queue = `#{consumer.queue}`")
65
+ end
66
+ end
67
+ end
68
+
69
+ def register_consumer(consumer)
70
+ consumer.channel.basic_consume_with(consumer)
71
+ end
72
+
73
+ def raise_missing_consumer_definition
74
+ raise MissingConsumerDefinition, "Please, call harmoniser_subscriber class method first with the queue_name that will be used for subscribing"
75
+ end
76
+ end
77
+
78
+ class << self
79
+ def included(base)
80
+ base.extend(ClassMethods)
81
+ harmoniser_register_included(base)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,72 @@
1
+ require "harmoniser/channelable"
2
+ require "harmoniser/definition"
3
+
4
+ module Harmoniser
5
+ class Topology
6
+ include Channelable
7
+
8
+ attr_reader :bindings, :exchanges, :queues
9
+
10
+ def initialize
11
+ @bindings = Set.new
12
+ @exchanges = Set.new
13
+ @queues = Set.new
14
+ end
15
+
16
+ def add_exchange(type, name, **opts)
17
+ @exchanges << Definition::Exchange.new(
18
+ type: type,
19
+ name: name,
20
+ opts: opts
21
+ )
22
+ end
23
+
24
+ def add_queue(name, **opts)
25
+ @queues << Definition::Queue.new(
26
+ name: name,
27
+ opts: opts
28
+ )
29
+ end
30
+
31
+ def add_binding(exchange_name, destination_name, destination_type = :queue, **opts)
32
+ @bindings << Definition::Binding.new(
33
+ exchange_name: exchange_name,
34
+ destination_name: destination_name,
35
+ destination_type: destination_type,
36
+ opts: opts
37
+ )
38
+ end
39
+
40
+ def declare
41
+ channel = self.class.create_channel
42
+ declare_exchanges(channel)
43
+ declare_queues(channel)
44
+ declare_bindings(channel)
45
+ Harmoniser.connection.close
46
+ end
47
+
48
+ private
49
+
50
+ def declare_exchanges(channel)
51
+ exchanges.each do |exchange|
52
+ Bunny::Exchange.new(channel, exchange.type, exchange.name, exchange.opts)
53
+ end
54
+ end
55
+
56
+ def declare_queues(channel)
57
+ queues.each do |queue|
58
+ Bunny::Queue.new(channel, queue.name, queue.opts)
59
+ end
60
+ end
61
+
62
+ def declare_bindings(channel)
63
+ bindings.each do |binding|
64
+ if binding.queue?
65
+ channel.queue_bind(binding.destination_name, binding.exchange_name, binding.opts)
66
+ elsif binding.exchange?
67
+ ;
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Harmoniser
2
+ VERSION = "0.1.0"
3
+ end
data/lib/harmoniser.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "harmoniser/version"
2
+ require "harmoniser/configurable"
3
+ require "harmoniser/publisher"
4
+ require "harmoniser/subscriber"
5
+
6
+ module Harmoniser
7
+ extend Configurable
8
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: harmoniser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jose Lloret
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bunny
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.22'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.22'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: standardrb
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: A declarative tool to communicate with RabbitMQ
70
+ email:
71
+ - jollopre@gmail.com
72
+ executables:
73
+ - harmoniser
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/console
78
+ - bin/harmoniser
79
+ - bin/setup
80
+ - lib/harmoniser.rb
81
+ - lib/harmoniser/channelable.rb
82
+ - lib/harmoniser/cli.rb
83
+ - lib/harmoniser/configurable.rb
84
+ - lib/harmoniser/configuration.rb
85
+ - lib/harmoniser/connection.rb
86
+ - lib/harmoniser/definition.rb
87
+ - lib/harmoniser/includable.rb
88
+ - lib/harmoniser/launcher.rb
89
+ - lib/harmoniser/options.rb
90
+ - lib/harmoniser/parser.rb
91
+ - lib/harmoniser/publisher.rb
92
+ - lib/harmoniser/subscriber.rb
93
+ - lib/harmoniser/topology.rb
94
+ - lib/harmoniser/version.rb
95
+ homepage: https://github.com/jollopre/harmoniser
96
+ licenses:
97
+ - MIT
98
+ metadata:
99
+ homepage_uri: https://github.com/jollopre/harmoniser
100
+ source_code_uri: https://github.com/jollopre/harmoniser
101
+ changelog_uri: https://github.com/jollopre/harmoniser/blob/master/CHANGELOG.md
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 2.7.0
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.4.10
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A declarative tool to communicate with RabbitMQ
121
+ test_files: []