amqp-client 1.1.7 → 1.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/.github/workflows/codeql-analysis.yml +1 -1
- data/.github/workflows/docs.yml +1 -1
- data/.github/workflows/main.yml +41 -9
- data/.github/workflows/release.yml +29 -1
- data/.rubocop.yml +5 -3
- data/CHANGELOG.md +12 -1
- data/CODEOWNERS +1 -1
- data/README.md +63 -4
- data/Rakefile +173 -5
- data/amqp-client.gemspec +3 -3
- data/lib/amqp/client/channel.rb +3 -3
- data/lib/amqp/client/connection.rb +67 -14
- data/lib/amqp/client/exchange.rb +14 -10
- data/lib/amqp/client/frame_bytes.rb +10 -1
- data/lib/amqp/client/queue.rb +6 -2
- data/lib/amqp/client/version.rb +1 -1
- data/lib/amqp/client.rb +45 -2
- metadata +6 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cfb2dd354eeb33334cf6848b168f2a0caa486b40cefcce924910a16a77ffa4f
|
4
|
+
data.tar.gz: 7a7896809e27539ab3395b833038c5b328298c9cf6a25aeff7f706e05657e995
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8d0c1797b3b8a81fbe33a154bb255c452f09d925fdeafdfc03fdbb3342b116d7875d6166f7e9b948232f09d69c16179e0270c752b3afdd955d4ad6fca3b6cd1
|
7
|
+
data.tar.gz: 47c518dee777f61435e748045fb7cfebf808ac5ed9cd53813081b222a9abc81b82f85f436482b25dba1dcd10a96c1d1f9bbf4b5266fc90ece48307b61f03ccfb
|
data/.github/workflows/docs.yml
CHANGED
data/.github/workflows/main.yml
CHANGED
@@ -19,26 +19,34 @@ jobs:
|
|
19
19
|
matrix:
|
20
20
|
sudo: [true]
|
21
21
|
ruby:
|
22
|
-
- "2.6"
|
23
|
-
- "2.7"
|
24
|
-
- "3.0"
|
25
|
-
- "3.1"
|
26
22
|
- "3.2"
|
27
23
|
- "3.3"
|
24
|
+
- "3.4"
|
28
25
|
include:
|
29
|
-
- { ruby: jruby, allow-failure:
|
30
|
-
- { ruby: truffleruby, allow-failure:
|
26
|
+
- { ruby: jruby, allow-failure: false, sudo: false }
|
27
|
+
- { ruby: truffleruby, allow-failure: false, sudo: false }
|
31
28
|
steps:
|
29
|
+
- name: Configure dpkg to skip building man pages
|
30
|
+
run: |
|
31
|
+
echo 'path-exclude /usr/share/doc/*' | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
32
|
+
echo 'path-exclude /usr/share/man/*' | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
32
33
|
- name: Install RabbitMQ
|
33
34
|
run: sudo apt-get update && sudo apt-get install -y rabbitmq-server
|
34
35
|
- name: Verify RabbitMQ started correctly
|
35
36
|
run: while true; do sudo rabbitmq-diagnostics status 2>/dev/null && break; echo -n .; sleep 2; done
|
36
|
-
- uses: actions/checkout@
|
37
|
+
- uses: actions/checkout@v5
|
37
38
|
- uses: ruby/setup-ruby@v1
|
38
39
|
with:
|
39
40
|
bundler-cache: true
|
40
41
|
ruby-version: ${{ matrix.ruby }}
|
42
|
+
- name: Run tests (excluding TLS tests) (JRuby)
|
43
|
+
if: ${{ matrix.ruby == 'jruby' }}
|
44
|
+
continue-on-error: ${{ matrix.allow-failure || false }}
|
45
|
+
run: bundle exec rake
|
46
|
+
env:
|
47
|
+
JAVA_OPTS: "-Djava.net.preferIPv4Stack=true"
|
41
48
|
- name: Run tests (excluding TLS tests)
|
49
|
+
if: ${{ matrix.ruby != 'jruby' }}
|
42
50
|
continue-on-error: ${{ matrix.allow-failure || false }}
|
43
51
|
run: bundle exec rake
|
44
52
|
env:
|
@@ -55,6 +63,10 @@ jobs:
|
|
55
63
|
- "jruby"
|
56
64
|
- "truffleruby"
|
57
65
|
steps:
|
66
|
+
- name: Configure dpkg to skip building man pages
|
67
|
+
run: |
|
68
|
+
echo 'path-exclude /usr/share/doc/*' | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
69
|
+
echo 'path-exclude /usr/share/man/*' | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
58
70
|
- name: Install RabbitMQ
|
59
71
|
run: sudo apt-get update && sudo apt-get install -y rabbitmq-server
|
60
72
|
- name: Stop RabbitMQ
|
@@ -81,12 +93,20 @@ jobs:
|
|
81
93
|
run: sudo systemctl start rabbitmq-server
|
82
94
|
- name: Verify RabbitMQ started correctly
|
83
95
|
run: while true; do sudo rabbitmq-diagnostics status 2>/dev/null && break; echo -n .; sleep 2; done
|
84
|
-
- uses: actions/checkout@
|
96
|
+
- uses: actions/checkout@v5
|
85
97
|
- uses: ruby/setup-ruby@v1
|
86
98
|
with:
|
87
99
|
bundler-cache: true
|
88
100
|
ruby-version: ${{ matrix.ruby }}
|
101
|
+
- name: Run TLS tests (JRuby)
|
102
|
+
if: ${{ matrix.ruby == 'jruby' }}
|
103
|
+
run: bundle exec rake
|
104
|
+
env:
|
105
|
+
JAVA_OPTS: "-Djava.net.preferIPv4Stack=true"
|
106
|
+
TEST_AMQP_HOST: "localhost"
|
107
|
+
TESTOPTS: --name=/_tls$/
|
89
108
|
- name: Run TLS tests
|
109
|
+
if: ${{ matrix.ruby != 'jruby' }}
|
90
110
|
run: bundle exec rake
|
91
111
|
env:
|
92
112
|
TESTOPTS: --name=/_tls$/
|
@@ -104,7 +124,7 @@ jobs:
|
|
104
124
|
run: brew install rabbitmq
|
105
125
|
- name: Start RabbitMQ
|
106
126
|
run: brew services start rabbitmq
|
107
|
-
- uses: actions/checkout@
|
127
|
+
- uses: actions/checkout@v5
|
108
128
|
- uses: ruby/setup-ruby@v1
|
109
129
|
with:
|
110
130
|
bundler-cache: true
|
@@ -113,3 +133,15 @@ jobs:
|
|
113
133
|
run: while true; do rabbitmq-diagnostics status 2>/dev/null && break; echo -n .; sleep 2; done
|
114
134
|
- name: Run tests (excluding TLS tests)
|
115
135
|
run: bundle exec rake
|
136
|
+
|
137
|
+
lint:
|
138
|
+
runs-on: ubuntu-latest
|
139
|
+
timeout-minutes: 10
|
140
|
+
steps:
|
141
|
+
- uses: actions/checkout@v5
|
142
|
+
- uses: ruby/setup-ruby@v1
|
143
|
+
with:
|
144
|
+
bundler-cache: true
|
145
|
+
ruby-version: ruby
|
146
|
+
- name: Run RuboCop
|
147
|
+
run: bundle exec rake rubocop
|
@@ -12,8 +12,9 @@ jobs:
|
|
12
12
|
runs-on: ubuntu-latest
|
13
13
|
permissions:
|
14
14
|
id-token: write # for trusted publishing
|
15
|
+
contents: write # for creating releases
|
15
16
|
steps:
|
16
|
-
- uses: actions/checkout@
|
17
|
+
- uses: actions/checkout@v5
|
17
18
|
- uses: ruby/setup-ruby@v1
|
18
19
|
with:
|
19
20
|
bundler-cache: true
|
@@ -24,3 +25,30 @@ jobs:
|
|
24
25
|
- run: gem build *.gemspec
|
25
26
|
- run: gem install *.gem
|
26
27
|
- run: gem push *.gem
|
28
|
+
|
29
|
+
# create GitHub release
|
30
|
+
- name: Extract release notes
|
31
|
+
id: extract_release_notes
|
32
|
+
run: |
|
33
|
+
# Extract version from tag (remove 'v' prefix)
|
34
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
35
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
36
|
+
|
37
|
+
# Extract changelog section for this version with better handling
|
38
|
+
if grep -q "## \[$VERSION\]" CHANGELOG.md; then
|
39
|
+
awk "/^## \[$VERSION\]/ {flag=1; next} /^## \[/ && flag {exit} flag && /\S/ {print}" CHANGELOG.md > release_notes.md
|
40
|
+
echo "Release notes extracted for version $VERSION:"
|
41
|
+
cat release_notes.md
|
42
|
+
else
|
43
|
+
echo "No changelog entry found for version $VERSION" > release_notes.md
|
44
|
+
echo "Warning: No changelog entry found for version $VERSION"
|
45
|
+
fi
|
46
|
+
|
47
|
+
- name: Create GitHub Release
|
48
|
+
uses: softprops/action-gh-release@v2
|
49
|
+
with:
|
50
|
+
name: Release ${{ steps.extract_release_notes.outputs.version }}
|
51
|
+
body_path: release_notes.md
|
52
|
+
draft: false
|
53
|
+
prerelease: false
|
54
|
+
files: '*.gem'
|
data/.rubocop.yml
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
2
|
+
plugins:
|
3
|
+
- rubocop-minitest
|
2
4
|
|
3
5
|
AllCops:
|
4
6
|
NewCops: disable
|
5
|
-
TargetRubyVersion: 2
|
7
|
+
TargetRubyVersion: 3.2
|
6
8
|
SuggestExtensions: false
|
7
9
|
|
8
10
|
Style/StringLiterals:
|
@@ -18,11 +20,11 @@ Layout/LineLength:
|
|
18
20
|
|
19
21
|
Naming/FileName:
|
20
22
|
Exclude:
|
21
|
-
-
|
23
|
+
- "lib/amqp-client.rb"
|
22
24
|
|
23
25
|
Metrics/PerceivedComplexity:
|
24
26
|
Exclude:
|
25
|
-
-
|
27
|
+
- "lib/amqp/client/properties.rb"
|
26
28
|
|
27
29
|
Metrics/ParameterLists:
|
28
30
|
Max: 8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.2.1] - 2025-09-15
|
4
|
+
|
5
|
+
- Added: Convenience methods for creating exchange types: `fanout()`, `direct()`, `topic()`, and `headers()`
|
6
|
+
- Added: Support for binding with high level objects (Exchange and Queue objects can now be passed as binding sources)
|
7
|
+
- Fixed: Bug where a client without any connection could not be closed properly
|
8
|
+
|
9
|
+
## [1.2.0] - 2025-09-10
|
10
|
+
|
11
|
+
- Fixed: `Connection#channel` wasn't thread-safe
|
12
|
+
- Added: Support for heartbeats
|
13
|
+
|
3
14
|
## [1.1.7] - 2024-05-12
|
4
15
|
|
5
16
|
- Support for Connection.update-secret
|
@@ -48,7 +59,7 @@
|
|
48
59
|
|
49
60
|
## [1.0.1] - 2021-09-06
|
50
61
|
|
51
|
-
- The API is fully documented! https://cloudamqp.github.io/amqp-client.rb
|
62
|
+
- The API is fully documented! <https://cloudamqp.github.io/amqp-client.rb/>
|
52
63
|
- Fixed: Socket writing is now thread-safe
|
53
64
|
- Change: Block while waiting for basic_cancel by default
|
54
65
|
- Added: Can specify channel_max, heartbeat and frame_max as options to the Client/Connection
|
data/CODEOWNERS
CHANGED
@@ -1 +1 @@
|
|
1
|
-
* @
|
1
|
+
* @carlhoerberg @spuun @dentarg @baelter @walro
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ It's safe by default, messages are published as persistent, and is waiting for c
|
|
8
8
|
|
9
9
|
## Support
|
10
10
|
|
11
|
-
The library is fully supported by [CloudAMQP](https://www.cloudamqp.com), the largest RabbitMQ hosting provider in the world. Open [an issue](https://github.com/cloudamqp/amqp-client.rb/issues) or [email our support](mailto:support@cloudamqp.com) if you have problems or questions.
|
11
|
+
The library is fully supported by [CloudAMQP](https://www.cloudamqp.com), the largest LavinMQ and RabbitMQ hosting provider in the world. Open [an issue](https://github.com/cloudamqp/amqp-client.rb/issues) or [email our support](mailto:support@cloudamqp.com) if you have problems or questions.
|
12
12
|
|
13
13
|
## Documentation
|
14
14
|
|
@@ -113,17 +113,76 @@ gem 'amqp-client'
|
|
113
113
|
|
114
114
|
And then execute:
|
115
115
|
|
116
|
-
|
116
|
+
bundle install
|
117
117
|
|
118
118
|
Or install it yourself as:
|
119
119
|
|
120
|
-
|
120
|
+
gem install amqp-client
|
121
121
|
|
122
122
|
## Development
|
123
123
|
|
124
124
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
125
125
|
|
126
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
126
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
127
|
+
|
128
|
+
### Release Process
|
129
|
+
|
130
|
+
The gem uses rake tasks to automate the release preparation process. The actual gem building and publishing is handled automatically by GitHub Actions when a tag is pushed.
|
131
|
+
|
132
|
+
#### Quick Release (Patch Version)
|
133
|
+
|
134
|
+
```bash
|
135
|
+
rake release:prepare
|
136
|
+
```
|
137
|
+
|
138
|
+
This will:
|
139
|
+
|
140
|
+
1. Run tests and RuboCop to ensure code quality
|
141
|
+
2. Bump the patch version (e.g., 1.2.0 → 1.2.1)
|
142
|
+
3. Update the CHANGELOG.md with the new version and current date
|
143
|
+
4. Create a git commit and tag for the release
|
144
|
+
5. Push commits and tags to the remote repository
|
145
|
+
6. GitHub Actions will automatically build and publish the gem to RubyGems
|
146
|
+
|
147
|
+
#### Custom Version Bump
|
148
|
+
|
149
|
+
For minor or major version bumps:
|
150
|
+
|
151
|
+
```bash
|
152
|
+
# Minor version bump (e.g., 1.2.0 → 1.3.0)
|
153
|
+
rake release:prepare[minor]
|
154
|
+
|
155
|
+
# Major version bump (e.g., 1.2.0 → 2.0.0)
|
156
|
+
rake release:prepare[major]
|
157
|
+
```
|
158
|
+
|
159
|
+
#### Individual Release Steps
|
160
|
+
|
161
|
+
You can also run individual steps if needed:
|
162
|
+
|
163
|
+
```bash
|
164
|
+
# Bump version only
|
165
|
+
rake release:bump[patch] # or [minor] or [major]
|
166
|
+
|
167
|
+
# Update changelog with current version
|
168
|
+
rake release:changelog
|
169
|
+
|
170
|
+
# Create git tag with changelog entries
|
171
|
+
rake release:tag
|
172
|
+
|
173
|
+
# Push tag to remote (handles conflicts)
|
174
|
+
rake release:push_tag
|
175
|
+
```
|
176
|
+
|
177
|
+
#### Manual Release Steps
|
178
|
+
|
179
|
+
If you prefer manual control:
|
180
|
+
|
181
|
+
1. Update the version number in `lib/amqp/client/version.rb`
|
182
|
+
2. Update the CHANGELOG.md with the new version and release notes
|
183
|
+
3. Commit your changes: `git add . && git commit -m "Release X.Y.Z"`
|
184
|
+
4. Create and push a tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
185
|
+
5. GitHub Actions will automatically build and publish the gem when the tag is pushed
|
127
186
|
|
128
187
|
## Contributing
|
129
188
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "bundler/gem_tasks"
|
4
3
|
require "rake/testtask"
|
5
4
|
|
6
5
|
Rake::TestTask.new(:test) do |t|
|
@@ -18,12 +17,181 @@ end
|
|
18
17
|
|
19
18
|
require "rubocop/rake_task"
|
20
19
|
|
21
|
-
RuboCop::RakeTask.new
|
22
|
-
task.requires << "rubocop-minitest"
|
23
|
-
end
|
20
|
+
RuboCop::RakeTask.new
|
24
21
|
|
25
22
|
require "yard"
|
26
23
|
|
27
24
|
YARD::Rake::YardocTask.new
|
28
25
|
|
29
|
-
|
26
|
+
# Release helper methods
|
27
|
+
def current_version
|
28
|
+
version_file = "lib/amqp/client/version.rb"
|
29
|
+
content = File.read(version_file)
|
30
|
+
content.match(/VERSION = "(.+)"/)[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_changelog_for_version(version)
|
34
|
+
changelog = File.read("CHANGELOG.md")
|
35
|
+
|
36
|
+
# Find the section for this version
|
37
|
+
version_pattern = /^## \[#{Regexp.escape(version)}\][^\n]*\n(.*?)(?=^## \[|\z)/m
|
38
|
+
match = changelog.match(version_pattern)
|
39
|
+
|
40
|
+
if match
|
41
|
+
# Clean up the changelog entries
|
42
|
+
entries = match[1].strip
|
43
|
+
# Remove empty lines at the start and end
|
44
|
+
entries.gsub(/\A\s*\n+/, "").gsub(/\n+\s*\z/, "")
|
45
|
+
else
|
46
|
+
"No changelog entries found for version #{version}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def push_tag_to_remote(version)
|
51
|
+
# Check if tag exists on remote
|
52
|
+
remote_tag_exists = system("git ls-remote --tags origin | grep -q refs/tags/v#{version}")
|
53
|
+
|
54
|
+
if remote_tag_exists
|
55
|
+
puts "Tag v#{version} already exists on remote. Force pushing updated tag..."
|
56
|
+
system("git push origin v#{version} --force")
|
57
|
+
else
|
58
|
+
puts "Pushing new tag v#{version} to remote..."
|
59
|
+
system("git push origin v#{version}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def bump_version(version_type)
|
64
|
+
unless %w[major minor patch].include?(version_type)
|
65
|
+
puts "Invalid version type. Use: major, minor, or patch"
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
version_file = "lib/amqp/client/version.rb"
|
70
|
+
content = File.read(version_file)
|
71
|
+
|
72
|
+
current_version = content.match(/VERSION = "(.+)"/)[1]
|
73
|
+
major, minor, patch = current_version.split(".").map(&:to_i)
|
74
|
+
|
75
|
+
case version_type
|
76
|
+
when "major"
|
77
|
+
major += 1
|
78
|
+
minor = 0
|
79
|
+
patch = 0
|
80
|
+
when "minor"
|
81
|
+
minor += 1
|
82
|
+
patch = 0
|
83
|
+
when "patch"
|
84
|
+
patch += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
new_version = "#{major}.#{minor}.#{patch}"
|
88
|
+
new_content = content.gsub(/VERSION = ".+"/, %(VERSION = "#{new_version}"))
|
89
|
+
|
90
|
+
File.write(version_file, new_content)
|
91
|
+
puts "Bumped version from #{current_version} to #{new_version}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def update_changelog
|
95
|
+
version = current_version
|
96
|
+
date = Time.now.strftime("%Y-%m-%d")
|
97
|
+
|
98
|
+
changelog = File.read("CHANGELOG.md")
|
99
|
+
|
100
|
+
if changelog.include?("## [#{version}]")
|
101
|
+
puts "Version #{version} already exists in CHANGELOG.md"
|
102
|
+
else
|
103
|
+
updated_changelog = changelog.sub(
|
104
|
+
"## [Unreleased]",
|
105
|
+
"## [Unreleased]\n\n## [#{version}] - #{date}"
|
106
|
+
)
|
107
|
+
|
108
|
+
File.write("CHANGELOG.md", updated_changelog)
|
109
|
+
puts "Updated CHANGELOG.md with version #{version}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_git_tag
|
114
|
+
version = current_version
|
115
|
+
|
116
|
+
system("git add .")
|
117
|
+
system("git commit -m 'Release #{version}'")
|
118
|
+
|
119
|
+
# Check if tag already exists locally and remove it if it does
|
120
|
+
if system("git tag -l v#{version} | grep -q v#{version}")
|
121
|
+
puts "Tag v#{version} already exists locally, removing it..."
|
122
|
+
system("git tag -d v#{version}")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Extract changelog entries for this version
|
126
|
+
changelog_entries = extract_changelog_for_version(version)
|
127
|
+
|
128
|
+
# Create tag message with version and changelog
|
129
|
+
tag_message = "Release #{version}\n\n#{changelog_entries}"
|
130
|
+
|
131
|
+
# Create annotated tag with the changelog
|
132
|
+
system("git", "tag", "-a", "v#{version}", "-m", tag_message)
|
133
|
+
|
134
|
+
puts "Created git tag v#{version} with changelog entries"
|
135
|
+
end
|
136
|
+
|
137
|
+
def prepare_release_process(version_type)
|
138
|
+
puts "Preparing release process..."
|
139
|
+
|
140
|
+
# Ensure working directory is clean
|
141
|
+
unless system("git diff --quiet && git diff --cached --quiet")
|
142
|
+
puts "Working directory is not clean. Please commit or stash changes first."
|
143
|
+
exit 1
|
144
|
+
end
|
145
|
+
|
146
|
+
# Bump version
|
147
|
+
Rake::Task["release:bump"].invoke(version_type)
|
148
|
+
Rake::Task["release:bump"].reenable
|
149
|
+
|
150
|
+
# Update changelog
|
151
|
+
Rake::Task["release:changelog"].invoke
|
152
|
+
Rake::Task["release:changelog"].reenable
|
153
|
+
|
154
|
+
# Create tag and push
|
155
|
+
Rake::Task["release:tag"].invoke
|
156
|
+
Rake::Task["release:tag"].reenable
|
157
|
+
|
158
|
+
# Push to git
|
159
|
+
system("git push origin")
|
160
|
+
|
161
|
+
# Handle tag push with potential conflicts
|
162
|
+
version = current_version
|
163
|
+
push_tag_to_remote(version)
|
164
|
+
|
165
|
+
puts "Successfully prepared release #{version}!"
|
166
|
+
puts "The CI will automatically build and publish the gem when the tag is pushed."
|
167
|
+
end
|
168
|
+
|
169
|
+
namespace :release do
|
170
|
+
desc "Bump version (usage: rake release:bump[major|minor|patch])"
|
171
|
+
task :bump, [:type] do |_t, args|
|
172
|
+
bump_version(args[:type] || "patch")
|
173
|
+
end
|
174
|
+
|
175
|
+
desc "Update changelog with current version"
|
176
|
+
task :changelog do
|
177
|
+
update_changelog
|
178
|
+
end
|
179
|
+
|
180
|
+
desc "Create git tag for current version"
|
181
|
+
task :tag do
|
182
|
+
create_git_tag
|
183
|
+
end
|
184
|
+
|
185
|
+
desc "Push tag to remote (handles conflicts)"
|
186
|
+
task :push_tag do
|
187
|
+
version = current_version
|
188
|
+
push_tag_to_remote(version)
|
189
|
+
end
|
190
|
+
|
191
|
+
desc "Prepare release (bump version, update changelog, create tag, push to git)"
|
192
|
+
task :prepare, [:type] => %i[test rubocop] do |_t, args|
|
193
|
+
prepare_release_process(args[:type] || "patch")
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
task default: [:test, *(:rubocop if ENV["CI"] != "true")]
|
data/amqp-client.gemspec
CHANGED
@@ -5,14 +5,14 @@ require_relative "lib/amqp/client/version"
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "amqp-client"
|
7
7
|
spec.version = AMQP::Client::VERSION
|
8
|
-
spec.authors = ["
|
9
|
-
spec.email = ["
|
8
|
+
spec.authors = ["CloudAMQP"]
|
9
|
+
spec.email = ["team@cloudamqp.com"]
|
10
10
|
|
11
11
|
spec.summary = "AMQP 0-9-1 client"
|
12
12
|
spec.description = "Modern AMQP 0-9-1 Ruby client"
|
13
13
|
spec.homepage = "https://github.com/cloudamqp/amqp-client.rb"
|
14
14
|
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
18
|
spec.metadata["source_code_uri"] = "#{spec.homepage}.git"
|
data/lib/amqp/client/channel.rb
CHANGED
@@ -251,7 +251,7 @@ module AMQP
|
|
251
251
|
# @option properties [Hash<String, Object>] headers Custom headers
|
252
252
|
# @option properties [Integer] delivery_mode 2 for persisted message, transient messages for all other values
|
253
253
|
# @option properties [Integer] priority A priority of the message (between 0 and 255)
|
254
|
-
# @option properties [
|
254
|
+
# @option properties [String] correlation_id A correlation id, most often used used for RPC communication
|
255
255
|
# @option properties [String] reply_to Queue to reply RPC responses to
|
256
256
|
# @option properties [Integer, String] expiration Number of seconds the message will stay in the queue
|
257
257
|
# @option properties [String] message_id Can be used to uniquely identify the message, e.g. for deduplication
|
@@ -345,8 +345,8 @@ module AMQP
|
|
345
345
|
end
|
346
346
|
|
347
347
|
# Specify how many messages to prefetch for consumers with `no_ack: false`
|
348
|
-
# @param prefetch_count [Integer] Number of messages to
|
349
|
-
# @param prefetch_size [Integer] Number of bytes to
|
348
|
+
# @param prefetch_count [Integer] Number of messages to maximum keep in flight
|
349
|
+
# @param prefetch_size [Integer] Number of bytes to maximum keep in flight
|
350
350
|
# @param global [Boolean] If true the limit will apply to channel rather than the consumer
|
351
351
|
# @return [nil]
|
352
352
|
def basic_qos(prefetch_count, prefetch_size: 0, global: false)
|
@@ -44,6 +44,7 @@ module AMQP
|
|
44
44
|
@frame_max = frame_max
|
45
45
|
@heartbeat = heartbeat
|
46
46
|
@channels = {}
|
47
|
+
@channels_lock = Mutex.new
|
47
48
|
@closed = nil
|
48
49
|
@replies = ::Queue.new
|
49
50
|
@write_lock = Mutex.new
|
@@ -51,6 +52,9 @@ module AMQP
|
|
51
52
|
@on_blocked = ->(reason) { warn "AMQP-Client blocked by broker: #{reason}" }
|
52
53
|
@on_unblocked = -> { warn "AMQP-Client unblocked by broker" }
|
53
54
|
|
55
|
+
# Only used with heartbeats
|
56
|
+
@last_activity_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
57
|
+
|
54
58
|
Thread.new { read_loop } if read_loop_thread
|
55
59
|
end
|
56
60
|
|
@@ -89,15 +93,17 @@ module AMQP
|
|
89
93
|
raise ArgumentError, "Channel ID cannot be 0" if id&.zero?
|
90
94
|
raise ArgumentError, "Channel ID higher than connection's channel max #{@channel_max}" if id && id > @channel_max
|
91
95
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
ch = @channels_lock.synchronize do
|
97
|
+
if id
|
98
|
+
@channels[id] ||= Channel.new(self, id)
|
99
|
+
else
|
100
|
+
1.upto(@channel_max) do |i|
|
101
|
+
break id = i unless @channels.key? i
|
102
|
+
end
|
103
|
+
raise Error, "Max channels reached" if id.nil?
|
99
104
|
|
100
|
-
|
105
|
+
@channels[id] = Channel.new(self, id)
|
106
|
+
end
|
101
107
|
end
|
102
108
|
ch.open
|
103
109
|
end
|
@@ -175,6 +181,7 @@ module AMQP
|
|
175
181
|
def write_bytes(*bytes)
|
176
182
|
@write_lock.synchronize do
|
177
183
|
@socket.write(*bytes)
|
184
|
+
update_last_activity
|
178
185
|
end
|
179
186
|
rescue *READ_EXCEPTIONS => e
|
180
187
|
raise Error::ConnectionClosed.new(*@closed) if @closed
|
@@ -206,6 +213,7 @@ module AMQP
|
|
206
213
|
|
207
214
|
# parse the frame, will return false if a close frame was received
|
208
215
|
parse_frame(type, channel_id, frame_buffer) || return
|
216
|
+
update_last_activity
|
209
217
|
end
|
210
218
|
nil
|
211
219
|
rescue *READ_EXCEPTIONS => e
|
@@ -232,7 +240,7 @@ module AMQP
|
|
232
240
|
READ_EXCEPTIONS = [IOError, OpenSSL::OpenSSLError, SystemCallError,
|
233
241
|
RUBY_ENGINE == "jruby" ? java.lang.NullPointerException : nil].compact.freeze
|
234
242
|
|
235
|
-
def parse_frame(type, channel_id, buf) # rubocop:disable Metrics/MethodLength
|
243
|
+
def parse_frame(type, channel_id, buf) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
236
244
|
channel = @channels[channel_id]
|
237
245
|
case type
|
238
246
|
when 1 # method frame
|
@@ -277,11 +285,11 @@ module AMQP
|
|
277
285
|
reply_code, reply_text_len = buf.unpack("@4 S> C")
|
278
286
|
reply_text = buf.byteslice(7, reply_text_len).force_encoding("utf-8")
|
279
287
|
classid, methodid = buf.byteslice(7 + reply_text_len, 4).unpack("S> S>")
|
280
|
-
channel = @channels.delete(channel_id)
|
288
|
+
channel = @channels_lock.synchronize { @channels.delete(channel_id) }
|
281
289
|
channel.closed!(:channel, reply_code, reply_text, classid, methodid)
|
282
290
|
write_bytes FrameBytes.channel_close_ok(channel_id)
|
283
291
|
when 41 # channel#close-ok
|
284
|
-
channel = @channels.delete(channel_id)
|
292
|
+
channel = @channels_lock.synchronize { @channels.delete(channel_id) }
|
285
293
|
channel.reply [:channel_close_ok]
|
286
294
|
else raise Error::UnsupportedMethodFrame, class_id, method_id
|
287
295
|
end
|
@@ -406,17 +414,61 @@ module AMQP
|
|
406
414
|
channel.header_delivered body_size, properties
|
407
415
|
when 3 # body
|
408
416
|
channel.body_delivered buf
|
417
|
+
when 8 # heartbeat
|
418
|
+
handle_server_heartbeat(channel_id)
|
409
419
|
else raise Error::UnsupportedFrameType, type
|
410
420
|
end
|
411
421
|
true
|
412
422
|
end
|
413
423
|
|
424
|
+
def update_last_activity
|
425
|
+
return unless @heartbeat&.positive?
|
426
|
+
|
427
|
+
@last_activity_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
428
|
+
end
|
429
|
+
|
430
|
+
def handle_server_heartbeat(channel_id)
|
431
|
+
return if channel_id.zero?
|
432
|
+
|
433
|
+
raise Error::ConnectionClosed.new(501, "Heartbeat frame received on non-zero channel #{channel_id}")
|
434
|
+
end
|
435
|
+
|
436
|
+
# Start the heartbeat background thread (called from connection#tune)
|
437
|
+
def start_heartbeats(period)
|
438
|
+
Thread.new do
|
439
|
+
Thread.current.abort_on_exception = true # Raising an unhandled exception is a bug
|
440
|
+
interval = period / 2.0
|
441
|
+
loop do
|
442
|
+
sleep interval
|
443
|
+
break if @closed
|
444
|
+
next if @socket.nil?
|
445
|
+
|
446
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
447
|
+
# If we haven't sent anything recently, send a heartbeat
|
448
|
+
next unless now - @last_activity_time >= interval
|
449
|
+
|
450
|
+
begin
|
451
|
+
send_heartbeat
|
452
|
+
rescue Error => e
|
453
|
+
warn "AMQP-Client heartbeat send failed: #{e.inspect}"
|
454
|
+
break
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def send_heartbeat
|
461
|
+
write_bytes FrameBytes.heartbeat
|
462
|
+
end
|
463
|
+
|
414
464
|
def expect(expected_frame_type)
|
415
465
|
frame_type, args = @replies.pop
|
416
466
|
if frame_type.nil?
|
417
467
|
return if expected_frame_type == :close_ok
|
418
468
|
|
419
|
-
raise
|
469
|
+
raise Error::ConnectionClosed.new(*@closed) if @closed
|
470
|
+
|
471
|
+
raise Error, "Connection closed while waiting for #{expected_frame_type}"
|
420
472
|
end
|
421
473
|
frame_type == expected_frame_type || raise(Error::UnexpectedFrame.new(expected_frame_type, frame_type))
|
422
474
|
args
|
@@ -454,7 +506,7 @@ module AMQP
|
|
454
506
|
channel_max, frame_max, heartbeat = nil
|
455
507
|
socket.write "AMQP\x00\x00\x09\x01"
|
456
508
|
buf = String.new(capacity: 4096)
|
457
|
-
loop do
|
509
|
+
loop do # rubocop:disable Metrics/BlockLength
|
458
510
|
begin
|
459
511
|
socket.readpartial(4096, buf)
|
460
512
|
rescue *READ_EXCEPTIONS => e
|
@@ -483,6 +535,7 @@ module AMQP
|
|
483
535
|
channel_max = [channel_max, options.fetch(:channel_max, 2048).to_i].min
|
484
536
|
frame_max = [frame_max, options.fetch(:frame_max, 131_072).to_i].min
|
485
537
|
heartbeat = [heartbeat, options.fetch(:heartbeat, 0).to_i].min
|
538
|
+
start_heartbeats(heartbeat) if heartbeat.positive?
|
486
539
|
socket.write FrameBytes.connection_tune_ok(channel_max, frame_max, heartbeat)
|
487
540
|
socket.write FrameBytes.connection_open(vhost)
|
488
541
|
when 41 # connection#open-ok
|
@@ -508,7 +561,7 @@ module AMQP
|
|
508
561
|
raise e
|
509
562
|
end
|
510
563
|
|
511
|
-
# Enable TCP keepalive, which is
|
564
|
+
# Enable TCP keepalive, which is preferred to heartbeats
|
512
565
|
# @return [void]
|
513
566
|
def enable_tcp_keepalive(socket, idle = 60, interval = 10, count = 3)
|
514
567
|
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
data/lib/amqp/client/exchange.rb
CHANGED
@@ -4,6 +4,8 @@ module AMQP
|
|
4
4
|
class Client
|
5
5
|
# High level representation of an exchange
|
6
6
|
class Exchange
|
7
|
+
attr_reader :name
|
8
|
+
|
7
9
|
# Should only be initialized from the Client
|
8
10
|
# @api private
|
9
11
|
def initialize(client, name)
|
@@ -14,7 +16,7 @@ module AMQP
|
|
14
16
|
# Publish to the exchange
|
15
17
|
# @param body [String] The message body
|
16
18
|
# @param routing_key [String] The routing key of the message,
|
17
|
-
# the exchange may use this when routing the message to bound queues
|
19
|
+
# the exchange may use this when routing the message to bound queues (defaults to empty string)
|
18
20
|
# @param properties [Properties]
|
19
21
|
# @option properties [String] content_type Content type of the message body
|
20
22
|
# @option properties [String] content_encoding Content encoding of the body
|
@@ -30,28 +32,30 @@ module AMQP
|
|
30
32
|
# @option properties [String] user_id Can be used to verify that this is the user that published the message
|
31
33
|
# @option properties [String] app_id Can be used to indicates which app that generated the message
|
32
34
|
# @return [Exchange] self
|
33
|
-
def publish(body, routing_key, **properties)
|
35
|
+
def publish(body, routing_key = "", **properties)
|
34
36
|
@client.publish(body, @name, routing_key, **properties)
|
35
37
|
self
|
36
38
|
end
|
37
39
|
|
38
40
|
# Bind to another exchange
|
39
|
-
# @param
|
40
|
-
# @param binding_key [String] Binding key on which messages that match might be routed (
|
41
|
+
# @param source [String, Exchange] Name of the exchange to bind to, or the exchange object itself
|
42
|
+
# @param binding_key [String] Binding key on which messages that match might be routed (defaults to empty string)
|
41
43
|
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
42
44
|
# @return [Exchange] self
|
43
|
-
def bind(
|
44
|
-
|
45
|
+
def bind(source, binding_key = "", arguments: {})
|
46
|
+
source = source.is_a?(String) ? source : source.name
|
47
|
+
@client.exchange_bind(@name, source, binding_key, arguments: arguments)
|
45
48
|
self
|
46
49
|
end
|
47
50
|
|
48
51
|
# Unbind from another exchange
|
49
|
-
# @param
|
50
|
-
# @param binding_key [String] Binding key which the queue is bound to the exchange with
|
52
|
+
# @param source [String, Exchange] Name of the exchange to unbind from, or the exchange object itself
|
53
|
+
# @param binding_key [String] Binding key which the queue is bound to the exchange with (defaults to empty string)
|
51
54
|
# @param arguments [Hash] Arguments matching the binding that's being removed
|
52
55
|
# @return [Exchange] self
|
53
|
-
def unbind(
|
54
|
-
|
56
|
+
def unbind(source, binding_key = "", arguments: {})
|
57
|
+
source = source.is_a?(String) ? source : source.name
|
58
|
+
@client.exchange_unbind(@name, source, binding_key, arguments: arguments)
|
55
59
|
self
|
56
60
|
end
|
57
61
|
|
@@ -9,7 +9,7 @@ module AMQP
|
|
9
9
|
# Each frame type implemented as a method
|
10
10
|
# Having a class for each frame type is more expensive in terms of CPU and memory
|
11
11
|
# @api private
|
12
|
-
module FrameBytes
|
12
|
+
module FrameBytes # rubocop:disable Metrics/ModuleLength
|
13
13
|
def self.connection_start_ok(response, properties)
|
14
14
|
prop_tbl = Table.encode(properties)
|
15
15
|
[
|
@@ -542,6 +542,15 @@ module AMQP
|
|
542
542
|
206 # frame end
|
543
543
|
].pack("C S> L> S> S> C")
|
544
544
|
end
|
545
|
+
|
546
|
+
def self.heartbeat
|
547
|
+
[
|
548
|
+
8, # type: heartbeat
|
549
|
+
0, # channel id
|
550
|
+
0, # frame size
|
551
|
+
206 # frame end
|
552
|
+
].pack("C S> L> C")
|
553
|
+
end
|
545
554
|
end
|
546
555
|
end
|
547
556
|
end
|
data/lib/amqp/client/queue.rb
CHANGED
@@ -4,6 +4,8 @@ module AMQP
|
|
4
4
|
class Client
|
5
5
|
# Queue abstraction
|
6
6
|
class Queue
|
7
|
+
attr_reader :name
|
8
|
+
|
7
9
|
# Should only be initialized from the Client
|
8
10
|
# @api private
|
9
11
|
def initialize(client, name)
|
@@ -35,21 +37,23 @@ module AMQP
|
|
35
37
|
end
|
36
38
|
|
37
39
|
# Bind the queue to an exchange
|
38
|
-
# @param exchange [String] Name of the exchange to bind to
|
40
|
+
# @param exchange [String, Exchange] Name of the exchange to bind to, or the exchange object itself
|
39
41
|
# @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
|
40
42
|
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
41
43
|
# @return [self]
|
42
44
|
def bind(exchange, binding_key, arguments: {})
|
45
|
+
exchange = exchange.is_a?(String) ? exchange : exchange.name
|
43
46
|
@client.bind(@name, exchange, binding_key, arguments: arguments)
|
44
47
|
self
|
45
48
|
end
|
46
49
|
|
47
50
|
# Unbind the queue from an exchange
|
48
|
-
# @param exchange [String] Name of the exchange to unbind from
|
51
|
+
# @param exchange [String, Exchange] Name of the exchange to unbind from, or the exchange object itself
|
49
52
|
# @param binding_key [String] Binding key which the queue is bound to the exchange with
|
50
53
|
# @param arguments [Hash] Arguments matching the binding that's being removed
|
51
54
|
# @return [self]
|
52
55
|
def unbind(exchange, binding_key, arguments: {})
|
56
|
+
exchange = exchange.is_a?(String) ? exchange : exchange.name
|
53
57
|
@client.unbind(@name, exchange, binding_key, arguments: arguments)
|
54
58
|
self
|
55
59
|
end
|
data/lib/amqp/client/version.rb
CHANGED
data/lib/amqp/client.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
3
|
require_relative "client/version"
|
5
4
|
require_relative "client/connection"
|
6
5
|
require_relative "client/exchange"
|
@@ -52,7 +51,7 @@ module AMQP
|
|
52
51
|
def start
|
53
52
|
@stopped = false
|
54
53
|
Thread.new(connect(read_loop_thread: false)) do |conn|
|
55
|
-
Thread.abort_on_exception = true # Raising an unhandled exception is a bug
|
54
|
+
Thread.current.abort_on_exception = true # Raising an unhandled exception is a bug
|
56
55
|
loop do
|
57
56
|
break if @stopped
|
58
57
|
|
@@ -84,6 +83,8 @@ module AMQP
|
|
84
83
|
return if @stopped
|
85
84
|
|
86
85
|
@stopped = true
|
86
|
+
return unless @connq.size.positive?
|
87
|
+
|
87
88
|
conn = @connq.pop
|
88
89
|
conn.close
|
89
90
|
nil
|
@@ -116,6 +117,12 @@ module AMQP
|
|
116
117
|
end
|
117
118
|
|
118
119
|
# Declare an exchange and return a high level Exchange object
|
120
|
+
# @param name [String] Name of the exchange
|
121
|
+
# @param type [String] Type of the exchange, one of "direct", "fanout", "topic", "headers" or custom exchange type
|
122
|
+
# @param durable [Boolean] If true the exchange will survive broker restarts
|
123
|
+
# @param auto_delete [Boolean] If true the exchange will be deleted when the last queue is unbound
|
124
|
+
# @param internal [Boolean] If true the exchange will not accept directly published messages
|
125
|
+
# @param arguments [Hash] Custom arguments such as alternate-exchange etc.
|
119
126
|
# @return [Exchange]
|
120
127
|
# @example
|
121
128
|
# amqp = AMQP::Client.new.start
|
@@ -131,6 +138,42 @@ module AMQP
|
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
141
|
+
# Declare a fanout exchange and return a high level Exchange object
|
142
|
+
# @param name [String] Name of the exchange (defaults to "amq.fanout")
|
143
|
+
# @see {#exchange} for other parameters
|
144
|
+
# @return [Exchange]
|
145
|
+
def fanout(name = "amq.fanout", **kwargs)
|
146
|
+
exchange(name, "fanout", **kwargs)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Declare a direct exchange and return a high level Exchange object
|
150
|
+
# @param name [String] Name of the exchange (defaults to "" for the default direct exchange)
|
151
|
+
# @see {#exchange} for other parameters
|
152
|
+
# @return [Exchange]
|
153
|
+
def direct(name = "", **kwargs)
|
154
|
+
return exchange(name, "direct", **kwargs) unless name.empty?
|
155
|
+
|
156
|
+
@exchanges.fetch(name) do
|
157
|
+
@exchanges[name] = Exchange.new(self, name)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Declare a topic exchange and return a high level Exchange object
|
162
|
+
# @param name [String] Name of the exchange (defaults to "amq.topic")
|
163
|
+
# @see {#exchange} for other parameters
|
164
|
+
# @return [Exchange]
|
165
|
+
def topic(name = "amq.topic", **kwargs)
|
166
|
+
exchange(name, "topic", **kwargs)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Declare a headers exchange and return a high level Exchange object
|
170
|
+
# @param name [String] Name of the exchange (defaults to "amq.headers")
|
171
|
+
# @see {#exchange} for other parameters
|
172
|
+
# @return [Exchange]
|
173
|
+
def headers(name = "amq.headers", **kwargs)
|
174
|
+
exchange(name, "headers", **kwargs)
|
175
|
+
end
|
176
|
+
|
134
177
|
# @!endgroup
|
135
178
|
# @!group Publish
|
136
179
|
|
metadata
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amqp-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- CloudAMQP
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: Modern AMQP 0-9-1 Ruby client
|
14
13
|
email:
|
15
|
-
-
|
14
|
+
- team@cloudamqp.com
|
16
15
|
executables: []
|
17
16
|
extensions: []
|
18
17
|
extra_rdoc_files: []
|
@@ -53,7 +52,6 @@ metadata:
|
|
53
52
|
homepage_uri: https://github.com/cloudamqp/amqp-client.rb
|
54
53
|
source_code_uri: https://github.com/cloudamqp/amqp-client.rb.git
|
55
54
|
changelog_uri: https://github.com/cloudamqp/amqp-client.rb/blob/main/CHANGELOG.md
|
56
|
-
post_install_message:
|
57
55
|
rdoc_options: []
|
58
56
|
require_paths:
|
59
57
|
- lib
|
@@ -61,15 +59,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
59
|
requirements:
|
62
60
|
- - ">="
|
63
61
|
- !ruby/object:Gem::Version
|
64
|
-
version: 2.
|
62
|
+
version: 3.2.0
|
65
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
64
|
requirements:
|
67
65
|
- - ">="
|
68
66
|
- !ruby/object:Gem::Version
|
69
67
|
version: '0'
|
70
68
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
72
|
-
signing_key:
|
69
|
+
rubygems_version: 3.6.9
|
73
70
|
specification_version: 4
|
74
71
|
summary: AMQP 0-9-1 client
|
75
72
|
test_files: []
|