actioncable 6.1.7.9 → 7.2.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -160
- data/MIT-LICENSE +1 -1
- data/README.md +5 -5
- data/app/assets/javascripts/action_cable.js +239 -302
- data/app/assets/javascripts/actioncable.esm.js +512 -0
- data/app/assets/javascripts/actioncable.js +510 -0
- data/lib/action_cable/channel/base.rb +114 -90
- data/lib/action_cable/channel/broadcasting.rb +25 -16
- data/lib/action_cable/channel/callbacks.rb +39 -0
- data/lib/action_cable/channel/naming.rb +10 -7
- data/lib/action_cable/channel/periodic_timers.rb +7 -7
- data/lib/action_cable/channel/streams.rb +81 -68
- data/lib/action_cable/channel/test_case.rb +133 -87
- data/lib/action_cable/connection/authorization.rb +4 -1
- data/lib/action_cable/connection/base.rb +71 -43
- data/lib/action_cable/connection/callbacks.rb +57 -0
- data/lib/action_cable/connection/client_socket.rb +3 -1
- data/lib/action_cable/connection/identification.rb +10 -6
- data/lib/action_cable/connection/internal_channel.rb +7 -2
- data/lib/action_cable/connection/message_buffer.rb +4 -1
- data/lib/action_cable/connection/stream.rb +2 -2
- data/lib/action_cable/connection/stream_event_loop.rb +4 -4
- data/lib/action_cable/connection/subscriptions.rb +8 -3
- data/lib/action_cable/connection/tagged_logger_proxy.rb +14 -9
- data/lib/action_cable/connection/test_case.rb +67 -55
- data/lib/action_cable/connection/web_socket.rb +11 -7
- data/lib/action_cable/deprecator.rb +9 -0
- data/lib/action_cable/engine.rb +28 -9
- data/lib/action_cable/gem_version.rb +7 -5
- data/lib/action_cable/helpers/action_cable_helper.rb +21 -18
- data/lib/action_cable/remote_connections.rb +25 -13
- data/lib/action_cable/server/base.rb +29 -14
- data/lib/action_cable/server/broadcasting.rb +24 -16
- data/lib/action_cable/server/configuration.rb +28 -14
- data/lib/action_cable/server/connections.rb +13 -5
- data/lib/action_cable/server/worker/active_record_connection_management.rb +4 -2
- data/lib/action_cable/server/worker.rb +7 -7
- data/lib/action_cable/subscription_adapter/async.rb +1 -1
- data/lib/action_cable/subscription_adapter/base.rb +2 -0
- data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
- data/lib/action_cable/subscription_adapter/inline.rb +2 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +6 -5
- data/lib/action_cable/subscription_adapter/redis.rb +101 -25
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
- data/lib/action_cable/subscription_adapter/test.rb +7 -6
- data/lib/action_cable/test_case.rb +2 -0
- data/lib/action_cable/test_helper.rb +89 -59
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +30 -12
- data/lib/rails/generators/channel/USAGE +14 -8
- data/lib/rails/generators/channel/channel_generator.rb +95 -20
- data/lib/rails/generators/channel/templates/javascript/index.js.tt +1 -5
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +29 -15
- data/lib/action_cable/channel.rb +0 -17
- data/lib/action_cable/connection.rb +0 -22
- data/lib/action_cable/server.rb +0 -16
- data/lib/action_cable/subscription_adapter.rb +0 -12
@@ -1,56 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch"
|
4
6
|
require "active_support/rescuable"
|
5
7
|
|
6
8
|
module ActionCable
|
7
9
|
module Connection
|
8
|
-
#
|
9
|
-
# of all of the channel subscriptions that are created from there on. Incoming messages are then routed to these channel subscriptions
|
10
|
-
# based on an identifier sent by the Action Cable consumer. The Connection itself does not deal with any specific application logic beyond
|
11
|
-
# authentication and authorization.
|
10
|
+
# # Action Cable Connection Base
|
12
11
|
#
|
13
|
-
#
|
12
|
+
# For every WebSocket connection the Action Cable server accepts, a Connection
|
13
|
+
# object will be instantiated. This instance becomes the parent of all of the
|
14
|
+
# channel subscriptions that are created from there on. Incoming messages are
|
15
|
+
# then routed to these channel subscriptions based on an identifier sent by the
|
16
|
+
# Action Cable consumer. The Connection itself does not deal with any specific
|
17
|
+
# application logic beyond authentication and authorization.
|
14
18
|
#
|
15
|
-
#
|
16
|
-
# class Connection < ActionCable::Connection::Base
|
17
|
-
# identified_by :current_user
|
19
|
+
# Here's a basic example:
|
18
20
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# end
|
21
|
+
# module ApplicationCable
|
22
|
+
# class Connection < ActionCable::Connection::Base
|
23
|
+
# identified_by :current_user
|
23
24
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
25
|
+
# def connect
|
26
|
+
# self.current_user = find_verified_user
|
27
|
+
# logger.add_tags current_user.name
|
28
|
+
# end
|
27
29
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# User.find_by_identity(cookies.encrypted[:identity_id]) ||
|
31
|
-
# reject_unauthorized_connection
|
30
|
+
# def disconnect
|
31
|
+
# # Any cleanup work needed when the cable connection is cut.
|
32
32
|
# end
|
33
|
+
#
|
34
|
+
# private
|
35
|
+
# def find_verified_user
|
36
|
+
# User.find_by_identity(cookies.encrypted[:identity_id]) ||
|
37
|
+
# reject_unauthorized_connection
|
38
|
+
# end
|
39
|
+
# end
|
33
40
|
# end
|
34
|
-
# end
|
35
41
|
#
|
36
|
-
# First, we declare that this connection can be identified by its current_user.
|
37
|
-
#
|
38
|
-
#
|
42
|
+
# First, we declare that this connection can be identified by its current_user.
|
43
|
+
# This allows us to later be able to find all connections established for that
|
44
|
+
# current_user (and potentially disconnect them). You can declare as many
|
45
|
+
# identification indexes as you like. Declaring an identification means that an
|
46
|
+
# attr_accessor is automatically set for that key.
|
39
47
|
#
|
40
|
-
# Second, we rely on the fact that the WebSocket connection is established with
|
41
|
-
#
|
48
|
+
# Second, we rely on the fact that the WebSocket connection is established with
|
49
|
+
# the cookies from the domain being sent along. This makes it easy to use signed
|
50
|
+
# cookies that were set when logging in via a web interface to authorize the
|
51
|
+
# WebSocket connection.
|
42
52
|
#
|
43
|
-
# Finally, we add a tag to the connection-specific logger with the name of the
|
53
|
+
# Finally, we add a tag to the connection-specific logger with the name of the
|
54
|
+
# current user to easily distinguish their messages in the log.
|
44
55
|
#
|
45
56
|
# Pretty simple, eh?
|
46
57
|
class Base
|
47
58
|
include Identification
|
48
59
|
include InternalChannel
|
49
60
|
include Authorization
|
61
|
+
include Callbacks
|
50
62
|
include ActiveSupport::Rescuable
|
51
63
|
|
52
64
|
attr_reader :server, :env, :subscriptions, :logger, :worker_pool, :protocol
|
53
|
-
delegate :event_loop, :pubsub, to: :server
|
65
|
+
delegate :event_loop, :pubsub, :config, to: :server
|
54
66
|
|
55
67
|
def initialize(server, env, coder: ActiveSupport::JSON)
|
56
68
|
@server, @env, @coder = server, env, coder
|
@@ -66,9 +78,11 @@ module ActionCable
|
|
66
78
|
@started_at = Time.now
|
67
79
|
end
|
68
80
|
|
69
|
-
# Called by the server when a new WebSocket connection is established. This
|
70
|
-
#
|
71
|
-
|
81
|
+
# Called by the server when a new WebSocket connection is established. This
|
82
|
+
# configures the callbacks intended for overwriting by the user. This method
|
83
|
+
# should not be called directly -- instead rely upon on the #connect (and
|
84
|
+
# #disconnect) callbacks.
|
85
|
+
def process # :nodoc:
|
72
86
|
logger.info started_request_message
|
73
87
|
|
74
88
|
if websocket.possible? && allow_request_origin?
|
@@ -80,18 +94,24 @@ module ActionCable
|
|
80
94
|
|
81
95
|
# Decodes WebSocket messages and dispatches them to subscribed channels.
|
82
96
|
# WebSocket message transfer encoding is always JSON.
|
83
|
-
def receive(websocket_message)
|
97
|
+
def receive(websocket_message) # :nodoc:
|
84
98
|
send_async :dispatch_websocket_message, websocket_message
|
85
99
|
end
|
86
100
|
|
87
|
-
def dispatch_websocket_message(websocket_message)
|
101
|
+
def dispatch_websocket_message(websocket_message) # :nodoc:
|
88
102
|
if websocket.alive?
|
89
|
-
|
103
|
+
handle_channel_command decode(websocket_message)
|
90
104
|
else
|
91
105
|
logger.error "Ignoring message processed after the WebSocket was closed: #{websocket_message.inspect})"
|
92
106
|
end
|
93
107
|
end
|
94
108
|
|
109
|
+
def handle_channel_command(payload)
|
110
|
+
run_callbacks :command do
|
111
|
+
subscriptions.execute_command payload
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
95
115
|
def transmit(cable_message) # :nodoc:
|
96
116
|
websocket.transmit encode(cable_message)
|
97
117
|
end
|
@@ -106,13 +126,15 @@ module ActionCable
|
|
106
126
|
websocket.close
|
107
127
|
end
|
108
128
|
|
109
|
-
# Invoke a method on the connection asynchronously through the pool of thread
|
129
|
+
# Invoke a method on the connection asynchronously through the pool of thread
|
130
|
+
# workers.
|
110
131
|
def send_async(method, *arguments)
|
111
132
|
worker_pool.async_invoke(self, method, *arguments)
|
112
133
|
end
|
113
134
|
|
114
|
-
# Return a basic hash of statistics for the connection keyed with
|
115
|
-
# This can be returned by a
|
135
|
+
# Return a basic hash of statistics for the connection keyed with `identifier`,
|
136
|
+
# `started_at`, `subscriptions`, and `request_id`. This can be returned by a
|
137
|
+
# health check against the connection.
|
116
138
|
def statistics
|
117
139
|
{
|
118
140
|
identifier: connection_identifier,
|
@@ -143,11 +165,16 @@ module ActionCable
|
|
143
165
|
send_async :handle_close
|
144
166
|
end
|
145
167
|
|
168
|
+
def inspect # :nodoc:
|
169
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
170
|
+
end
|
171
|
+
|
146
172
|
private
|
147
173
|
attr_reader :websocket
|
148
174
|
attr_reader :message_buffer
|
149
175
|
|
150
|
-
# The request that initiated the WebSocket connection is available here. This
|
176
|
+
# The request that initiated the WebSocket connection is available here. This
|
177
|
+
# gives access to the environment, cookies, etc.
|
151
178
|
def request # :doc:
|
152
179
|
@request ||= begin
|
153
180
|
environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
|
@@ -155,7 +182,8 @@ module ActionCable
|
|
155
182
|
end
|
156
183
|
end
|
157
184
|
|
158
|
-
# The cookies of the request that initiated the WebSocket connection. Useful for
|
185
|
+
# The cookies of the request that initiated the WebSocket connection. Useful for
|
186
|
+
# performing authorization checks.
|
159
187
|
def cookies # :doc:
|
160
188
|
request.cookie_jar
|
161
189
|
end
|
@@ -192,9 +220,8 @@ module ActionCable
|
|
192
220
|
end
|
193
221
|
|
194
222
|
def send_welcome_message
|
195
|
-
# Send welcome message to the internal connection monitor channel.
|
196
|
-
#
|
197
|
-
# websocket connection.
|
223
|
+
# Send welcome message to the internal connection monitor channel. This ensures
|
224
|
+
# the connection monitor state is reset after a successful websocket connection.
|
198
225
|
transmit type: ActionCable::INTERNAL[:message_types][:welcome]
|
199
226
|
end
|
200
227
|
|
@@ -222,10 +249,11 @@ module ActionCable
|
|
222
249
|
|
223
250
|
logger.error invalid_request_message
|
224
251
|
logger.info finished_request_message
|
225
|
-
[ 404, {
|
252
|
+
[ 404, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" }, [ "Page not found" ] ]
|
226
253
|
end
|
227
254
|
|
228
|
-
# Tags are declared in the server but computed in the connection. This allows us
|
255
|
+
# Tags are declared in the server but computed in the connection. This allows us
|
256
|
+
# per-connection tailored tags.
|
229
257
|
def new_tagged_logger
|
230
258
|
TaggedLoggerProxy.new server.logger,
|
231
259
|
tags: server.config.log_tags.map { |tag| tag.respond_to?(:call) ? tag.call(request) : tag.to_s.camelize }
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "active_support/callbacks"
|
6
|
+
|
7
|
+
module ActionCable
|
8
|
+
module Connection
|
9
|
+
# # Action Cable Connection Callbacks
|
10
|
+
#
|
11
|
+
# The [before_command](rdoc-ref:ClassMethods#before_command),
|
12
|
+
# [after_command](rdoc-ref:ClassMethods#after_command), and
|
13
|
+
# [around_command](rdoc-ref:ClassMethods#around_command) callbacks are invoked
|
14
|
+
# when sending commands to the client, such as when subscribing, unsubscribing,
|
15
|
+
# or performing an action.
|
16
|
+
#
|
17
|
+
# #### Example
|
18
|
+
#
|
19
|
+
# module ApplicationCable
|
20
|
+
# class Connection < ActionCable::Connection::Base
|
21
|
+
# identified_by :user
|
22
|
+
#
|
23
|
+
# around_command :set_current_account
|
24
|
+
#
|
25
|
+
# private
|
26
|
+
#
|
27
|
+
# def set_current_account
|
28
|
+
# # Now all channels could use Current.account
|
29
|
+
# Current.set(account: user.account) { yield }
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
module Callbacks
|
35
|
+
extend ActiveSupport::Concern
|
36
|
+
include ActiveSupport::Callbacks
|
37
|
+
|
38
|
+
included do
|
39
|
+
define_callbacks :command
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def before_command(*methods, &block)
|
44
|
+
set_callback(:command, :before, *methods, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_command(*methods, &block)
|
48
|
+
set_callback(:command, :after, *methods, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def around_command(*methods, &block)
|
52
|
+
set_callback(:command, :around, *methods, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "websocket/driver"
|
4
6
|
|
5
7
|
module ActionCable
|
@@ -43,7 +45,7 @@ module ActionCable
|
|
43
45
|
|
44
46
|
@ready_state = CONNECTING
|
45
47
|
|
46
|
-
# The driver calls
|
48
|
+
# The driver calls `env`, `url`, and `write`
|
47
49
|
@driver = ::WebSocket::Driver.rack(self, protocols: protocols)
|
48
50
|
|
49
51
|
@driver.on(:open) { |e| open }
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "set"
|
4
6
|
|
5
7
|
module ActionCable
|
@@ -12,21 +14,23 @@ module ActionCable
|
|
12
14
|
end
|
13
15
|
|
14
16
|
module ClassMethods
|
15
|
-
# Mark a key as being a connection identifier index that can then be used to
|
16
|
-
#
|
17
|
+
# Mark a key as being a connection identifier index that can then be used to
|
18
|
+
# find the specific connection again later. Common identifiers are current_user
|
19
|
+
# and current_account, but could be anything, really.
|
17
20
|
#
|
18
|
-
# Note that anything marked as an identifier will automatically create a
|
19
|
-
# channel instances created off the connection.
|
21
|
+
# Note that anything marked as an identifier will automatically create a
|
22
|
+
# delegate by the same name on any channel instances created off the connection.
|
20
23
|
def identified_by(*identifiers)
|
21
24
|
Array(identifiers).each { |identifier| attr_accessor identifier }
|
22
25
|
self.identifiers += identifiers
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
# Return a single connection identifier that combines the value of all the
|
29
|
+
# Return a single connection identifier that combines the value of all the
|
30
|
+
# registered identifiers into a single gid.
|
27
31
|
def connection_identifier
|
28
32
|
unless defined? @connection_identifier
|
29
|
-
@connection_identifier = connection_gid identifiers.
|
33
|
+
@connection_identifier = connection_gid identifiers.filter_map { |id| instance_variable_get("@#{id}") }
|
30
34
|
end
|
31
35
|
|
32
36
|
@connection_identifier
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionCable
|
4
6
|
module Connection
|
5
|
-
#
|
7
|
+
# # Action Cable InternalChannel
|
8
|
+
#
|
9
|
+
# Makes it possible for the RemoteConnection to disconnect a specific
|
10
|
+
# connection.
|
6
11
|
module InternalChannel
|
7
12
|
extend ActiveSupport::Concern
|
8
13
|
|
@@ -32,7 +37,7 @@ module ActionCable
|
|
32
37
|
case message["type"]
|
33
38
|
when "disconnect"
|
34
39
|
logger.info "Removing connection (#{connection_identifier})"
|
35
|
-
|
40
|
+
close(reason: ActionCable::INTERNAL[:disconnect_reasons][:remote], reconnect: message.fetch("reconnect", true))
|
36
41
|
end
|
37
42
|
rescue Exception => e
|
38
43
|
logger.error "There was an exception - #{e.class}(#{e.message})"
|
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionCable
|
4
6
|
module Connection
|
5
|
-
# Allows us to buffer messages received from the WebSocket before the Connection
|
7
|
+
# Allows us to buffer messages received from the WebSocket before the Connection
|
8
|
+
# has been fully initialized, and is ready to receive them.
|
6
9
|
class MessageBuffer # :nodoc:
|
7
10
|
def initialize(connection)
|
8
11
|
@connection = connection
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# :markup: markdown
|
4
4
|
|
5
5
|
module ActionCable
|
6
6
|
module Connection
|
@@ -100,7 +100,7 @@ module ActionCable
|
|
100
100
|
|
101
101
|
# This should return the underlying io according to the SPEC:
|
102
102
|
@rack_hijack_io = @socket_object.env["rack.hijack"].call
|
103
|
-
# Retain existing
|
103
|
+
# Retain existing behavior if required:
|
104
104
|
@rack_hijack_io ||= @socket_object.env["rack.hijack_io"]
|
105
105
|
|
106
106
|
@event_loop.attach(@rack_hijack_io, self)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "nio"
|
4
|
-
require "thread"
|
5
6
|
|
6
7
|
module ActionCable
|
7
8
|
module Connection
|
@@ -117,9 +118,8 @@ module ActionCable
|
|
117
118
|
stream.receive incoming
|
118
119
|
end
|
119
120
|
rescue
|
120
|
-
# We expect one of EOFError or Errno::ECONNRESET in
|
121
|
-
#
|
122
|
-
# anything else goes wrong, this is still the best way
|
121
|
+
# We expect one of EOFError or Errno::ECONNRESET in normal operation (when the
|
122
|
+
# client goes away). But if anything else goes wrong, this is still the best way
|
123
123
|
# to handle it.
|
124
124
|
begin
|
125
125
|
stream.close
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/hash/indifferent_access"
|
4
6
|
|
5
7
|
module ActionCable
|
6
8
|
module Connection
|
7
|
-
#
|
8
|
-
#
|
9
|
+
# # Action Cable Connection Subscriptions
|
10
|
+
#
|
11
|
+
# Collection class for all the channel subscriptions established on a given
|
12
|
+
# connection. Responsible for routing incoming commands that arrive on the
|
13
|
+
# connection to the proper channel.
|
9
14
|
class Subscriptions # :nodoc:
|
10
15
|
def initialize(connection)
|
11
16
|
@connection = connection
|
@@ -33,7 +38,7 @@ module ActionCable
|
|
33
38
|
|
34
39
|
subscription_klass = id_options[:channel].safe_constantize
|
35
40
|
|
36
|
-
if subscription_klass && ActionCable::Channel::Base
|
41
|
+
if subscription_klass && ActionCable::Channel::Base > subscription_klass
|
37
42
|
subscription = subscription_klass.new(connection, id_key, id_options)
|
38
43
|
subscriptions[id_key] = subscription
|
39
44
|
subscription.subscribe_to_channel
|
@@ -1,10 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionCable
|
4
6
|
module Connection
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
7
|
+
# # Action Cable Connection TaggedLoggerProxy
|
8
|
+
#
|
9
|
+
# Allows the use of per-connection tags against the server logger. This wouldn't
|
10
|
+
# work using the traditional ActiveSupport::TaggedLogging enhanced Rails.logger,
|
11
|
+
# as that logger will reset the tags between requests. The connection is
|
12
|
+
# long-lived, so it needs its own set of tags for its independent duration.
|
8
13
|
class TaggedLoggerProxy
|
9
14
|
attr_reader :tags
|
10
15
|
|
@@ -18,24 +23,24 @@ module ActionCable
|
|
18
23
|
@tags = @tags.uniq
|
19
24
|
end
|
20
25
|
|
21
|
-
def tag(logger)
|
26
|
+
def tag(logger, &block)
|
22
27
|
if logger.respond_to?(:tagged)
|
23
28
|
current_tags = tags - logger.formatter.current_tags
|
24
|
-
logger.tagged(*current_tags)
|
29
|
+
logger.tagged(*current_tags, &block)
|
25
30
|
else
|
26
31
|
yield
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
35
|
%i( debug info warn error fatal unknown ).each do |severity|
|
31
|
-
define_method(severity) do |message|
|
32
|
-
log severity, message
|
36
|
+
define_method(severity) do |message = nil, &block|
|
37
|
+
log severity, message, &block
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
36
41
|
private
|
37
|
-
def log(type, message) # :doc:
|
38
|
-
tag(@logger) { @logger.send type, message }
|
42
|
+
def log(type, message, &block) # :doc:
|
43
|
+
tag(@logger) { @logger.send type, message, &block }
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|