amqp-client 1.2.0 → 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/main.yml +7 -0
- data/.github/workflows/release.yml +28 -0
- data/CHANGELOG.md +6 -0
- data/README.md +10 -10
- data/Rakefile +53 -36
- data/lib/amqp/client/exchange.rb +14 -10
- data/lib/amqp/client/queue.rb +6 -2
- data/lib/amqp/client/version.rb +1 -1
- data/lib/amqp/client.rb +44 -0
- metadata +1 -1
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/main.yml
CHANGED
@@ -39,7 +39,14 @@ jobs:
|
|
39
39
|
with:
|
40
40
|
bundler-cache: true
|
41
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"
|
42
48
|
- name: Run tests (excluding TLS tests)
|
49
|
+
if: ${{ matrix.ruby != 'jruby' }}
|
43
50
|
continue-on-error: ${{ matrix.allow-failure || false }}
|
44
51
|
run: bundle exec rake
|
45
52
|
env:
|
@@ -12,6 +12,7 @@ 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
17
|
- uses: actions/checkout@v5
|
17
18
|
- uses: ruby/setup-ruby@v1
|
@@ -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/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
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
|
+
|
3
9
|
## [1.2.0] - 2025-09-10
|
4
10
|
|
5
11
|
- Fixed: `Connection#channel` wasn't thread-safe
|
data/README.md
CHANGED
@@ -127,12 +127,12 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
127
127
|
|
128
128
|
### Release Process
|
129
129
|
|
130
|
-
The gem uses rake tasks to automate the release process.
|
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
131
|
|
132
132
|
#### Quick Release (Patch Version)
|
133
133
|
|
134
134
|
```bash
|
135
|
-
rake release:
|
135
|
+
rake release:prepare
|
136
136
|
```
|
137
137
|
|
138
138
|
This will:
|
@@ -141,8 +141,8 @@ This will:
|
|
141
141
|
2. Bump the patch version (e.g., 1.2.0 → 1.2.1)
|
142
142
|
3. Update the CHANGELOG.md with the new version and current date
|
143
143
|
4. Create a git commit and tag for the release
|
144
|
-
5.
|
145
|
-
6.
|
144
|
+
5. Push commits and tags to the remote repository
|
145
|
+
6. GitHub Actions will automatically build and publish the gem to RubyGems
|
146
146
|
|
147
147
|
#### Custom Version Bump
|
148
148
|
|
@@ -150,10 +150,10 @@ For minor or major version bumps:
|
|
150
150
|
|
151
151
|
```bash
|
152
152
|
# Minor version bump (e.g., 1.2.0 → 1.3.0)
|
153
|
-
rake release:
|
153
|
+
rake release:prepare[minor]
|
154
154
|
|
155
155
|
# Major version bump (e.g., 1.2.0 → 2.0.0)
|
156
|
-
rake release:
|
156
|
+
rake release:prepare[major]
|
157
157
|
```
|
158
158
|
|
159
159
|
#### Individual Release Steps
|
@@ -167,11 +167,11 @@ rake release:bump[patch] # or [minor] or [major]
|
|
167
167
|
# Update changelog with current version
|
168
168
|
rake release:changelog
|
169
169
|
|
170
|
-
# Create git tag
|
170
|
+
# Create git tag with changelog entries
|
171
171
|
rake release:tag
|
172
172
|
|
173
|
-
#
|
174
|
-
rake release:
|
173
|
+
# Push tag to remote (handles conflicts)
|
174
|
+
rake release:push_tag
|
175
175
|
```
|
176
176
|
|
177
177
|
#### Manual Release Steps
|
@@ -182,7 +182,7 @@ If you prefer manual control:
|
|
182
182
|
2. Update the CHANGELOG.md with the new version and release notes
|
183
183
|
3. Commit your changes: `git add . && git commit -m "Release X.Y.Z"`
|
184
184
|
4. Create and push a tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
185
|
-
5.
|
185
|
+
5. GitHub Actions will automatically build and publish the gem when the tag is pushed
|
186
186
|
|
187
187
|
## Contributing
|
188
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|
|
@@ -31,6 +30,36 @@ def current_version
|
|
31
30
|
content.match(/VERSION = "(.+)"/)[1]
|
32
31
|
end
|
33
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
|
+
|
34
63
|
def bump_version(version_type)
|
35
64
|
unless %w[major minor patch].include?(version_type)
|
36
65
|
puts "Invalid version type. Use: major, minor, or patch"
|
@@ -87,38 +116,26 @@ def create_git_tag
|
|
87
116
|
system("git add .")
|
88
117
|
system("git commit -m 'Release #{version}'")
|
89
118
|
|
90
|
-
# Check if tag already exists and remove it if it does
|
119
|
+
# Check if tag already exists locally and remove it if it does
|
91
120
|
if system("git tag -l v#{version} | grep -q v#{version}")
|
92
|
-
puts "Tag v#{version} already exists, removing it..."
|
121
|
+
puts "Tag v#{version} already exists locally, removing it..."
|
93
122
|
system("git tag -d v#{version}")
|
94
123
|
end
|
95
124
|
|
96
|
-
|
125
|
+
# Extract changelog entries for this version
|
126
|
+
changelog_entries = extract_changelog_for_version(version)
|
97
127
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
def push_gem_to_rubygems
|
102
|
-
version = current_version
|
128
|
+
# Create tag message with version and changelog
|
129
|
+
tag_message = "Release #{version}\n\n#{changelog_entries}"
|
103
130
|
|
104
|
-
#
|
105
|
-
|
106
|
-
pkg_gem_file = "pkg/amqp-client-#{version}.gem"
|
131
|
+
# Create annotated tag with the changelog
|
132
|
+
system("git", "tag", "-a", "v#{version}", "-m", tag_message)
|
107
133
|
|
108
|
-
|
109
|
-
system("gem push #{pkg_gem_file}")
|
110
|
-
puts "Pushed #{pkg_gem_file} to RubyGems"
|
111
|
-
elsif File.exist?(gem_file)
|
112
|
-
system("gem push #{gem_file}")
|
113
|
-
puts "Pushed #{gem_file} to RubyGems"
|
114
|
-
else
|
115
|
-
puts "Gem file #{gem_file} not found in current directory or pkg/. Make sure to build first."
|
116
|
-
exit 1
|
117
|
-
end
|
134
|
+
puts "Created git tag v#{version} with changelog entries"
|
118
135
|
end
|
119
136
|
|
120
|
-
def
|
121
|
-
puts "
|
137
|
+
def prepare_release_process(version_type)
|
138
|
+
puts "Preparing release process..."
|
122
139
|
|
123
140
|
# Ensure working directory is clean
|
124
141
|
unless system("git diff --quiet && git diff --cached --quiet")
|
@@ -138,16 +155,15 @@ def full_release_process(version_type)
|
|
138
155
|
Rake::Task["release:tag"].invoke
|
139
156
|
Rake::Task["release:tag"].reenable
|
140
157
|
|
141
|
-
# Build and push gem
|
142
|
-
Rake::Task["release:push"].invoke
|
143
|
-
Rake::Task["release:push"].reenable
|
144
|
-
|
145
158
|
# Push to git
|
146
159
|
system("git push origin")
|
147
|
-
system("git push origin --tags")
|
148
160
|
|
161
|
+
# Handle tag push with potential conflicts
|
149
162
|
version = current_version
|
150
|
-
|
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."
|
151
167
|
end
|
152
168
|
|
153
169
|
namespace :release do
|
@@ -166,14 +182,15 @@ namespace :release do
|
|
166
182
|
create_git_tag
|
167
183
|
end
|
168
184
|
|
169
|
-
desc "
|
170
|
-
task
|
171
|
-
|
185
|
+
desc "Push tag to remote (handles conflicts)"
|
186
|
+
task :push_tag do
|
187
|
+
version = current_version
|
188
|
+
push_tag_to_remote(version)
|
172
189
|
end
|
173
190
|
|
174
|
-
desc "
|
175
|
-
task :
|
176
|
-
|
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")
|
177
194
|
end
|
178
195
|
end
|
179
196
|
|
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
|
|
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
@@ -83,6 +83,8 @@ module AMQP
|
|
83
83
|
return if @stopped
|
84
84
|
|
85
85
|
@stopped = true
|
86
|
+
return unless @connq.size.positive?
|
87
|
+
|
86
88
|
conn = @connq.pop
|
87
89
|
conn.close
|
88
90
|
nil
|
@@ -115,6 +117,12 @@ module AMQP
|
|
115
117
|
end
|
116
118
|
|
117
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.
|
118
126
|
# @return [Exchange]
|
119
127
|
# @example
|
120
128
|
# amqp = AMQP::Client.new.start
|
@@ -130,6 +138,42 @@ module AMQP
|
|
130
138
|
end
|
131
139
|
end
|
132
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
|
+
|
133
177
|
# @!endgroup
|
134
178
|
# @!group Publish
|
135
179
|
|