fleck 1.0.1 → 2.1.2

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 +60 -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 +147 -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 +79 -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 +56 -0
  22. data/lib/fleck/core/consumer/logger.rb +88 -0
  23. data/lib/fleck/core/consumer/request.rb +100 -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 +82 -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,147 @@
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
+ attr_reader :logfile, :loglevel, :progname, :hosts
8
+ attr_accessor :default_user, :default_pass, :default_host, :default_port, :default_vhost, :default_queue,
9
+ :app_name, :filters
10
+
11
+ def initialize
12
+ @logfile = $stdout
13
+ @loglevel = ::Logger::INFO
14
+ @progname = 'Fleck'
15
+ @app_name = $PROGRAM_NAME
16
+ @default_host = '127.0.0.1'
17
+ @default_port = 5672
18
+ @default_user = nil
19
+ @default_pass = nil
20
+ @default_vhost = '/'
21
+ @default_queue = 'default'
22
+ @filters = %w[password secret token]
23
+ @hosts = []
24
+ @credentials = {}
25
+ end
26
+
27
+ def hosts=(*args)
28
+ args.flatten.each do |host|
29
+ add_host host
30
+ end
31
+ end
32
+
33
+ def add_host(data)
34
+ case data
35
+ when String then add_host_from_string(data)
36
+ when Hash then add_host_from_hash(data)
37
+ else
38
+ raise ArgumentError, "Invalid host type #{data.inspect}: String or Hash expected"
39
+ end
40
+ end
41
+
42
+ def default_options
43
+ best = @hosts.min
44
+ opts = {}
45
+
46
+ host = best ? best.host : @default_host
47
+ port = best ? best.port : @default_port
48
+ credentials = @credentials["#{host}:#{port}"] || { user: @default_user, pass: @default_pass }
49
+
50
+ opts[:host] = host
51
+ opts[:port] = port
52
+ opts[:user] = credentials[:user] || @default_user
53
+ opts[:pass] = credentials[:pass] || @default_pass
54
+ opts[:vhost] = @default_vhost
55
+ opts[:queue] = @default_queue
56
+
57
+ opts
58
+ end
59
+
60
+ def logfile=(new_logfile)
61
+ return unless @logfile != new_logfile
62
+
63
+ @logfile = new_logfile
64
+ reset_logger
65
+ end
66
+
67
+ def loglevel=(new_loglevel)
68
+ @loglevel = new_loglevel
69
+ @logger.level = @loglevel if @logger
70
+ end
71
+
72
+ def progname=(new_progname)
73
+ @progname = new_progname
74
+ @logger.progname = @progname if @logger
75
+ end
76
+
77
+ def logger
78
+ @logger || reset_logger
79
+ end
80
+
81
+ def logger=(new_logger)
82
+ @logger&.close
83
+
84
+ if new_logger.nil?
85
+ @logger = ::Logger.new(nil)
86
+ else
87
+ @logger = new_logger.clone
88
+ @logger.formatter = formatter
89
+ @logger.progname = @progname
90
+ @logger.level = @loglevel
91
+ end
92
+ end
93
+
94
+ def reset_logger
95
+ new_logger = ::Logger.new(@logfile)
96
+ new_logger.formatter = formatter
97
+ new_logger.progname = @progname
98
+ new_logger.level = @loglevel
99
+ @logger&.close
100
+ @logger = new_logger
101
+
102
+ @logger
103
+ end
104
+
105
+ def formatter
106
+ return @formatter if @formatter
107
+
108
+ @formatter = proc do |severity, datetime, progname, msg|
109
+ color = severity_color(severity)
110
+ "[#{datetime.strftime('%F %T.%L')}]".color(:cyan) +
111
+ "(#{$PID})".color(:blue) +
112
+ "|#{severity}|".color(color) +
113
+ (progname ? "<#{progname}>".color(:yellow) : '') +
114
+ " #{msg}\n"
115
+ end
116
+
117
+ @formatter
118
+ end
119
+
120
+ private
121
+
122
+ def add_host_from_string(data)
123
+ host, port = data.split(':')
124
+ port = port ? port.to_i : 5672
125
+ @hosts << Fleck::HostRating.new(host: host, port: port)
126
+ @credentials["#{host}:#{port}"] ||= { user: @default_user, pass: @default_pass }
127
+ end
128
+
129
+ def add_host_from_hash(data)
130
+ data = data.to_hash_with_indifferent_access
131
+ host = data[:host] || @default_host
132
+ port = data[:port] || @default_port
133
+ @hosts << Fleck::HostRating.new(host: host, port: port)
134
+ @credentials["#{host}:#{port}"] ||= { user: data[:user] || @default_user, pass: data[:pass] || @default_pass }
135
+ end
136
+
137
+ def severity_color(severity)
138
+ case severity
139
+ when 'DEBUG' then '#512DA8'
140
+ when 'INFO' then '#33691E'
141
+ when 'WARN' then '#E65100'
142
+ when 'ERROR', 'FATAL' then '#B71C1C'
143
+ else '#00BCD4'
144
+ end
145
+ end
146
+ end
147
+ end