fleck 1.0.1 → 2.0.0

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -10
  3. data/CHANGELOG.md +89 -74
  4. data/Gemfile +6 -4
  5. data/examples/actions.rb +59 -53
  6. data/examples/blocking_consumer.rb +42 -42
  7. data/examples/consumer_initialization.rb +44 -42
  8. data/examples/deprecation.rb +50 -57
  9. data/examples/example.rb +76 -74
  10. data/examples/expired.rb +72 -76
  11. data/examples/fanout.rb +62 -64
  12. data/fleck.gemspec +37 -36
  13. data/lib/fleck/client.rb +124 -124
  14. data/lib/fleck/configuration.rb +149 -144
  15. data/lib/fleck/consumer.rb +7 -287
  16. data/lib/fleck/core/consumer/action_param.rb +106 -0
  17. data/lib/fleck/core/consumer/actions.rb +76 -0
  18. data/lib/fleck/core/consumer/base.rb +111 -0
  19. data/lib/fleck/core/consumer/configuration.rb +69 -0
  20. data/lib/fleck/core/consumer/decorators.rb +77 -0
  21. data/lib/fleck/core/consumer/helpers_definers.rb +55 -0
  22. data/lib/fleck/core/consumer/logger.rb +88 -0
  23. data/lib/fleck/core/consumer/request.rb +89 -0
  24. data/lib/fleck/core/consumer/response.rb +77 -0
  25. data/lib/fleck/core/consumer/response_helpers.rb +81 -0
  26. data/lib/fleck/core/consumer/validation.rb +163 -0
  27. data/lib/fleck/core/consumer.rb +166 -0
  28. data/lib/fleck/core.rb +9 -0
  29. data/lib/fleck/loggable.rb +15 -10
  30. data/lib/fleck/{hash_with_indifferent_access.rb → utilities/hash_with_indifferent_access.rb} +80 -85
  31. data/lib/fleck/utilities/host_rating.rb +104 -0
  32. data/lib/fleck/version.rb +6 -3
  33. data/lib/fleck.rb +81 -72
  34. metadata +35 -24
  35. data/lib/fleck/consumer/request.rb +0 -52
  36. data/lib/fleck/consumer/response.rb +0 -80
  37. data/lib/fleck/host_rating.rb +0 -74
data/lib/fleck/client.rb CHANGED
@@ -1,124 +1,124 @@
1
-
2
- module Fleck
3
- class Client
4
- include Fleck::Loggable
5
-
6
- attr_reader :local_ip, :remote_ip
7
-
8
- def initialize(connection, queue_name = "", exchange_type: :direct, exchange_name: "", multiple_responses: false, concurrency: 1)
9
- @connection = connection
10
- @queue_name = queue_name
11
- @multiple_responses = multiple_responses
12
- @default_timeout = multiple_responses ? 60 : nil
13
- @concurrency = [concurrency.to_i, 1].max
14
- @requests = ThreadSafe::Hash.new
15
- @subscriptions = ThreadSafe::Array.new
16
- @terminated = false
17
- @mutex = Mutex.new
18
- @local_ip = @connection.transport.socket.local_address.ip_address
19
- @remote_ip = @connection.transport.socket.remote_address.ip_address
20
-
21
- @channel = @connection.create_channel
22
- @exchange = Bunny::Exchange.new(@channel, :direct, 'fleck')
23
- @publisher = Bunny::Exchange.new(@connection.create_channel, exchange_type, exchange_name)
24
- @reply_queue = @channel.queue("", exclusive: true, auto_delete: true)
25
- @reply_queue.bind(@exchange, routing_key: @reply_queue.name)
26
-
27
- handle_returned_messages!
28
- @concurrency.times { handle_responses! }
29
-
30
- logger.debug("Client initialized!")
31
-
32
- at_exit do
33
- terminate
34
- end
35
- end
36
-
37
- def request(action: nil, version: nil, headers: {}, params: {}, async: @multiple_responses || false, timeout: @default_timeout, queue: @queue_name, rmq_options: {}, &block)
38
-
39
- if @terminated
40
- return Fleck::Client::Response.new(Oj.dump({status: 503, errors: ['Service Unavailable'], body: nil} , mode: :compat))
41
- end
42
-
43
- request = Fleck::Client::Request.new(
44
- self, queue, @reply_queue.name,
45
- action: action,
46
- version: version,
47
- headers: headers,
48
- params: params,
49
- timeout: timeout,
50
- multiple_responses: @multiple_responses,
51
- rmq_options: rmq_options,
52
- &block
53
- )
54
-
55
- @requests[request.id] = request
56
- request.send!(async)
57
-
58
- return request.response
59
- end
60
-
61
-
62
- def publish(data, options)
63
- return if @terminated
64
- @mutex.synchronize { @publisher.publish(data, options) }
65
- end
66
-
67
-
68
- def remove_request(request_id)
69
- @requests.delete request_id
70
- end
71
-
72
-
73
- def terminate
74
- @terminated = true
75
- logger.info "Unsubscribing from #{@reply_queue.name}"
76
- @subscriptions.map(&:cancel) # stop receiving new messages
77
- logger.info "Canceling pending requests"
78
- # cancel pending requests
79
- while item = @requests.shift do
80
- begin
81
- item[1].cancel!
82
- rescue => e
83
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
84
- end
85
- end
86
- end
87
-
88
-
89
- protected
90
-
91
- def handle_returned_messages!
92
- @exchange.on_return do |return_info, metadata, content|
93
- begin
94
- logger.warn "Request #{metadata[:correlation_id]} returned"
95
- request = @requests[metadata[:correlation_id]]
96
- if request
97
- request.cancel!
98
- end
99
- rescue => e
100
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
101
- end
102
- end
103
- end
104
-
105
- def handle_responses!
106
- @subscriptions << @reply_queue.subscribe do |delivery_info, metadata, payload|
107
- begin
108
- logger.debug "Response received: #{payload}"
109
- request = @requests[metadata[:correlation_id]]
110
- if request
111
- request.response = Fleck::Client::Response.new(payload)
112
- else
113
- logger.warn "Request #{metadata[:correlation_id]} not found!"
114
- end
115
- rescue => e
116
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
117
- end
118
- end
119
- end
120
- end
121
- end
122
-
123
- require "fleck/client/request"
124
- require "fleck/client/response"
1
+
2
+ module Fleck
3
+ class Client
4
+ include Fleck::Loggable
5
+
6
+ attr_reader :local_ip, :remote_ip
7
+
8
+ def initialize(connection, queue_name = "", exchange_type: :direct, exchange_name: "", multiple_responses: false, concurrency: 1)
9
+ @connection = connection
10
+ @queue_name = queue_name
11
+ @multiple_responses = multiple_responses
12
+ @default_timeout = multiple_responses ? 60 : nil
13
+ @concurrency = [concurrency.to_i, 1].max
14
+ @requests = ThreadSafe::Hash.new
15
+ @subscriptions = ThreadSafe::Array.new
16
+ @terminated = false
17
+ @mutex = Mutex.new
18
+ @local_ip = @connection.transport.socket.local_address.ip_address
19
+ @remote_ip = @connection.transport.socket.remote_address.ip_address
20
+
21
+ @channel = @connection.create_channel
22
+ @exchange = Bunny::Exchange.new(@channel, :direct, 'fleck')
23
+ @publisher = Bunny::Exchange.new(@connection.create_channel, exchange_type, exchange_name)
24
+ @reply_queue = @channel.queue("", exclusive: true, auto_delete: true)
25
+ @reply_queue.bind(@exchange, routing_key: @reply_queue.name)
26
+
27
+ handle_returned_messages!
28
+ @concurrency.times { handle_responses! }
29
+
30
+ logger.debug("Client initialized!")
31
+
32
+ at_exit do
33
+ terminate
34
+ end
35
+ end
36
+
37
+ def request(action: nil, version: nil, headers: {}, params: {}, async: @multiple_responses || false, timeout: @default_timeout, queue: @queue_name, rmq_options: {}, &block)
38
+
39
+ if @terminated
40
+ return Fleck::Client::Response.new(Oj.dump({status: 503, errors: ['Service Unavailable'], body: nil} , mode: :compat))
41
+ end
42
+
43
+ request = Fleck::Client::Request.new(
44
+ self, queue, @reply_queue.name,
45
+ action: action,
46
+ version: version,
47
+ headers: headers,
48
+ params: params,
49
+ timeout: timeout,
50
+ multiple_responses: @multiple_responses,
51
+ rmq_options: rmq_options,
52
+ &block
53
+ )
54
+
55
+ @requests[request.id] = request
56
+ request.send!(async)
57
+
58
+ return request.response
59
+ end
60
+
61
+
62
+ def publish(data, options)
63
+ return if @terminated
64
+ @mutex.synchronize { @publisher.publish(data, options) }
65
+ end
66
+
67
+
68
+ def remove_request(request_id)
69
+ @requests.delete request_id
70
+ end
71
+
72
+
73
+ def terminate
74
+ @terminated = true
75
+ logger.info "Unsubscribing from #{@reply_queue.name}"
76
+ @subscriptions.map(&:cancel) # stop receiving new messages
77
+ logger.info "Canceling pending requests"
78
+ # cancel pending requests
79
+ while item = @requests.shift do
80
+ begin
81
+ item[1].cancel!
82
+ rescue => e
83
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ protected
90
+
91
+ def handle_returned_messages!
92
+ @exchange.on_return do |return_info, metadata, content|
93
+ begin
94
+ logger.warn "Request #{metadata[:correlation_id]} returned"
95
+ request = @requests[metadata[:correlation_id]]
96
+ if request
97
+ request.cancel!
98
+ end
99
+ rescue => e
100
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
101
+ end
102
+ end
103
+ end
104
+
105
+ def handle_responses!
106
+ @subscriptions << @reply_queue.subscribe do |delivery_info, metadata, payload|
107
+ begin
108
+ logger.debug "Response received: #{payload}"
109
+ request = @requests[metadata[:correlation_id]]
110
+ if request
111
+ request.response = Fleck::Client::Response.new(payload)
112
+ else
113
+ logger.warn "Request #{metadata[:correlation_id]} not found!"
114
+ end
115
+ rescue => e
116
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ require "fleck/client/request"
124
+ require "fleck/client/response"
@@ -1,144 +1,149 @@
1
-
2
- module Fleck
3
- class Configuration
4
-
5
- attr_reader :logfile, :loglevel, :progname, :hosts
6
- attr_accessor :default_user, :default_pass, :default_host, :default_port, :default_vhost, :default_queue,
7
- :app_name, :filters
8
-
9
- def initialize
10
- @logfile = STDOUT
11
- @loglevel = ::Logger::INFO
12
- @progname = "Fleck"
13
- @app_name = $0
14
- @default_host = "127.0.0.1"
15
- @default_port = 5672
16
- @default_user = nil
17
- @default_pass = nil
18
- @default_vhost = "/"
19
- @default_queue = "default"
20
- @filters = ["password", "secret", "token"]
21
- @hosts = []
22
- @credentials = {}
23
- end
24
-
25
- def hosts=(*args)
26
- args.flatten.each do |host|
27
- add_host host
28
- end
29
- return @hosts
30
- end
31
-
32
- def add_host(data)
33
- if data.is_a?(String)
34
- host, port = data.split(":")
35
- port = port ? port.to_i : 5672
36
- @hosts << Fleck::HostRating.new(host: host, port: port)
37
- @credentials["#{host}:#{port}"] ||= { user: @default_user, pass: @default_pass }
38
- elsif data.is_a?(Hash)
39
- data = data.to_hash_with_indifferent_access
40
- host = data[:host] || @default_host
41
- port = data[:port] || @default_port
42
- @hosts << Fleck::HostRating.new(host: data[:host] || @default_host, port: data[:port] || @default_port)
43
- @credentials["#{host}:#{port}"] ||= { user: data[:user] || @default_user, pass: data[:pass] || @default_pass }
44
- else
45
- raise ArgumentError.new("Invalid host type #{data.inspect}: String or Hash expected")
46
- end
47
- end
48
-
49
- def default_options
50
- best = @hosts.sort.first
51
- opts = {}
52
-
53
- host = best ? best.host : @default_host
54
- port = best ? best.port : @default_port
55
- credentials = @credentials["#{host}:#{port}"] || {user: @default_user, pass: @default_pass}
56
-
57
- opts[:host] = host
58
- opts[:port] = port
59
- opts[:user] = credentials[:user] || @default_user
60
- opts[:pass] = credentials[:pass] || @default_pass
61
- opts[:vhost] = @default_vhost
62
- opts[:queue] = @default_queue
63
-
64
- return opts
65
- end
66
-
67
- def logfile=(new_logfile)
68
- if @logfile != new_logfile
69
- @logfile = new_logfile
70
- reset_logger
71
- end
72
-
73
- return @logfile
74
- end
75
-
76
- def loglevel=(new_loglevel)
77
- @loglevel = new_loglevel
78
- @logger.level = @loglevel if @logger
79
-
80
- return @loglevel
81
- end
82
-
83
- def progname=(new_progname)
84
- @progname = new_progname
85
- @logger.progname = @progname if @logger
86
-
87
- return @progname
88
- end
89
-
90
- def logger
91
- return @logger || reset_logger
92
- end
93
-
94
- def logger=(new_logger)
95
- if new_logger.nil?
96
- @logger.close if @logger
97
- @logger = ::Logger.new(nil)
98
- else
99
- @logger.close if @logger
100
- @logger = new_logger.clone
101
- @logger.formatter = formatter
102
- @logger.progname = @progname
103
- @logger.level = @loglevel
104
- end
105
-
106
- return @logger
107
- end
108
-
109
- def reset_logger
110
- new_logger = ::Logger.new(@logfile)
111
- new_logger.formatter = formatter
112
- new_logger.progname = @progname
113
- new_logger.level = @loglevel
114
- @logger.close if @logger
115
- @logger = new_logger
116
-
117
- return @logger
118
- end
119
-
120
- def formatter
121
- return @formatter if @formatter
122
-
123
- @formatter = proc do |severity, datetime, progname, msg|
124
- color = :blue
125
- case severity
126
- when 'DEBUG'
127
- color = "#512DA8"
128
- when 'INFO'
129
- color = "#33691E"
130
- when 'WARN'
131
- color = "#E65100"
132
- when 'ERROR', 'FATAL'
133
- color = "#B71C1C"
134
- else
135
- color = "#00BCD4"
136
- end
137
- "[#{datetime.strftime('%F %T.%L')}]".color(:cyan) + "(#{$$})".color(:blue) + "|#{severity}|".color(color) +
138
- (progname ? "<#{progname}>".color(:yellow) : "") + " #{msg}\n"
139
- end
140
-
141
- return @formatter
142
- end
143
- end
144
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Open `Fleck` module to add `Configuration` class implementation.
4
+ module Fleck
5
+ # `Fleck::Configuration` implements a set of methods useful for `Fleck` clients and consumers configuration.
6
+ class Configuration
7
+ autoload :HostRating, 'fleck/utilities/host_rating.rb'
8
+
9
+ attr_reader :logfile, :loglevel, :progname, :hosts
10
+ attr_accessor :default_user, :default_pass, :default_host, :default_port, :default_vhost, :default_queue,
11
+ :app_name, :filters
12
+
13
+ def initialize
14
+ @logfile = $stdout
15
+ @loglevel = ::Logger::INFO
16
+ @progname = 'Fleck'
17
+ @app_name = $PROGRAM_NAME
18
+ @default_host = '127.0.0.1'
19
+ @default_port = 5672
20
+ @default_user = nil
21
+ @default_pass = nil
22
+ @default_vhost = '/'
23
+ @default_queue = 'default'
24
+ @filters = %w[password secret token]
25
+ @hosts = []
26
+ @credentials = {}
27
+ end
28
+
29
+ def hosts=(*args)
30
+ args.flatten.each do |host|
31
+ add_host host
32
+ end
33
+ end
34
+
35
+ def add_host(data)
36
+ case data
37
+ when String then add_host_from_string(data)
38
+ when Hash then add_host_from_hash(data)
39
+ else
40
+ raise ArgumentError, "Invalid host type #{data.inspect}: String or Hash expected"
41
+ end
42
+ end
43
+
44
+ def default_options
45
+ best = @hosts.min
46
+ opts = {}
47
+
48
+ host = best ? best.host : @default_host
49
+ port = best ? best.port : @default_port
50
+ credentials = @credentials["#{host}:#{port}"] || { user: @default_user, pass: @default_pass }
51
+
52
+ opts[:host] = host
53
+ opts[:port] = port
54
+ opts[:user] = credentials[:user] || @default_user
55
+ opts[:pass] = credentials[:pass] || @default_pass
56
+ opts[:vhost] = @default_vhost
57
+ opts[:queue] = @default_queue
58
+
59
+ opts
60
+ end
61
+
62
+ def logfile=(new_logfile)
63
+ return unless @logfile != new_logfile
64
+
65
+ @logfile = new_logfile
66
+ reset_logger
67
+ end
68
+
69
+ def loglevel=(new_loglevel)
70
+ @loglevel = new_loglevel
71
+ @logger.level = @loglevel if @logger
72
+ end
73
+
74
+ def progname=(new_progname)
75
+ @progname = new_progname
76
+ @logger.progname = @progname if @logger
77
+ end
78
+
79
+ def logger
80
+ @logger || reset_logger
81
+ end
82
+
83
+ def logger=(new_logger)
84
+ @logger&.close
85
+
86
+ if new_logger.nil?
87
+ @logger = ::Logger.new(nil)
88
+ else
89
+ @logger = new_logger.clone
90
+ @logger.formatter = formatter
91
+ @logger.progname = @progname
92
+ @logger.level = @loglevel
93
+ end
94
+ end
95
+
96
+ def reset_logger
97
+ new_logger = ::Logger.new(@logfile)
98
+ new_logger.formatter = formatter
99
+ new_logger.progname = @progname
100
+ new_logger.level = @loglevel
101
+ @logger&.close
102
+ @logger = new_logger
103
+
104
+ @logger
105
+ end
106
+
107
+ def formatter
108
+ return @formatter if @formatter
109
+
110
+ @formatter = proc do |severity, datetime, progname, msg|
111
+ color = severity_color(severity)
112
+ "[#{datetime.strftime('%F %T.%L')}]".color(:cyan) +
113
+ "(#{$PID})".color(:blue) +
114
+ "|#{severity}|".color(color) +
115
+ (progname ? "<#{progname}>".color(:yellow) : '') +
116
+ " #{msg}\n"
117
+ end
118
+
119
+ @formatter
120
+ end
121
+
122
+ private
123
+
124
+ def add_host_from_string(data)
125
+ host, port = data.split(':')
126
+ port = port ? port.to_i : 5672
127
+ @hosts << Fleck::HostRating.new(host: host, port: port)
128
+ @credentials["#{host}:#{port}"] ||= { user: @default_user, pass: @default_pass }
129
+ end
130
+
131
+ def add_host_from_hash(data)
132
+ data = data.to_hash_with_indifferent_access
133
+ host = data[:host] || @default_host
134
+ port = data[:port] || @default_port
135
+ @hosts << Fleck::HostRating.new(host: host, port: port)
136
+ @credentials["#{host}:#{port}"] ||= { user: data[:user] || @default_user, pass: data[:pass] || @default_pass }
137
+ end
138
+
139
+ def severity_color(severity)
140
+ case severity
141
+ when 'DEBUG' then '#512DA8'
142
+ when 'INFO' then '#33691E'
143
+ when 'WARN' then '#E65100'
144
+ when 'ERROR', 'FATAL' then '#B71C1C'
145
+ else '#00BCD4'
146
+ end
147
+ end
148
+ end
149
+ end