From c742f4c7060eae7e42334063c4e13ec263b9eda4 Mon Sep 17 00:00:00 2001 From: ???????? ???????????? Date: Mon, 19 Jul 2021 23:32:31 +0300 Subject: [PATCH] Refactoring --- .rspec | 2 + Gemfile | 3 + Gemfile.lock | 102 ++++++++++++++++++++++++ Guardfile | 6 ++ bunny_messenger.gemspec | 8 +- lib/bunny_messenger.rb | 7 +- lib/bunny_messenger/Rakefile | 4 +- lib/bunny_messenger/config.rb | 43 +++------- lib/bunny_messenger/consumer.rb | 28 +++---- lib/bunny_messenger/exchange_by_name.rb | 6 +- lib/bunny_messenger/migration.rb | 12 +-- lib/bunny_messenger/migrator.rb | 49 +++++++----- lib/bunny_messenger/queue_by_name.rb | 10 +-- lib/bunny_messenger/rpc_consumer.rb | 4 + lib/bunny_messenger/rpc_message.rb | 46 +++++++++++ lib/tasks/dump.rake | 2 +- spec/lib/config_spec.rb | 86 ++++++++++++++++++++ spec/lib/connection_spec.rb | 4 + spec/lib/consumer_spec.rb | 2 + spec/spec_helper.rb | 3 + 20 files changed, 333 insertions(+), 94 deletions(-) create mode 100644 .rspec create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Guardfile create mode 100644 lib/bunny_messenger/rpc_consumer.rb create mode 100644 lib/bunny_messenger/rpc_message.rb create mode 100644 spec/lib/config_spec.rb create mode 100644 spec/lib/connection_spec.rb create mode 100644 spec/lib/consumer_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..7ca29b8 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--require 'spec_helper' +--format=documentation \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..cd8aa9e --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..c256fa3 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,102 @@ +PATH + remote: . + specs: + bunny_messenger (0.1.8) + bunny (>= 2.18.0) + faraday (>= 1.5.1) + faraday_middleware (>= 1.0.0) + rake (>= 12) + +GEM + remote: https://rubygems.org/ + specs: + amq-protocol (2.3.2) + bunny (2.19.0) + amq-protocol (~> 2.3, >= 2.3.1) + sorted_set (~> 1, >= 1.0.2) + coderay (1.1.3) + diff-lcs (1.4.4) + faraday (1.5.1) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday_middleware (1.0.0) + faraday (~> 1.0) + ffi (1.15.3) + formatador (0.3.0) + guard (2.17.0) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (>= 1.0.12, < 2.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-rspec (4.7.3) + guard (~> 2.1) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) + listen (3.5.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + lumberjack (1.2.8) + method_source (1.0.0) + multipart-post (2.1.1) + nenv (0.3.0) + notiffany (0.1.3) + nenv (~> 0.1) + shellany (~> 0.0) + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.0.6) + rb-fsevent (0.11.0) + rb-inotify (0.10.1) + ffi (~> 1.0) + rbtree (0.4.4) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + ruby2_keywords (0.0.4) + set (1.0.1) + shellany (0.0.1) + sorted_set (1.0.3) + rbtree + set (~> 1.0) + thor (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + bunny_messenger! + guard-rspec + rspec + +BUNDLED WITH + 2.1.4 diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..f156eb5 --- /dev/null +++ b/Guardfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true +guard :rspec, cmd: 'bundle exec rspec', notification: true, all_after_pass: true do + watch('spec/spec_helper.rb') { 'spec' } + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/bunny_messenger/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } +end diff --git a/bunny_messenger.gemspec b/bunny_messenger.gemspec index 8c77578..b5d9c73 100644 --- a/bunny_messenger.gemspec +++ b/bunny_messenger.gemspec @@ -4,15 +4,17 @@ $LOAD_PATH.push File.expand_path('lib', __dir__) Gem::Specification.new do |s| s.name = 'bunny_messenger' - s.version = '0.1.7' + s.version = '0.1.8' s.summary = 'Hola!' s.description = 'Bunny Messenger gemt' s.authors = ['Ivan'] s.email = 'ivan@srgh.tech' s.files = Dir['lib/**/*'] + ['LICENSE.txt', 'Rakefile', 'README.md'] + s.add_development_dependency 'guard-rspec' s.add_development_dependency 'rspec' s.add_runtime_dependency 'bunny', '>= 2.18.0' - s.add_runtime_dependency 'httparty', '>= 0.18.1' + s.add_runtime_dependency 'faraday', '>= 1.5.1' + s.add_runtime_dependency 'faraday_middleware', '>=1.0.0' s.add_runtime_dependency 'rake', '>= 12' s.license = 'MIT' -end \ No newline at end of file +end diff --git a/lib/bunny_messenger.rb b/lib/bunny_messenger.rb index 9e1c36f..5650b89 100644 --- a/lib/bunny_messenger.rb +++ b/lib/bunny_messenger.rb @@ -4,11 +4,12 @@ require 'logger' require 'pathname' require 'yaml' -require 'httparty' +require 'faraday' +require 'faraday_middleware' require 'fileutils' require 'singleton' require 'json' - +require 'securerandom' # Basis class for work with RabbitMQ # - web_host - http://localhost:15672/ - url for web admin # - auth_params - { username: , password: } credentials for web admin panel @@ -37,3 +38,5 @@ class << self require 'bunny_messenger/exchange_by_name' require 'bunny_messenger/queue_by_name' require 'bunny_messenger/consumer' +require 'bunny_messenger/rpc_consumer' +require 'bunny_messenger/rpc_message' diff --git a/lib/bunny_messenger/Rakefile b/lib/bunny_messenger/Rakefile index a5457f4..d89816b 100644 --- a/lib/bunny_messenger/Rakefile +++ b/lib/bunny_messenger/Rakefile @@ -3,6 +3,4 @@ require 'rake' spec = Gem::Specification.find_by_name 'bunny_messenger' -if defined?(Rake) - Dir["#{spec.gem_dir}/lib/tasks/**/*.rake"].each { |ext| load ext } -end +Dir["#{spec.gem_dir}/lib/tasks/**/*.rake"].each { |ext| load ext } if defined?(Rake) diff --git a/lib/bunny_messenger/config.rb b/lib/bunny_messenger/config.rb index 94c1d2c..f9933f2 100644 --- a/lib/bunny_messenger/config.rb +++ b/lib/bunny_messenger/config.rb @@ -4,41 +4,18 @@ class BunnyMessenger # Dynamic configuration for gem class Config class << self - def auth_params - load_config - BunnyMessenger.auth_params || default_auth_params - end - - def connection_params - load_config - BunnyMessenger.connection_params || default_connection_params - end - - def web_host - load_config - BunnyMessenger.web_host || default_web_host - end - - def structure_file_path - load_config - BunnyMessenger.structure_file_path || default_structure_file_path - end - - def migrations_path - load_config - BunnyMessenger.migrations_path || default_migrations_path + %i[auth_params connection_params web_host structure_file_path migrations_path + logger_level].each do |m_name| + define_method m_name do + load_config + BunnyMessenger.send(m_name) || send(['default', m_name].join('_')) + end end def logger - load_config BunnyMessenger.logger || default_logger end - def logger_level - load_config - BunnyMessenger.logger_level || default_logger_level - end - private def environment @@ -55,9 +32,9 @@ def load_config @config = YAML.load_file('config/bunny.yml')[environment] @config.each do |k, v| if v.is_a?(Hash) - BunnyMessenger.send(k.to_s + '=', v.transform_keys(&:to_sym)) + BunnyMessenger.send("#{k}=", v.transform_keys(&:to_sym)) else - BunnyMessenger.send(k.to_s + '=', v) + BunnyMessenger.send("#{k}=", v) end end rescue Errno::ENOENT @@ -66,13 +43,13 @@ def load_config end def default_logger_level - Logger::DEBUG + Logger::INFO end def default_logger return @default_logger if @default_logger - @default_logger = Logger.new(STDOUT) + @default_logger = Logger.new($stdout) @default_logger.level = logger_level @default_logger end diff --git a/lib/bunny_messenger/consumer.rb b/lib/bunny_messenger/consumer.rb index 340c4ad..9f6c394 100644 --- a/lib/bunny_messenger/consumer.rb +++ b/lib/bunny_messenger/consumer.rb @@ -4,6 +4,7 @@ class BunnyMessenger # Basis class for consumer class Consumer attr_reader :queue + include BunnyMessenger::Loggable def initialize(queue_name) @@ -19,29 +20,24 @@ def perform_on_fail(delivery_info, _metadata, _payload, _exception) end def call - begin - queue.subscribe(block: true, manual_ack: true) do |delivery_info, metadata, payload| - begin - trace_time "Message #{metadata.message_id} processed in" do - perform(delivery_info, metadata, payload) - delivery_info.channel.ack(delivery_info.delivery_tag) - end - rescue StandardError => e - trace_time "Message #{metadata.message_id} failed with #{e.message}" do - perform_on_fail(delivery_info, metadata, payload, e) - end - end + queue.subscribe(block: true, manual_ack: true) do |delivery_info, metadata, payload| + trace_time "Message #{metadata.message_id} processed in" do + perform(delivery_info, metadata, payload) + delivery_info.channel.ack(delivery_info.delivery_tag) + end + rescue StandardError => e + trace_time "Message #{metadata.message_id} failed with #{e.message}" do + perform_on_fail(delivery_info, metadata, payload, e) end - rescue Exception => e - serve_exception(e) - raise e end + rescue Exception => e + serve_exception(e) + raise e end # Tihs is used to capture exceptions def serve_exception(_exception); end - def multiple false end diff --git a/lib/bunny_messenger/exchange_by_name.rb b/lib/bunny_messenger/exchange_by_name.rb index 875a089..59b393d 100644 --- a/lib/bunny_messenger/exchange_by_name.rb +++ b/lib/bunny_messenger/exchange_by_name.rb @@ -7,8 +7,8 @@ class << self def call(name, channel = nil) return known_exchanges[name.to_s] if known_exchanges[name.to_s] - ex_dat = structure.dig(:exchanges) - .find { |x_hash| x_hash['name'] == name } + ex_dat = structure[:exchanges] + .find { |x_hash| x_hash['name'] == name } raise "Exchange not found with name #{name} in schema" unless ex_dat new_exchange = Bunny::Exchange.new( @@ -29,7 +29,7 @@ def known_exchanges end def ex_channel - @ex_channel = BunnyMessenger::Connection.instance.create_channel + @ex_channel ||= BunnyMessenger::Connection.instance.create_channel end def structure diff --git a/lib/bunny_messenger/migration.rb b/lib/bunny_messenger/migration.rb index 3deedd4..936bf75 100644 --- a/lib/bunny_messenger/migration.rb +++ b/lib/bunny_messenger/migration.rb @@ -8,7 +8,7 @@ class Migration def call start_time = Time.now log_info("#{version} Migration started #{self.class.name}") - up && BunnyMessenger::Migrator.save + up && ::BunnyMessenger::Migrator.save duration = Time.now - start_time log_info("Migration completed #{self.class.name} in #{duration} seconds") end @@ -24,23 +24,23 @@ def version private def channel - @channel ||= BunnyMessenger::Connection.instance.create_channel + @channel ||= ::BunnyMessenger::Connection.instance.create_channel end def delete_exchange(name) - BunnyMessenger::ExchangeByName.call(name).delete + ::BunnyMessenger::ExchangeByName.call(name).delete end def delete_queue(name) - BunnyMessenger::QueueByName.call(name).delete + ::BunnyMessenger::QueueByName.call(name).delete end def create_queue(name, opts = {}) - Bunny::Queue.new(channel, name.to_s, opts) + ::Bunny::Queue.new(channel, name.to_s, opts) end def create_exchange(name, type, opts = {}) - Bunny::Exchange.new(channel, type, name.to_s, opts) + ::Bunny::Exchange.new(channel, type, name.to_s, opts) end def create_binding(target, source, opts = {}) diff --git a/lib/bunny_messenger/migrator.rb b/lib/bunny_messenger/migrator.rb index 72ceae4..2f8def2 100644 --- a/lib/bunny_messenger/migrator.rb +++ b/lib/bunny_messenger/migrator.rb @@ -25,65 +25,70 @@ def save bindings: dump_bindings }.to_yaml ) end - log_info('Schema saved to file ' + - File.expand_path(config.structure_file_path)) + log_info("Schema saved to file #{File.expand_path(config.structure_file_path)}") end def load struct = YAML.load_file(config.structure_file_path) exchanges = {} queues = {} - struct.dig(:exchanges).each do |x_hash| + struct[:exchanges].each do |x_hash| exchanges[x_hash['name']] = create_exchange(x_hash.transform_keys(&:to_sym)) end - struct.dig(:queues).each do |q_hash| + struct[:queues].each do |q_hash| queues[q_hash['name']] = create_queue(q_hash.transform_keys(&:to_sym)) end - struct.dig(:bindings).each do |b_hash| + struct[:bindings].each do |b_hash| destination = { 'queue' => queues, 'exchange' => exchanges } .dig(b_hash['destination_type'], b_hash['destination']) - source = exchanges.dig(b_hash['source']) + source = exchanges[b_hash['source']] destination.bind(source, routing_key: b_hash['routing_key']) end - log_info('Loaded file ' + File.expand_path(config.structure_file_path)) + log_info("Loaded file #{File.expand_path(config.structure_file_path)}") rescue Errno::ENOENT log_info("Cant access #{File.expand_path(config.structure_file_path)}") end def flush - dump_queues.map {|queue| ::BunnyMessenger::QueueByName.(queue.dig('name')).delete} + dump_queues.map { |queue| ::BunnyMessenger::QueueByName.call(queue['name']).delete } dump_exchanges - .reject{|exc| exc['name'].match?(/^amq\./) || exc['name'].empty?} - .map {|exc| ::BunnyMessenger::ExchangeByName.(exc.dig('name')).delete} + .reject { |exc| exc['name'].match?(/^amq\./) || exc['name'].empty? } + .map { |exc| ::BunnyMessenger::ExchangeByName.call(exc['name']).delete } end private + def http_client + return @http_client if @http_client + + @http_client = Faraday.new(url: config.web_host ) do |faraday| + faraday.request :basic_auth, *config.auth_params.fetch_values(:username, :password) + faraday.response :json + end + end + def dump_bindings - HTTParty - .get(config.web_host + '/api/bindings', - basic_auth: config.auth_params) - .parsed_response + http_client + .get("/api/bindings") + .body .reject { |b_hash| b_hash['source'].empty? } end def dump_queues filter_queues_params( - HTTParty - .get(config.web_host + '/api/queues', - basic_auth: config.auth_params) - .parsed_response + http_client + .get("/api/queues") + .body ) end def dump_exchanges filter_exchanges_params( - HTTParty - .get(config.web_host + '/api/exchanges', - basic_auth: config.auth_params) - .parsed_response + http_client + .get("/api/exchanges") + .body ) end diff --git a/lib/bunny_messenger/queue_by_name.rb b/lib/bunny_messenger/queue_by_name.rb index 0a17c5e..f074ff7 100644 --- a/lib/bunny_messenger/queue_by_name.rb +++ b/lib/bunny_messenger/queue_by_name.rb @@ -5,21 +5,21 @@ class BunnyMessenger class QueueByName class << self def call(name, channel = nil) - q_dat = structure.dig(:queues) - .find { |x_hash| x_hash['name'] == name } + q_dat = structure[:queues] + .find { |x_hash| x_hash['name'] == name } raise "Queue not found with name #{name} in schema" unless q_dat - new_queue = Bunny::Queue.new( + + Bunny::Queue.new( channel || q_channel, name, q_dat.transform_keys(&:to_sym) ) - new_queue end private def q_channel - @q_channel = BunnyMessenger::Connection.instance.create_channel + @q_channel ||= BunnyMessenger::Connection.instance.create_channel end def structure diff --git a/lib/bunny_messenger/rpc_consumer.rb b/lib/bunny_messenger/rpc_consumer.rb new file mode 100644 index 0000000..d4947fa --- /dev/null +++ b/lib/bunny_messenger/rpc_consumer.rb @@ -0,0 +1,4 @@ +class BunnyMessenger + class RpcConsumer < Consumer + end +end \ No newline at end of file diff --git a/lib/bunny_messenger/rpc_message.rb b/lib/bunny_messenger/rpc_message.rb new file mode 100644 index 0000000..616d33a --- /dev/null +++ b/lib/bunny_messenger/rpc_message.rb @@ -0,0 +1,46 @@ +class BunnyMessenger + class RpcMessage < Message + def initialize(payload, opts = {}) + super + @opts = @opts.merge({ reply_to: reply_to }) + end + + def call + trace_time "Message #{message_id} sent in" do + exchange.publish(payload, opts) + end + + reply_to_queue.subscribe(manual_ack: true) do |delivery_info, metadata, payload| + loop { sleep 1 } + end + puts 'Ok, finished' + end + + # ReplyTo queue name + def reply_to + raise NotImplementedError + end + + private + + def locked? + @lock + end + + def lock + @lock = true + end + + def unlock + @lock = false + end + + def reply_to_queue + @queue = BunnyMessenger::QueueByName.call(reply_to) + end + + def correlation_id + @correlation_id = SecureRandom.hex(10) + end + end +end \ No newline at end of file diff --git a/lib/tasks/dump.rake b/lib/tasks/dump.rake index 0c2caea..868c3e2 100644 --- a/lib/tasks/dump.rake +++ b/lib/tasks/dump.rake @@ -21,7 +21,7 @@ namespace :bunny_messenger do desc 'Create migration' task :create_migration, [:name] do |_task, args| path = BunnyMessenger::Config.migrations_path - full_path = [path, args.name + '.rb'].join('/') + full_path = [path, "#{args.name}.rb"].join('/') klass_name = args.name.split('_').collect(&:capitalize).join raise 'Migration exists with this name' if File.file?(full_path) diff --git a/spec/lib/config_spec.rb b/spec/lib/config_spec.rb new file mode 100644 index 0000000..bd5f8ea --- /dev/null +++ b/spec/lib/config_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +describe BunnyMessenger::Config do + describe '.auth_params' do + it { expect(described_class).to respond_to(:auth_params) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.auth_params + end + it 'is expected to launch default_auth_params' do + expect(described_class).to receive(:default_auth_params).once + described_class.auth_params + end + end + describe '.connection_params' do + it { expect(described_class).to respond_to(:connection_params) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.connection_params + end + it 'is expected to launch default_connection_params' do + expect(described_class).to receive(:default_connection_params).once + described_class.connection_params + end + end + + describe '.web_host' do + it { expect(described_class).to respond_to(:web_host) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.web_host + end + it 'is expected to launch default_web_host' do + expect(described_class).to receive(:default_web_host).once + described_class.web_host + end + end + + describe '.structure_file_path' do + it { expect(described_class).to respond_to(:structure_file_path) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.structure_file_path + end + it 'is expected to launch default_structure_file_path' do + expect(described_class).to receive(:default_structure_file_path).once + described_class.structure_file_path + end + end + + describe '.migrations_path' do + it { expect(described_class).to respond_to(:migrations_path) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.migrations_path + end + it 'is expected to launch default_migrations_path' do + expect(described_class).to receive(:default_migrations_path).once + described_class.migrations_path + end + end + + describe '.logger_level' do + it { expect(described_class).to respond_to(:logger_level) } + it 'is expected to launch #load_config' do + expect(described_class).to receive(:load_config).once + described_class.logger_level + end + it 'is expected to launch default_logger_level' do + expect(described_class).to receive(:default_logger_level).once + described_class.logger_level + end + end + + describe '.logger' do + it { expect(described_class).to respond_to(:logger) } + it 'is expected to launch #logger_level' do + expect(described_class).to receive(:logger_level).once.and_return(Logger::INFO) + described_class.logger + end + it 'is expected to launch default_logger' do + expect(described_class).to receive(:default_logger) + described_class.logger + end + end +end diff --git a/spec/lib/connection_spec.rb b/spec/lib/connection_spec.rb new file mode 100644 index 0000000..cf35df4 --- /dev/null +++ b/spec/lib/connection_spec.rb @@ -0,0 +1,4 @@ +describe BunnyMessenger::Connection do + it { expect(described_class.instance).to be_a Singleton } + it { expect(described_class.instance).to be_a Bunny::Session } +end \ No newline at end of file diff --git a/spec/lib/consumer_spec.rb b/spec/lib/consumer_spec.rb new file mode 100644 index 0000000..bd4bd47 --- /dev/null +++ b/spec/lib/consumer_spec.rb @@ -0,0 +1,2 @@ +describe BunnyMessenger::Consumer do +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..f86b028 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative '../lib/bunny_messenger'