broadcaster 1.0.2 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ceb2fcd033b6405112a208cc1feed602dce814168f445235e07a1274848b66ff
4
- data.tar.gz: 767ecb5b8008dcd2d4de5d7da5a194535212005acab53f29e70211658041dc36
3
+ metadata.gz: 8db15deb65dc4589963f402e1db172de1fd5cc31c8e46f99e34897a5dc1f3409
4
+ data.tar.gz: 57fb1bf72218bbfe631fd8547488b4408921ccec0cce2d09d389eb1cc5996f6b
5
5
  SHA512:
6
- metadata.gz: 61c646e1eef1b4a3ae2a393b53c0432ae5607497799280b8f99aaa8a63206d868874082665aa5c070fff674d51816cd9445377aeed36553968421ca97ad5df78
7
- data.tar.gz: 312d25d807de4f462f6c87cfe63ffac69de515db14be52c18603781051557d99ea3e94d2d0cb509c974e9e1558d2f1f7faa18cc604d6b6a6916b99deb7cc9378
6
+ metadata.gz: 581d6bcc885ee1366c70e73bd70fad9ba4072b525046e6dde9bb0e0d7091c47edaacc643ee31b06658591c53b41d2a212be04ca7479f77b948f0da8b423e3bed
7
+ data.tar.gz: 012557be64bfdcda584ac04cea11f456cbc2c0f3f5eab7e83659260288259be837c8bf76e5dc548da1b5fe4d31aa5f11f2afae410651d20815358dfe77b99920
@@ -0,0 +1,55 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby-version: ['2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', '3.4']
17
+ include:
18
+ - ruby-version: '4.0'
19
+ allow-failure: true
20
+ - ruby-version: ruby-head
21
+ allow-failure: true
22
+ - ruby-version: jruby-head
23
+ allow-failure: true
24
+
25
+ continue-on-error: ${{ matrix.allow-failure || false }}
26
+
27
+ services:
28
+ redis:
29
+ image: redis
30
+ ports:
31
+ - 6379:6379
32
+ options: >-
33
+ --health-cmd "redis-cli ping"
34
+ --health-interval 10s
35
+ --health-timeout 5s
36
+ --health-retries 5
37
+
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - name: Set up Ruby ${{ matrix.ruby-version }}
42
+ uses: ruby/setup-ruby@v1
43
+ with:
44
+ ruby-version: ${{ matrix.ruby-version }}
45
+ bundler-cache: true
46
+
47
+ - name: Run tests
48
+ run: bundle exec rake spec
49
+
50
+ - name: Upload coverage to Codecov
51
+ if: matrix.ruby-version == '3.4'
52
+ uses: codecov/codecov-action@v5
53
+ with:
54
+ files: coverage/.resultset.json
55
+ token: ${{ secrets.CODECOV_TOKEN }}
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Broadcaster
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/broadcaster.svg)](https://rubygems.org/gems/broadcaster)
4
- [![Build Status](https://travis-ci.org/gabynaiman/broadcaster.svg?branch=master)](https://travis-ci.org/gabynaiman/broadcaster)
5
- [![Coverage Status](https://coveralls.io/repos/gabynaiman/broadcaster/badge.svg?branch=master)](https://coveralls.io/r/gabynaiman/broadcaster?branch=master)
6
- [![Code Climate](https://codeclimate.com/github/gabynaiman/broadcaster.svg)](https://codeclimate.com/github/gabynaiman/broadcaster)
4
+ [![CI](https://github.com/gabynaiman/broadcaster/actions/workflows/ci.yml/badge.svg)](https://github.com/gabynaiman/broadcaster/actions/workflows/ci.yml)
7
5
 
8
6
  Broadcasting based on Redis PubSub
9
7
 
8
+ Requires a running Redis server.
9
+
10
10
  ## Installation
11
11
 
12
12
  Add this line to your application's Gemfile:
@@ -40,6 +40,46 @@ broadcaster.unsubscribe subscription_id
40
40
  broadcaster.unsubscribe_all
41
41
  ```
42
42
 
43
+ Any callable object can be used instead of a block:
44
+
45
+ ```ruby
46
+ class Receiver
47
+ def call(message)
48
+ puts message
49
+ end
50
+ end
51
+
52
+ broadcaster.subscribe 'channel_1', Receiver.new
53
+ ```
54
+
55
+ Messages are serialized with `Marshal`, so any serializable Ruby object can be published.
56
+
57
+ Each subscriber runs isolated from the others: if one raises an error it is logged
58
+ and the message is still delivered to the remaining subscribers.
59
+
60
+ ## Scoping
61
+
62
+ Channels are scoped by the broadcaster `id`. Without an explicit `id` each instance
63
+ gets a random one, so instances are isolated from each other:
64
+
65
+ ```ruby
66
+ broadcaster_1 = Broadcaster.new
67
+ broadcaster_2 = Broadcaster.new
68
+
69
+ broadcaster_2.subscribe('channel_1') { |message| puts message }
70
+ broadcaster_1.publish 'channel_1', 'text' # not received by broadcaster_2
71
+ ```
72
+
73
+ Sharing the same `id` makes them see each other's messages, even across processes:
74
+
75
+ ```ruby
76
+ broadcaster_1 = Broadcaster.new id: 'my_app'
77
+ broadcaster_2 = Broadcaster.new id: 'my_app'
78
+
79
+ broadcaster_2.subscribe('channel_1') { |message| puts message }
80
+ broadcaster_1.publish 'channel_1', 'text' # received by broadcaster_2
81
+ ```
82
+
43
83
  ## Global configuration
44
84
 
45
85
  ```ruby
@@ -52,13 +92,24 @@ Broadcaster.configure do |config|
52
92
  end
53
93
  ```
54
94
 
95
+ | Setting | Default | Description |
96
+ |---|---|---|
97
+ | `redis_client` | `Redic` | Redis client class |
98
+ | `redis_settings` | `'redis://localhost:6379'` | Connection settings passed to the client |
99
+ | `logger` | `Logger.new(STDOUT)` | Logger used by every broadcaster |
100
+ | `reconnection_timeout` | `10` | Seconds to wait before retrying a broken listener connection |
101
+ | `watchdog_interval` | `30` | Seconds between watchdog checks |
102
+ | `watchdog_timeout` | `120` | Seconds without listener activity before restarting it |
103
+
55
104
  ## Options
56
105
 
57
106
  ```ruby
58
107
  options = {
59
108
  id: 'my_app', # Shared broadcaster for multiple processes
60
109
  redis_settings: 'redis://host_name:6379', # Custom redis connection
61
- logger: Logger.new('/file.log') # Custom logger
110
+ logger: Logger.new('/file.log'), # Custom logger
111
+ watchdog_interval: 30, # Watchdog tuning for this instance
112
+ watchdog_timeout: 120
62
113
  # or
63
114
  redis_client: Redic::Sentinels,
64
115
  redis_settings: hosts: [sentinel_1, sentinel_2], master_name: 'mymaster'
@@ -67,6 +118,38 @@ options = {
67
118
  Broadcaster.new options
68
119
  ```
69
120
 
121
+ `reconnection_timeout` can only be set globally.
122
+
123
+ ## Reconnection
124
+
125
+ The listener runs on its own thread. If the connection to Redis drops, it is
126
+ re-established automatically after `reconnection_timeout` seconds, keeping the
127
+ current subscriptions. Publishing while the connection is down raises an error.
128
+
129
+ ## Watchdog
130
+
131
+ Reconnection relies on the connection raising an error. A half-open socket never
132
+ does: if the TCP connection is silently dropped, or a Redis Sentinel failover
133
+ promotes a new master, the listener stays blocked reading from a socket that will
134
+ never deliver another message. The process looks healthy while no message is
135
+ received, and nothing recovers on its own.
136
+
137
+ The watchdog covers that case. It publishes a ping on its own channel every
138
+ `watchdog_interval` seconds, and if no message reaches the listener for
139
+ `watchdog_timeout` seconds it restarts the listener over a new connection —
140
+ which also re-resolves the current master when using `Redic::Sentinels`.
141
+
142
+ `watchdog_timeout` must be comfortably greater than `watchdog_interval`, so that a
143
+ single lost ping does not trigger a restart.
144
+
145
+ ## Shutdown
146
+
147
+ Stops the watchdog and the listener, and closes both connections:
148
+
149
+ ```ruby
150
+ broadcaster.shutdown
151
+ ```
152
+
70
153
  ## Contributing
71
154
 
72
155
  Bug reports and pull requests are welcome on GitHub at https://github.com/gabynaiman/broadcaster.
data/Rakefile CHANGED
@@ -3,6 +3,7 @@ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:spec) do |t|
5
5
  t.libs << 'spec'
6
+ t.libs << 'lib'
6
7
  t.pattern = 'spec/**/*_spec.rb'
7
8
  t.verbose = false
8
9
  t.warning = false
@@ -13,11 +14,12 @@ Rake::TestTask.new(:spec) do |t|
13
14
  t.options << "-l #{ENV['LINE']} " if ENV['LINE'] && ENV['TEST']
14
15
  end
15
16
 
17
+ task default: :spec
18
+
19
+ desc 'IRB console'
16
20
  task :console do
17
- require 'pry'
21
+ require 'irb'
18
22
  require 'broadcaster'
19
23
  ARGV.clear
20
- Pry.start
21
- end
22
-
23
- task default: :spec
24
+ IRB.start
25
+ end
data/broadcaster.gemspec CHANGED
@@ -1,10 +1,11 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'broadcaster/version'
4
5
 
5
6
  Gem::Specification.new do |spec|
6
7
  spec.name = 'broadcaster'
7
- spec.version = '1.0.2'
8
+ spec.version = Broadcaster::VERSION
8
9
  spec.authors = ['Gabriel Naiman']
9
10
  spec.email = ['gabynaiman@gmail.com']
10
11
  spec.summary = 'Broadcasting based on Redis PubSub'
@@ -12,6 +13,8 @@ Gem::Specification.new do |spec|
12
13
  spec.homepage = 'https://github.com/gabynaiman/broadcaster'
13
14
  spec.license = 'MIT'
14
15
 
16
+ spec.required_ruby_version = '>= 2.3.0'
17
+
15
18
  spec.files = `git ls-files -z`.split("\x0")
16
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -20,11 +23,19 @@ Gem::Specification.new do |spec|
20
23
  spec.add_dependency 'redic', '~> 1.5'
21
24
  spec.add_dependency 'class_config', '~> 0.0'
22
25
 
26
+ if RUBY_VERSION >= '4.0'
27
+ spec.add_dependency 'logger'
28
+ spec.add_dependency 'securerandom'
29
+ end
30
+
23
31
  spec.add_development_dependency 'rake', '~> 12.0'
24
32
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
25
33
  spec.add_development_dependency 'minitest-colorin', '~> 0.1'
26
34
  spec.add_development_dependency 'minitest-line', '~> 0.6'
27
35
  spec.add_development_dependency 'simplecov', '~> 0.12'
28
- spec.add_development_dependency 'coveralls', '~> 0.8'
29
- spec.add_development_dependency 'pry-nav', '~> 0.2'
36
+
37
+ if RUBY_VERSION >= '3.3'
38
+ spec.add_development_dependency 'mutex_m'
39
+ spec.add_development_dependency 'ostruct'
40
+ end
30
41
  end
@@ -0,0 +1,3 @@
1
+ class Broadcaster
2
+ VERSION = '1.1.0'
3
+ end
@@ -0,0 +1,47 @@
1
+ class Broadcaster
2
+ class Watchdog
3
+
4
+ CHANNEL = '__watchdog__'.freeze
5
+
6
+ def initialize(broadcaster, interval:, timeout:, logger:)
7
+ @broadcaster = broadcaster
8
+ @interval = interval
9
+ @timeout = timeout
10
+ @logger = logger
11
+ end
12
+
13
+ def start
14
+ @thread = Thread.new do
15
+ loop do
16
+ sleep interval
17
+ check
18
+ end
19
+ end
20
+ end
21
+
22
+ def stop
23
+ thread.kill
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :broadcaster, :interval, :timeout, :logger, :thread
29
+
30
+ def check
31
+ broadcaster.publish CHANNEL, ping: Time.now.to_f
32
+
33
+ return unless unresponsive?
34
+
35
+ logger.warn(self.class.name) { "No notifications received for #{timeout}s" }
36
+ broadcaster.restart_listener
37
+
38
+ rescue => ex
39
+ logger.error(self.class.name) { "#{ex.class}: #{ex.message}" }
40
+ end
41
+
42
+ def unresponsive?
43
+ Time.now - broadcaster.last_notification_at > timeout
44
+ end
45
+
46
+ end
47
+ end
data/lib/broadcaster.rb CHANGED
@@ -3,6 +3,9 @@ require 'class_config'
3
3
  require 'logger'
4
4
  require 'securerandom'
5
5
 
6
+ require_relative 'broadcaster/version'
7
+ require_relative 'broadcaster/watchdog'
8
+
6
9
  class Broadcaster
7
10
 
8
11
  extend ClassConfig
@@ -11,8 +14,10 @@ class Broadcaster
11
14
  attr_config :redis_settings, 'redis://localhost:6379'
12
15
  attr_config :logger, Logger.new(STDOUT)
13
16
  attr_config :reconnection_timeout, 10
17
+ attr_config :watchdog_interval, 30
18
+ attr_config :watchdog_timeout, 120
14
19
 
15
- attr_reader :id
20
+ attr_reader :id, :last_notification_at
16
21
 
17
22
  def initialize(options={})
18
23
  @id = options.fetch(:id, SecureRandom.uuid)
@@ -23,7 +28,12 @@ class Broadcaster
23
28
  @publisher = establish_connection
24
29
  @subscriptions = {}
25
30
  @mutex = Mutex.new
26
- listen
31
+ @watchdog = Watchdog.new self, logger: logger,
32
+ interval: options.fetch(:watchdog_interval, Broadcaster.watchdog_interval),
33
+ timeout: options.fetch(:watchdog_timeout, Broadcaster.watchdog_timeout)
34
+
35
+ start_listener
36
+ watchdog.start
27
37
  end
28
38
 
29
39
  def publish(channel, message)
@@ -60,28 +70,57 @@ class Broadcaster
60
70
  end
61
71
  end
62
72
 
73
+ def restart_listener
74
+ logger.warn(logger_name) { 'Restarting listener' }
75
+
76
+ listener.kill if listener && listener != Thread.current
77
+ disconnect subscriber
78
+
79
+ start_listener
80
+ end
81
+
82
+ def shutdown
83
+ watchdog.stop
84
+ listener.kill
85
+
86
+ disconnect subscriber
87
+ disconnect publisher
88
+
89
+ logger.info(logger_name) { 'Shutdown' }
90
+ end
91
+
63
92
  private
64
93
 
65
- attr_reader :publisher, :subscriber, :subscriptions, :mutex, :logger, :logger_name, :redis_client, :redis_settings
94
+ attr_reader :publisher, :subscriber, :listener, :watchdog, :subscriptions, :mutex, :logger, :logger_name,
95
+ :redis_client, :redis_settings
66
96
 
67
97
  def establish_connection
68
98
  redis_client.new(redis_settings).tap { |redis| redis.call! 'PING' }
69
99
  end
70
100
 
101
+ def disconnect(connection)
102
+ connection.client.disconnect!
103
+ rescue => ex
104
+ logger.error(logger_name) { ex }
105
+ end
106
+
71
107
  def scoped(channel)
72
108
  "#{id}:#{channel}"
73
109
  end
74
110
 
75
- def listen
76
- subscriber = establish_connection
111
+ def start_listener
112
+ @subscriber = establish_connection
77
113
  subscriber.call! 'PSUBSCRIBE', scoped('*')
78
114
 
79
115
  logger.info(logger_name) { 'Listener started' }
80
116
 
81
- Thread.new do
117
+ @last_notification_at = Time.now
118
+
119
+ @listener = Thread.new do
82
120
  loop do
83
121
  begin
84
122
  notification = subscriber.client.read
123
+ @last_notification_at = Time.now
85
124
 
86
125
  channel = notification[2]
87
126
  message = Marshal.load notification[3]
@@ -109,15 +148,15 @@ class Broadcaster
109
148
  end
110
149
 
111
150
  logger.warn(logger_name) { 'Listener broken' }
112
- listen
151
+ restart_listener
113
152
  end
114
153
 
115
154
  rescue => ex
116
155
  logger.error(logger_name) { ex }
117
156
  sleep Broadcaster.reconnection_timeout
118
157
 
119
- logger.info(logger_name) { 'Listener reconnectig' }
158
+ logger.info(logger_name) { 'Listener reconnecting' }
120
159
  retry
121
160
  end
122
161
 
123
- end
162
+ end
@@ -2,8 +2,8 @@ require 'minitest_helper'
2
2
 
3
3
  describe Broadcaster do
4
4
 
5
- def wait_for(&block)
6
- Timeout.timeout(1) do
5
+ def wait_for(timeout=1, &block)
6
+ Timeout.timeout(timeout) do
7
7
  while !block.call
8
8
  sleep 0.001
9
9
  end
@@ -183,4 +183,55 @@ describe Broadcaster do
183
183
  received_messages.must_equal ['message 1', 'message 3']
184
184
  end
185
185
 
186
+ describe 'Watchdog' do
187
+
188
+ def with_blocked_listener(options={})
189
+ HangingRedis.reset
190
+
191
+ broadcaster = Broadcaster.new options.merge(redis_client: HangingRedis)
192
+ wait_for { HangingRedis.blocked_reads > 0 }
193
+ HangingRedis.hanging = false
194
+
195
+ yield broadcaster
196
+
197
+ ensure
198
+ broadcaster.shutdown if broadcaster
199
+ HangingRedis.reset
200
+ end
201
+
202
+ it 'Restarts a listener blocked on a silent connection' do
203
+ with_blocked_listener watchdog_interval: 0.05, watchdog_timeout: 0.2 do |broadcaster|
204
+ received_messages = []
205
+
206
+ broadcaster.subscribe 'channel_1' do |message|
207
+ received_messages << message
208
+ end
209
+
210
+ wait_for(10) do
211
+ broadcaster.publish 'channel_1', 'message 1'
212
+ sleep 0.1
213
+ received_messages.any?
214
+ end
215
+
216
+ received_messages.must_include 'message 1'
217
+ end
218
+ end
219
+
220
+ it 'Does not restart the listener before the timeout elapses' do
221
+ with_blocked_listener watchdog_interval: 0.05, watchdog_timeout: 60 do |broadcaster|
222
+ received_messages = []
223
+
224
+ broadcaster.subscribe 'channel_1' do |message|
225
+ received_messages << message
226
+ end
227
+
228
+ broadcaster.publish 'channel_1', 'message 1'
229
+ sleep 0.5
230
+
231
+ received_messages.must_be_empty
232
+ end
233
+ end
234
+
235
+ end
236
+
186
237
  end
@@ -1,13 +1,10 @@
1
1
  require 'simplecov'
2
- require 'coveralls'
3
2
 
4
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter]
5
3
  SimpleCov.start
6
4
 
7
5
  require 'broadcaster'
8
6
  require 'minitest/autorun'
9
7
  require 'minitest/colorin'
10
- require 'pry-nav'
11
8
  require 'timeout'
12
9
 
13
10
  Broadcaster.configure do |config|
@@ -61,4 +58,51 @@ class MockRedis
61
58
  @redis.client
62
59
  end
63
60
 
61
+ end
62
+
63
+ class HangingRedis
64
+
65
+ class << self
66
+
67
+ attr_writer :hanging
68
+ attr_accessor :blocked_reads
69
+
70
+ def hanging?
71
+ @hanging.nil? ? true : @hanging
72
+ end
73
+
74
+ def reset
75
+ @hanging = nil
76
+ @blocked_reads = 0
77
+ end
78
+
79
+ def blocked_reads
80
+ @blocked_reads ||= 0
81
+ end
82
+
83
+ end
84
+
85
+ def initialize(*args)
86
+ @redis = Redic.new(*args)
87
+ end
88
+
89
+ def call!(*args)
90
+ @redis.call!(*args)
91
+ end
92
+
93
+ def client
94
+ self
95
+ end
96
+
97
+ def read
98
+ return @redis.client.read unless self.class.hanging?
99
+
100
+ self.class.blocked_reads += 1
101
+ sleep
102
+ end
103
+
104
+ def disconnect!
105
+ @redis.client.disconnect!
106
+ end
107
+
64
108
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: broadcaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Naiman
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-26 00:00:00.000000000 Z
11
+ date: 2026-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redic
@@ -114,34 +114,6 @@ dependencies:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0.12'
117
- - !ruby/object:Gem::Dependency
118
- name: coveralls
119
- requirement: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '0.8'
124
- type: :development
125
- prerelease: false
126
- version_requirements: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: '0.8'
131
- - !ruby/object:Gem::Dependency
132
- name: pry-nav
133
- requirement: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '0.2'
138
- type: :development
139
- prerelease: false
140
- version_requirements: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - "~>"
143
- - !ruby/object:Gem::Version
144
- version: '0.2'
145
117
  description: Broadcasting based on Redis PubSub
146
118
  email:
147
119
  - gabynaiman@gmail.com
@@ -149,24 +121,25 @@ executables: []
149
121
  extensions: []
150
122
  extra_rdoc_files: []
151
123
  files:
152
- - ".coveralls.yml"
124
+ - ".github/workflows/ci.yml"
153
125
  - ".gitignore"
154
126
  - ".ruby-gemset"
155
127
  - ".ruby-version"
156
- - ".travis.yml"
157
128
  - Gemfile
158
129
  - LICENSE.txt
159
130
  - README.md
160
131
  - Rakefile
161
132
  - broadcaster.gemspec
162
133
  - lib/broadcaster.rb
134
+ - lib/broadcaster/version.rb
135
+ - lib/broadcaster/watchdog.rb
163
136
  - spec/broadcast_spec.rb
164
137
  - spec/minitest_helper.rb
165
138
  homepage: https://github.com/gabynaiman/broadcaster
166
139
  licenses:
167
140
  - MIT
168
141
  metadata: {}
169
- post_install_message:
142
+ post_install_message:
170
143
  rdoc_options: []
171
144
  require_paths:
172
145
  - lib
@@ -174,15 +147,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
147
  requirements:
175
148
  - - ">="
176
149
  - !ruby/object:Gem::Version
177
- version: '0'
150
+ version: 2.3.0
178
151
  required_rubygems_version: !ruby/object:Gem::Requirement
179
152
  requirements:
180
153
  - - ">="
181
154
  - !ruby/object:Gem::Version
182
155
  version: '0'
183
156
  requirements: []
184
- rubygems_version: 3.0.6
185
- signing_key:
157
+ rubygems_version: 3.0.9
158
+ signing_key:
186
159
  specification_version: 4
187
160
  summary: Broadcasting based on Redis PubSub
188
161
  test_files:
data/.coveralls.yml DELETED
@@ -1,2 +0,0 @@
1
- service_name: travis-ci
2
- repo_token: DIwiPcMHvmwcB0uOFrsaZ3ftClakHZSCM
data/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.0
5
- - 2.1
6
- - 2.2
7
- - 2.3
8
- - 2.4
9
- - 2.5
10
- - 2.6
11
- - 2.7
12
- - jruby-9.2.9.0
13
- - ruby-head
14
- - jruby-head
15
-
16
- matrix:
17
- fast_finish: true
18
- allow_failures:
19
- - rvm: ruby-head
20
- - rvm: jruby-head
21
-
22
- services:
23
- - redis-server