pgmq-ruby 0.3.0 → 0.4.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: 53bd6f9c6cbc408bf96813d0fa19f16d753a52a52edc93f87db61444cb2cfbd8
4
- data.tar.gz: ca1b41b82cf2cf4cde197e03309c516be9430b91f5c7ad06d0bdeea7f83b3812
3
+ metadata.gz: a6b6b3dcddd3785167a0fd8482fb0900f0d51c9c5cf68ddd23b60e84eb306012
4
+ data.tar.gz: 94604bf7c55a2bd62a63486278641c247b0a9d9cf3b50379d30ee0b7c8c840f4
5
5
  SHA512:
6
- metadata.gz: 127cb4848ce4aae0929690794dbbe128bc8ed8449b28e41687f6268152005810378b7e63cabc9cde7bcbd66701a7b4a689cf864dee48e6196a6715df2e02ead4
7
- data.tar.gz: 007c60255e9c87ba2e68765a4248319ac378677646739ac44b5f59db803a5effa27c36118e6f641d9f4145497309df11f1688080d72ca021f74c6139c88738f5
6
+ metadata.gz: 3ea99c27e92f96f137c9552985830949cdff6538409646ddf6b10e587c05c2b831c8c083570996ff2c0ffbefd09ae01fa84002e2ca9093f0a27ec6f7387a2b59
7
+ data.tar.gz: 437713bad4d37ff821487493097c6229b8c2069b260b74e587b3452f74508392b1dba7cf37ee3dd91c52143a4c147db373035351a8dc0d86f1d6cd70a4370d4d
@@ -21,7 +21,7 @@ jobs:
21
21
  fail-fast: false
22
22
  matrix:
23
23
  ruby:
24
- - '3.5.0-preview1'
24
+ - '4.0.0'
25
25
  - '3.4'
26
26
  - '3.3'
27
27
  - '3.2'
@@ -52,7 +52,7 @@ jobs:
52
52
  - 5433:5432
53
53
 
54
54
  steps:
55
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
55
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
56
56
  with:
57
57
  fetch-depth: 0
58
58
 
@@ -60,11 +60,11 @@ jobs:
60
60
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends postgresql-client"
61
61
 
62
62
  - name: Remove Gemfile.lock for Ruby previews
63
- if: contains(matrix.ruby, '3.5')
63
+ if: contains(matrix.ruby, '4.0')
64
64
  run: rm -f Gemfile.lock
65
65
 
66
66
  - name: Set up Ruby
67
- uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
67
+ uses: ruby/setup-ruby@ae195bbe749a7cef685ac729197124a48305c1cb # v1.276.0
68
68
  with:
69
69
  ruby-version: ${{ matrix.ruby }}
70
70
  bundler-cache: true
@@ -108,13 +108,15 @@ jobs:
108
108
  strategy:
109
109
  fail-fast: false
110
110
  steps:
111
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
111
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
112
112
  with:
113
113
  fetch-depth: 0
114
+ - name: Remove Gemfile.lock for Ruby 4.0
115
+ run: rm -f Gemfile.lock
114
116
  - name: Set up Ruby
115
- uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
117
+ uses: ruby/setup-ruby@ae195bbe749a7cef685ac729197124a48305c1cb # v1.276.0
116
118
  with:
117
- ruby-version: '3.4.7'
119
+ ruby-version: '4.0.0'
118
120
  bundler-cache: true
119
121
  - name: Run yard-lint
120
122
  run: bundle exec yard-lint lib/
@@ -125,7 +127,7 @@ jobs:
125
127
  strategy:
126
128
  fail-fast: false
127
129
  steps:
128
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
130
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
129
131
  with:
130
132
  fetch-depth: 0
131
133
  - name: Download Coditsu script
@@ -19,12 +19,12 @@ jobs:
19
19
  id-token: write
20
20
 
21
21
  steps:
22
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
22
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
23
23
  with:
24
24
  fetch-depth: 0
25
25
 
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
27
+ uses: ruby/setup-ruby@ae195bbe749a7cef685ac729197124a48305c1cb # v1.276.0
28
28
  with:
29
29
  bundler-cache: false
30
30
 
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.5
1
+ 4.0.0
data/.yard-lint.yml CHANGED
@@ -3,166 +3,273 @@
3
3
 
4
4
  # Global settings for all validators
5
5
  AllValidators:
6
- # YARD command-line options (applied to all validators by default)
7
6
  YardOptions:
8
- - --private
9
- - --protected
10
-
11
- # Global file exclusion patterns
7
+ - "--private"
8
+ - "--protected"
12
9
  Exclude:
13
- - '\.git'
14
- - 'vendor/**/*'
15
- - 'node_modules/**/*'
16
- - 'spec/**/*'
17
- - 'test/**/*'
18
-
19
- # Exit code behavior (error, warning, convention, never)
20
- FailOnSeverity: error
21
-
22
- # Require 100% documentation coverage
10
+ - "\\.git"
11
+ - vendor/**/*
12
+ - node_modules/**/*
13
+ - spec/**/*
14
+ - test/**/*
15
+ - benchmark/**/*
16
+ FailOnSeverity: convention
23
17
  RequiredCoverage: 100
24
18
 
25
19
  # Documentation validators
26
20
  Documentation/UndocumentedObjects:
27
- Description: 'Checks for classes, modules, and methods without documentation.'
21
+ Description: Checks for classes, modules, and methods without documentation.
28
22
  Enabled: true
29
23
  Severity: error
30
24
  ExcludedMethods:
31
- - 'initialize/0' # Exclude parameter-less initialize
32
- - '/^_/' # Exclude private methods (by convention)
25
+ - initialize/0
26
+ - "/^_/"
33
27
 
34
28
  Documentation/UndocumentedMethodArguments:
35
- Description: 'Checks for method parameters without @param tags.'
29
+ Description: Checks for method parameters without @param tags.
36
30
  Enabled: true
37
31
  Severity: error
38
32
 
39
33
  Documentation/UndocumentedBooleanMethods:
40
- Description: 'Checks that question mark methods document their boolean return.'
34
+ Description: Checks that question mark methods document their boolean return.
41
35
  Enabled: true
42
36
  Severity: error
43
37
 
44
38
  Documentation/UndocumentedOptions:
45
- Description: 'Detects methods with options hash parameters but no @option tags.'
39
+ Description: Detects methods with options hash parameters but no @option tags.
46
40
  Enabled: true
47
41
  Severity: error
48
42
 
49
43
  Documentation/MarkdownSyntax:
50
- Description: 'Detects common markdown syntax errors in documentation.'
44
+ Description: Detects common markdown syntax errors in documentation.
51
45
  Enabled: true
52
46
  Severity: error
53
47
 
48
+ Documentation/EmptyCommentLine:
49
+ Description: Detects empty comment lines at the start or end of documentation blocks.
50
+ Enabled: true
51
+ Severity: convention
52
+ EnabledPatterns:
53
+ Leading: true
54
+ Trailing: true
55
+
56
+ Documentation/BlankLineBeforeDefinition:
57
+ Description: Detects blank lines between YARD documentation and method definition.
58
+ Enabled: true
59
+ Severity: convention
60
+ OrphanedSeverity: convention
61
+ EnabledPatterns:
62
+ SingleBlankLine: true
63
+ OrphanedDocs: true
64
+
54
65
  # Tags validators
55
66
  Tags/Order:
56
- Description: 'Enforces consistent ordering of YARD tags.'
67
+ Description: Enforces consistent ordering of YARD tags.
57
68
  Enabled: true
58
69
  Severity: error
59
70
  EnforcedOrder:
60
- - param
61
- - option
62
- - return
63
- - raise
64
- - example
71
+ - param
72
+ - option
73
+ - return
74
+ - raise
75
+ - example
65
76
 
66
77
  Tags/InvalidTypes:
67
- Description: 'Validates type definitions in @param, @return, @option tags.'
78
+ Description: Validates type definitions in @param, @return, @option tags.
68
79
  Enabled: true
69
80
  Severity: error
70
81
  ValidatedTags:
71
- - param
72
- - option
73
- - return
82
+ - param
83
+ - option
84
+ - return
74
85
 
75
86
  Tags/TypeSyntax:
76
- Description: 'Validates YARD type syntax using YARD parser.'
87
+ Description: Validates YARD type syntax using YARD parser.
77
88
  Enabled: true
78
89
  Severity: error
79
90
  ValidatedTags:
80
- - param
81
- - option
82
- - return
83
- - yieldreturn
91
+ - param
92
+ - option
93
+ - return
94
+ - yieldreturn
84
95
 
85
96
  Tags/MeaninglessTag:
86
- Description: 'Detects @param/@option tags on classes, modules, or constants.'
97
+ Description: Detects @param/@option tags on classes, modules, or constants.
87
98
  Enabled: true
88
99
  Severity: error
89
100
  CheckedTags:
90
- - param
91
- - option
101
+ - param
102
+ - option
92
103
  InvalidObjectTypes:
93
- - class
94
- - module
95
- - constant
104
+ - class
105
+ - module
106
+ - constant
96
107
 
97
108
  Tags/CollectionType:
98
- Description: 'Validates Hash collection syntax consistency.'
109
+ Description: Validates Hash collection syntax consistency.
99
110
  Enabled: true
100
111
  Severity: error
101
- EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
112
+ EnforcedStyle: long
102
113
  ValidatedTags:
103
- - param
104
- - option
105
- - return
106
- - yieldreturn
114
+ - param
115
+ - option
116
+ - return
117
+ - yieldreturn
107
118
 
108
119
  Tags/TagTypePosition:
109
- Description: 'Validates type annotation position in tags.'
120
+ Description: Validates type annotation position in tags.
110
121
  Enabled: true
111
122
  Severity: error
112
123
  CheckedTags:
113
- - param
114
- - option
115
- # EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
116
- # or 'type_first' (@param [Type] name)
124
+ - param
125
+ - option
117
126
  EnforcedStyle: type_after_name
118
127
 
119
128
  Tags/ApiTags:
120
- Description: 'Enforces @api tags on public objects.'
121
- Enabled: true
129
+ Description: Enforces @api tags on public objects.
130
+ Enabled: false
122
131
  Severity: error
123
132
  AllowedApis:
124
- - public
125
- - private
126
- - internal
133
+ - public
134
+ - private
135
+ - internal
127
136
 
128
137
  Tags/OptionTags:
129
- Description: 'Requires @option tags for methods with options parameters.'
138
+ Description: Requires @option tags for methods with options parameters.
130
139
  Enabled: true
131
140
  Severity: error
132
141
 
142
+ Tags/ExampleSyntax:
143
+ Description: Validates Ruby syntax in @example tags.
144
+ Enabled: true
145
+ Severity: warning
146
+
147
+ Tags/RedundantParamDescription:
148
+ Description: Detects meaningless parameter descriptions that add no value.
149
+ Enabled: true
150
+ Severity: convention
151
+ CheckedTags:
152
+ - param
153
+ - option
154
+ Articles:
155
+ - The
156
+ - the
157
+ - A
158
+ - a
159
+ - An
160
+ - an
161
+ MaxRedundantWords: 6
162
+ GenericTerms:
163
+ - object
164
+ - instance
165
+ - value
166
+ - data
167
+ - item
168
+ - element
169
+ EnabledPatterns:
170
+ ArticleParam: true
171
+ PossessiveParam: true
172
+ TypeRestatement: true
173
+ ParamToVerb: true
174
+ IdPattern: true
175
+ DirectionalDate: true
176
+ TypeGeneric: true
177
+
178
+ Tags/InformalNotation:
179
+ Description: Detects informal tag notation patterns like "Note:" instead of @note.
180
+ Enabled: true
181
+ Severity: warning
182
+ CaseSensitive: false
183
+ RequireStartOfLine: true
184
+ Patterns:
185
+ Note: "@note"
186
+ Todo: "@todo"
187
+ TODO: "@todo"
188
+ FIXME: "@todo"
189
+ See: "@see"
190
+ See also: "@see"
191
+ Warning: "@deprecated"
192
+ Deprecated: "@deprecated"
193
+ Author: "@author"
194
+ Version: "@version"
195
+ Since: "@since"
196
+ Returns: "@return"
197
+ Raises: "@raise"
198
+ Example: "@example"
199
+
200
+ Tags/NonAsciiType:
201
+ Description: Detects non-ASCII characters in type annotations.
202
+ Enabled: true
203
+ Severity: warning
204
+ ValidatedTags:
205
+ - param
206
+ - option
207
+ - return
208
+ - yieldreturn
209
+ - yieldparam
210
+
211
+ Tags/TagGroupSeparator:
212
+ Description: Enforces blank line separators between different YARD tag groups.
213
+ Enabled: false
214
+ Severity: convention
215
+ TagGroups:
216
+ param:
217
+ - param
218
+ - option
219
+ return:
220
+ - return
221
+ error:
222
+ - raise
223
+ - throws
224
+ example:
225
+ - example
226
+ meta:
227
+ - see
228
+ - note
229
+ - todo
230
+ - deprecated
231
+ - since
232
+ - version
233
+ - api
234
+ yield:
235
+ - yield
236
+ - yieldparam
237
+ - yieldreturn
238
+ RequireAfterDescription: false
239
+
133
240
  # Warnings validators - catches YARD parser errors
134
241
  Warnings/UnknownTag:
135
- Description: 'Detects unknown YARD tags.'
242
+ Description: Detects unknown YARD tags.
136
243
  Enabled: true
137
244
  Severity: error
138
245
 
139
246
  Warnings/UnknownDirective:
140
- Description: 'Detects unknown YARD directives.'
247
+ Description: Detects unknown YARD directives.
141
248
  Enabled: true
142
249
  Severity: error
143
250
 
144
251
  Warnings/InvalidTagFormat:
145
- Description: 'Detects malformed tag syntax.'
252
+ Description: Detects malformed tag syntax.
146
253
  Enabled: true
147
254
  Severity: error
148
255
 
149
256
  Warnings/InvalidDirectiveFormat:
150
- Description: 'Detects malformed directive syntax.'
257
+ Description: Detects malformed directive syntax.
151
258
  Enabled: true
152
259
  Severity: error
153
260
 
154
261
  Warnings/DuplicatedParameterName:
155
- Description: 'Detects duplicate @param tags.'
262
+ Description: Detects duplicate @param tags.
156
263
  Enabled: true
157
264
  Severity: error
158
265
 
159
266
  Warnings/UnknownParameterName:
160
- Description: 'Detects @param tags for non-existent parameters.'
267
+ Description: Detects @param tags for non-existent parameters.
161
268
  Enabled: true
162
269
  Severity: error
163
270
 
164
271
  # Semantic validators
165
272
  Semantic/AbstractMethods:
166
- Description: 'Ensures @abstract methods do not have real implementations.'
273
+ Description: Ensures @abstract methods do not have real implementations.
167
274
  Enabled: true
168
275
  Severity: error
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 (2025-12-26)
4
+
5
+ ### Breaking Changes
6
+ - **[Breaking]** Rename `send` to `produce` and `send_batch` to `produce_batch`. This avoids shadowing Ruby's built-in `Object#send` method which caused confusion and required workarounds (e.g., using `__send__`). The new names also align better with the producer/consumer terminology used in message queue systems.
7
+
8
+ ### Queue Management
9
+ - [Enhancement] `create`, `create_partitioned`, and `create_unlogged` now return `true` if the queue was newly created, `false` if it already existed. This provides clearer feedback and aligns with the Rust PGMQ client behavior.
10
+
11
+ ### Message Operations
12
+ - **[Feature]** Add `headers:` parameter to `produce(queue_name, message, headers:, delay:)` for message metadata (routing, tracing, correlation IDs).
13
+ - **[Feature]** Add `headers:` parameter to `produce_batch(queue_name, messages, headers:, delay:)` for batch message metadata.
14
+ - **[Feature]** Introduce `pop_batch(queue_name, qty)` for atomic batch pop (read + delete) operations.
15
+ - **[Feature]** Introduce `set_vt_batch(queue_name, msg_ids, vt_offset:)` for batch visibility timeout updates.
16
+ - **[Feature]** Introduce `set_vt_multi(updates_hash, vt_offset:)` for updating visibility timeouts across multiple queues atomically.
17
+
18
+ ### Notifications
19
+ - **[Feature]** Introduce `enable_notify_insert(queue_name, throttle_interval_ms:)` for PostgreSQL LISTEN/NOTIFY support.
20
+ - **[Feature]** Introduce `disable_notify_insert(queue_name)` to disable notifications.
21
+
22
+ ### Compatibility
23
+ - [Enhancement] Add Ruby 4.0.0 support with full CI testing.
24
+
3
25
  ## 0.3.0 (2025-11-14)
4
26
 
5
27
  Initial release of pgmq-ruby - a low-level Ruby client for PGMQ (PostgreSQL Message Queue).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pgmq-ruby (0.3.0)
4
+ pgmq-ruby (0.4.0)
5
5
  connection_pool (~> 2.4)
6
6
  pg (~> 1.5)
7
7
  zeitwerk (~> 2.6)
@@ -39,8 +39,8 @@ GEM
39
39
  simplecov_json_formatter (~> 0.1)
40
40
  simplecov-html (0.13.2)
41
41
  simplecov_json_formatter (0.1.4)
42
- yard (0.9.37)
43
- yard-lint (1.2.3)
42
+ yard (0.9.38)
43
+ yard-lint (1.3.0)
44
44
  yard (~> 0.9)
45
45
  zeitwerk (~> 2.6)
46
46
  zeitwerk (2.7.3)
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # PGMQ-Ruby
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/pgmq-ruby.svg)](https://badge.fury.io/rb/pgmq-ruby)
4
- [![Build Status](https://github.com/mensfeld/pgmq-ruby/workflows/ci/badge.svg)](https://github.com/mensfeld/pgmq-ruby/actions)
4
+ [![Build Status](https://github.com/mensfeld/pgmq-ruby/workflows/CI/badge.svg)](https://github.com/mensfeld/pgmq-ruby/actions)
5
5
 
6
6
  **Ruby client for [PGMQ](https://github.com/pgmq/pgmq) - PostgreSQL Message Queue**
7
7
 
@@ -38,12 +38,13 @@ This gem provides complete support for all core PGMQ SQL functions. Based on the
38
38
 
39
39
  | Category | Method | Description | Status |
40
40
  |----------|--------|-------------|--------|
41
- | **Sending** | `send` | Send single message with optional delay | ✅ |
42
- | | `send_batch` | Send multiple messages atomically | ✅ |
41
+ | **Producing** | `produce` | Send single message with optional delay and headers | ✅ |
42
+ | | `produce_batch` | Send multiple messages atomically with headers | ✅ |
43
43
  | **Reading** | `read` | Read single message with visibility timeout | ✅ |
44
44
  | | `read_batch` | Read multiple messages with visibility timeout | ✅ |
45
45
  | | `read_with_poll` | Long-polling for efficient message consumption | ✅ |
46
46
  | | `pop` | Atomic read + delete operation | ✅ |
47
+ | | `pop_batch` | Atomic batch read + delete operation | ✅ |
47
48
  | **Deleting/Archiving** | `delete` | Delete single message | ✅ |
48
49
  | | `delete_batch` | Delete multiple messages | ✅ |
49
50
  | | `archive` | Archive single message for long-term storage | ✅ |
@@ -55,9 +56,13 @@ This gem provides complete support for all core PGMQ SQL functions. Based on the
55
56
  | | `drop_queue` | Delete queue and all messages | ✅ |
56
57
  | | `detach_archive` | Detach archive table from queue | ✅ |
57
58
  | **Utilities** | `set_vt` | Update message visibility timeout | ✅ |
59
+ | | `set_vt_batch` | Batch update visibility timeouts | ✅ |
60
+ | | `set_vt_multi` | Update visibility timeouts across multiple queues | ✅ |
58
61
  | | `list_queues` | List all queues with metadata | ✅ |
59
62
  | | `metrics` | Get queue metrics (length, age, total messages) | ✅ |
60
63
  | | `metrics_all` | Get metrics for all queues | ✅ |
64
+ | | `enable_notify_insert` | Enable PostgreSQL NOTIFY on insert | ✅ |
65
+ | | `disable_notify_insert` | Disable notifications | ✅ |
61
66
  | **Ruby Enhancements** | Transaction Support | Atomic operations via `client.transaction do \|txn\|` | ✅ |
62
67
  | | Conditional Filtering | Server-side JSONB filtering with `conditional:` | ✅ |
63
68
  | | Multi-Queue Ops | Read/pop/delete/archive from multiple queues | ✅ |
@@ -104,7 +109,7 @@ client = PGMQ::Client.new(
104
109
  client.create('orders')
105
110
 
106
111
  # Send a message (must be JSON string)
107
- msg_id = client.send('orders', '{"order_id":123,"total":99.99}')
112
+ msg_id = client.produce('orders', '{"order_id":123,"total":99.99}')
108
113
 
109
114
  # Read a message (30 second visibility timeout)
110
115
  msg = client.read('orders', vt: 30)
@@ -224,20 +229,21 @@ client = PGMQ::Client.new(
224
229
  ### Queue Management
225
230
 
226
231
  ```ruby
227
- # Create a queue
228
- client.create("queue_name")
232
+ # Create a queue (returns true if created, false if already exists)
233
+ client.create("queue_name") # => true
234
+ client.create("queue_name") # => false (idempotent)
229
235
 
230
236
  # Create partitioned queue (requires pg_partman)
231
237
  client.create_partitioned("queue_name",
232
238
  partition_interval: "daily",
233
239
  retention_interval: "7 days"
234
- )
240
+ ) # => true/false
235
241
 
236
242
  # Create unlogged queue (faster, no crash recovery)
237
- client.create_unlogged("queue_name")
243
+ client.create_unlogged("queue_name") # => true/false
238
244
 
239
- # Drop queue
240
- client.drop_queue("queue_name")
245
+ # Drop queue (returns true if dropped, false if didn't exist)
246
+ client.drop_queue("queue_name") # => true/false
241
247
 
242
248
  # List all queues
243
249
  queues = client.list_queues
@@ -277,18 +283,32 @@ client.create("a" * 48) # ✗ Too long (48+ chars)
277
283
 
278
284
  ```ruby
279
285
  # Send single message (must be JSON string)
280
- msg_id = client.send("queue_name", '{"data":"value"}')
286
+ msg_id = client.produce("queue_name", '{"data":"value"}')
281
287
 
282
288
  # Send with delay (seconds)
283
- msg_id = client.send("queue_name", '{"data":"value"}', delay: 60)
289
+ msg_id = client.produce("queue_name", '{"data":"value"}', delay: 60)
290
+
291
+ # Send with headers (for routing, tracing, correlation)
292
+ msg_id = client.produce("queue_name", '{"data":"value"}',
293
+ headers: '{"trace_id":"abc123","priority":"high"}')
294
+
295
+ # Send with headers and delay
296
+ msg_id = client.produce("queue_name", '{"data":"value"}',
297
+ headers: '{"correlation_id":"req-456"}',
298
+ delay: 60)
284
299
 
285
300
  # Send batch (array of JSON strings)
286
- msg_ids = client.send_batch("queue_name", [
301
+ msg_ids = client.produce_batch("queue_name", [
287
302
  '{"order":1}',
288
303
  '{"order":2}',
289
304
  '{"order":3}'
290
305
  ])
291
306
  # => ["101", "102", "103"]
307
+
308
+ # Send batch with headers (one per message)
309
+ msg_ids = client.produce_batch("queue_name",
310
+ ['{"order":1}', '{"order":2}'],
311
+ headers: ['{"priority":"high"}', '{"priority":"low"}'])
292
312
  ```
293
313
 
294
314
  ### Reading Messages
@@ -311,6 +331,9 @@ msg = client.read_with_poll("queue_name",
311
331
 
312
332
  # Pop (atomic read + delete)
313
333
  msg = client.pop("queue_name")
334
+
335
+ # Pop batch (atomic read + delete for multiple messages)
336
+ messages = client.pop_batch("queue_name", 10)
314
337
  ```
315
338
 
316
339
  #### Conditional Message Filtering
@@ -378,8 +401,23 @@ archived_ids = client.archive_batch("queue_name", [101, 102, 103])
378
401
  # Update visibility timeout
379
402
  msg = client.set_vt("queue_name", msg_id, vt_offset: 60)
380
403
 
404
+ # Batch update visibility timeout
405
+ updated_msgs = client.set_vt_batch("queue_name", [101, 102, 103], vt_offset: 60)
406
+
407
+ # Update visibility timeout across multiple queues
408
+ client.set_vt_multi({
409
+ "orders" => [1, 2, 3],
410
+ "notifications" => [5, 6]
411
+ }, vt_offset: 120)
412
+
381
413
  # Purge all messages
382
414
  count = client.purge_queue("queue_name")
415
+
416
+ # Enable PostgreSQL NOTIFY for a queue (for LISTEN-based consumers)
417
+ client.enable_notify_insert("queue_name", throttle_interval_ms: 250)
418
+
419
+ # Disable notifications
420
+ client.disable_notify_insert("queue_name")
383
421
  ```
384
422
 
385
423
  ### Monitoring
@@ -409,9 +447,9 @@ Execute atomic operations across multiple queues or combine queue operations wit
409
447
  # Atomic operations across multiple queues
410
448
  client.transaction do |txn|
411
449
  # Send to multiple queues atomically
412
- txn.send("orders", '{"order_id":123}')
413
- txn.send("notifications", '{"user_id":456,"type":"order_created"}')
414
- txn.send("analytics", '{"event":"order_placed"}')
450
+ txn.produce("orders", '{"order_id":123}')
451
+ txn.produce("notifications", '{"user_id":456,"type":"order_created"}')
452
+ txn.produce("analytics", '{"event":"order_placed"}')
415
453
  end
416
454
 
417
455
  # Process message and update application state atomically
@@ -431,8 +469,8 @@ end
431
469
 
432
470
  # Automatic rollback on errors
433
471
  client.transaction do |txn|
434
- txn.send("queue1", '{"data":"message1"}')
435
- txn.send("queue2", '{"data":"message2"}')
472
+ txn.produce("queue1", '{"data":"message1"}')
473
+ txn.produce("queue2", '{"data":"message2"}')
436
474
 
437
475
  raise "Something went wrong!"
438
476
  # Both messages are rolled back - neither queue receives anything
@@ -446,7 +484,7 @@ client.transaction do |txn|
446
484
  data = JSON.parse(msg.message)
447
485
  if data["priority"] == "high"
448
486
  # Move to high-priority queue
449
- txn.send("priority_orders", msg.message)
487
+ txn.produce("priority_orders", msg.message)
450
488
  txn.delete("pending_orders", msg.msg_id)
451
489
  end
452
490
  end
@@ -503,21 +541,43 @@ enqueued = Time.parse(msg.enqueued_at) # => 2025-01-15 10:30:00 UTC
503
541
 
504
542
  ### Message Headers
505
543
 
506
- PGMQ supports optional message headers via the `headers` JSONB column:
544
+ PGMQ supports optional message headers via the `headers` JSONB column. Headers are useful for metadata like routing information, correlation IDs, and distributed tracing:
507
545
 
508
546
  ```ruby
509
- # Sending with headers requires direct SQL or a custom wrapper
510
- # (pgmq-ruby focuses on the core PGMQ API which doesn't have a send_with_headers function)
547
+ # Sending a message with headers
548
+ message = '{"order_id":123}'
549
+ headers = '{"trace_id":"abc123","priority":"high","correlation_id":"req-456"}'
550
+
551
+ msg_id = client.produce("orders", message, headers: headers)
552
+
553
+ # Sending with headers and delay
554
+ msg_id = client.produce("orders", message, headers: headers, delay: 60)
555
+
556
+ # Batch produce with headers (one header object per message)
557
+ messages = ['{"id":1}', '{"id":2}', '{"id":3}']
558
+ headers = [
559
+ '{"priority":"high"}',
560
+ '{"priority":"medium"}',
561
+ '{"priority":"low"}'
562
+ ]
563
+ msg_ids = client.produce_batch("orders", messages, headers: headers)
511
564
 
512
565
  # Reading messages with headers
513
- msg = client.read("queue", vt: 30)
566
+ msg = client.read("orders", vt: 30)
514
567
  if msg.headers
515
568
  metadata = JSON.parse(msg.headers)
516
569
  trace_id = metadata["trace_id"]
570
+ priority = metadata["priority"]
517
571
  correlation_id = metadata["correlation_id"]
518
572
  end
519
573
  ```
520
574
 
575
+ Common header use cases:
576
+ - **Distributed tracing**: `trace_id`, `span_id`, `parent_span_id`
577
+ - **Request correlation**: `correlation_id`, `causation_id`
578
+ - **Routing**: `priority`, `region`, `tenant_id`
579
+ - **Content metadata**: `content_type`, `encoding`, `version`
580
+
521
581
  ### Why Raw Values?
522
582
 
523
583
  This library follows the **rdkafka-ruby philosophy** - provide a thin, performant wrapper around the underlying system:
@@ -538,14 +598,14 @@ PGMQ stores messages as JSONB in PostgreSQL. You must handle JSON serialization
538
598
  ```ruby
539
599
  # Simple hash
540
600
  msg = { order_id: 123, status: "pending" }
541
- client.send("orders", msg.to_json)
601
+ client.produce("orders", msg.to_json)
542
602
 
543
603
  # Using JSON.generate for explicit control
544
- client.send("orders", JSON.generate(order_id: 123, status: "pending"))
604
+ client.produce("orders", JSON.generate(order_id: 123, status: "pending"))
545
605
 
546
606
  # Pre-serialized JSON string
547
607
  json_str = '{"order_id":123,"status":"pending"}'
548
- client.send("orders", json_str)
608
+ client.produce("orders", json_str)
549
609
  ```
550
610
 
551
611
  ### Reading Messages
@@ -577,8 +637,8 @@ class QueueHelper
577
637
  @client = client
578
638
  end
579
639
 
580
- def send(queue, data)
581
- @client.send(queue, data.to_json)
640
+ def produce(queue, data)
641
+ @client.produce(queue, data.to_json)
582
642
  end
583
643
 
584
644
  def read(queue, vt:)
@@ -595,7 +655,7 @@ class QueueHelper
595
655
  end
596
656
 
597
657
  helper = QueueHelper.new(client)
598
- helper.send("orders", { order_id: 123 })
658
+ helper.produce("orders", { order_id: 123 })
599
659
  msg = helper.read("orders", vt: 30)
600
660
  puts msg.data["order_id"] # => 123
601
661
  ```
data/docker-compose.yml CHANGED
@@ -2,7 +2,7 @@ version: '3.8'
2
2
 
3
3
  services:
4
4
  postgres:
5
- image: ghcr.io/pgmq/pg18-pgmq:v1.7.0
5
+ image: ghcr.io/pgmq/pg18-pgmq:v1.8.0
6
6
  container_name: pgmq_postgres_test
7
7
  environment:
8
8
  POSTGRES_USER: postgres
@@ -41,6 +41,54 @@ module PGMQ
41
41
 
42
42
  nil
43
43
  end
44
+
45
+ # Enables PostgreSQL NOTIFY when messages are inserted into a queue
46
+ #
47
+ # When enabled, PostgreSQL will send a NOTIFY event on message insert,
48
+ # allowing clients to use LISTEN instead of polling. The throttle interval
49
+ # prevents notification storms during high-volume inserts.
50
+ #
51
+ # @param queue_name [String] name of the queue
52
+ # @param throttle_interval_ms [Integer] minimum ms between notifications (default: 250)
53
+ # @return [void]
54
+ #
55
+ # @example Enable with default throttle (250ms)
56
+ # client.enable_notify_insert("orders")
57
+ #
58
+ # @example Enable with custom throttle (1 second)
59
+ # client.enable_notify_insert("orders", throttle_interval_ms: 1000)
60
+ #
61
+ # @example Disable throttling (notify on every insert)
62
+ # client.enable_notify_insert("orders", throttle_interval_ms: 0)
63
+ def enable_notify_insert(queue_name, throttle_interval_ms: 250)
64
+ validate_queue_name!(queue_name)
65
+
66
+ with_connection do |conn|
67
+ conn.exec_params(
68
+ 'SELECT pgmq.enable_notify_insert($1::text, $2::integer)',
69
+ [queue_name, throttle_interval_ms]
70
+ )
71
+ end
72
+
73
+ nil
74
+ end
75
+
76
+ # Disables PostgreSQL NOTIFY for a queue
77
+ #
78
+ # @param queue_name [String] name of the queue
79
+ # @return [void]
80
+ #
81
+ # @example
82
+ # client.disable_notify_insert("orders")
83
+ def disable_notify_insert(queue_name)
84
+ validate_queue_name!(queue_name)
85
+
86
+ with_connection do |conn|
87
+ conn.exec_params('SELECT pgmq.disable_notify_insert($1::text)', [queue_name])
88
+ end
89
+
90
+ nil
91
+ end
44
92
  end
45
93
  end
46
94
  end
@@ -27,6 +27,26 @@ module PGMQ
27
27
  Message.new(result[0])
28
28
  end
29
29
 
30
+ # Pops multiple messages atomically (atomic read + delete for batch)
31
+ #
32
+ # @param queue_name [String] name of the queue
33
+ # @param qty [Integer] maximum number of messages to pop
34
+ # @return [Array<PGMQ::Message>] array of message objects (empty if queue is empty)
35
+ #
36
+ # @example Pop up to 10 messages
37
+ # messages = client.pop_batch("orders", 10)
38
+ # messages.each { |msg| process(msg.payload) }
39
+ def pop_batch(queue_name, qty)
40
+ validate_queue_name!(queue_name)
41
+ return [] if qty <= 0
42
+
43
+ result = with_connection do |conn|
44
+ conn.exec_params('SELECT * FROM pgmq.pop($1::text, $2::integer)', [queue_name, qty])
45
+ end
46
+
47
+ result.map { |row| Message.new(row) }
48
+ end
49
+
30
50
  # Deletes a message from the queue
31
51
  #
32
52
  # @param queue_name [String] name of the queue
@@ -212,7 +232,7 @@ module PGMQ
212
232
  # @param queue_name [String] name of the queue
213
233
  # @param msg_id [Integer] message ID
214
234
  # @param vt_offset [Integer] visibility timeout offset in seconds
215
- # @return [PGMQ::Message] updated message
235
+ # @return [PGMQ::Message, nil] updated message or nil if not found
216
236
  #
217
237
  # @example
218
238
  # # Extend processing time by 60 more seconds
@@ -235,6 +255,78 @@ module PGMQ
235
255
 
236
256
  Message.new(result[0])
237
257
  end
258
+
259
+ # Updates visibility timeout for multiple messages
260
+ #
261
+ # @param queue_name [String] name of the queue
262
+ # @param msg_ids [Array<Integer>] array of message IDs
263
+ # @param vt_offset [Integer] visibility timeout offset in seconds
264
+ # @return [Array<PGMQ::Message>] array of updated messages
265
+ #
266
+ # @example
267
+ # # Extend processing time for multiple messages
268
+ # messages = client.set_vt_batch("orders", [101, 102, 103], vt_offset: 60)
269
+ def set_vt_batch(
270
+ queue_name,
271
+ msg_ids,
272
+ vt_offset:
273
+ )
274
+ validate_queue_name!(queue_name)
275
+ return [] if msg_ids.empty?
276
+
277
+ result = with_connection do |conn|
278
+ encoder = PG::TextEncoder::Array.new
279
+ encoded_array = encoder.encode(msg_ids)
280
+
281
+ conn.exec_params(
282
+ 'SELECT * FROM pgmq.set_vt($1::text, $2::bigint[], $3::integer)',
283
+ [queue_name, encoded_array, vt_offset]
284
+ )
285
+ end
286
+
287
+ result.map { |row| Message.new(row) }
288
+ end
289
+
290
+ # Updates visibility timeout for messages across multiple queues in a single transaction
291
+ #
292
+ # Efficiently updates visibility timeouts across different queues atomically.
293
+ # Useful when processing related messages from different queues and needing
294
+ # to extend their visibility timeouts together.
295
+ #
296
+ # @param updates [Hash] hash of queue_name => array of msg_ids
297
+ # @param vt_offset [Integer] visibility timeout offset in seconds (applied to all)
298
+ # @return [Hash] hash of queue_name => array of updated PGMQ::Message objects
299
+ #
300
+ # @example Extend visibility timeout for messages from multiple queues
301
+ # client.set_vt_multi({
302
+ # 'orders' => [1, 2, 3],
303
+ # 'notifications' => [5, 6],
304
+ # 'emails' => [10]
305
+ # }, vt_offset: 60)
306
+ # # => { 'orders' => [<Message>, ...], 'notifications' => [...], 'emails' => [...] }
307
+ #
308
+ # @example Extend timeout after batch reading from multiple queues
309
+ # messages = client.read_multi(['q1', 'q2', 'q3'], qty: 10)
310
+ # updates = messages.group_by(&:queue_name).transform_values { |msgs| msgs.map(&:msg_id) }
311
+ # client.set_vt_multi(updates, vt_offset: 120)
312
+ def set_vt_multi(updates, vt_offset:)
313
+ raise ArgumentError, 'updates must be a hash' unless updates.is_a?(Hash)
314
+ return {} if updates.empty?
315
+
316
+ # Validate all queue names
317
+ updates.each_key { |qn| validate_queue_name!(qn) }
318
+
319
+ transaction do |txn|
320
+ result = {}
321
+ updates.each do |queue_name, msg_ids|
322
+ next if msg_ids.empty?
323
+
324
+ updated_messages = txn.set_vt_batch(queue_name, msg_ids, vt_offset: vt_offset)
325
+ result[queue_name] = updated_messages
326
+ end
327
+ result
328
+ end
329
+ end
238
330
  end
239
331
  end
240
332
  end
@@ -2,75 +2,120 @@
2
2
 
3
3
  module PGMQ
4
4
  class Client
5
- # Message sending operations
5
+ # Message producing operations
6
6
  #
7
- # This module handles sending messages to queues, both individual messages
7
+ # This module handles producing messages to queues, both individual messages
8
8
  # and batches. Users must serialize messages to JSON strings themselves.
9
9
  module Producer
10
- # Sends a message to a queue
10
+ # Produces a message to a queue
11
11
  #
12
12
  # @param queue_name [String] name of the queue
13
13
  # @param message [String] message as JSON string (for PostgreSQL JSONB)
14
+ # @param headers [String, nil] optional headers as JSON string (for metadata, routing, tracing)
14
15
  # @param delay [Integer] delay in seconds before message becomes visible
15
16
  # @return [String] message ID as string
16
17
  #
17
- # @example
18
- # msg_id = client.send("orders", '{"order_id":123,"total":99.99}')
18
+ # @example Basic produce
19
+ # msg_id = client.produce("orders", '{"order_id":123,"total":99.99}')
19
20
  #
20
21
  # @example With delay
21
- # msg_id = client.send("orders", '{"data":"value"}', delay: 60)
22
+ # msg_id = client.produce("orders", '{"data":"value"}', delay: 60)
23
+ #
24
+ # @example With headers for routing/tracing
25
+ # msg_id = client.produce("orders", '{"order_id":123}',
26
+ # headers: '{"trace_id":"abc123","priority":"high"}')
27
+ #
28
+ # @example With headers and delay
29
+ # msg_id = client.produce("orders", '{"order_id":123}',
30
+ # headers: '{"correlation_id":"req-456"}',
31
+ # delay: 30)
22
32
  #
23
33
  # @note Users must serialize to JSON themselves. Higher-level frameworks
24
34
  # should handle serialization.
25
- def send(
35
+ def produce(
26
36
  queue_name,
27
37
  message,
38
+ headers: nil,
28
39
  delay: 0
29
40
  )
30
41
  validate_queue_name!(queue_name)
31
42
 
32
43
  result = with_connection do |conn|
33
- conn.exec_params(
34
- 'SELECT * FROM pgmq.send($1::text, $2::jsonb, $3::integer)',
35
- [queue_name, message, delay]
36
- )
44
+ if headers
45
+ conn.exec_params(
46
+ 'SELECT * FROM pgmq.send($1::text, $2::jsonb, $3::jsonb, $4::integer)',
47
+ [queue_name, message, headers, delay]
48
+ )
49
+ else
50
+ conn.exec_params(
51
+ 'SELECT * FROM pgmq.send($1::text, $2::jsonb, $3::integer)',
52
+ [queue_name, message, delay]
53
+ )
54
+ end
37
55
  end
38
56
 
39
57
  result[0]['send']
40
58
  end
41
59
 
42
- # Sends multiple messages to a queue in a batch
60
+ # Produces multiple messages to a queue in a batch
43
61
  #
44
62
  # @param queue_name [String] name of the queue
45
- # @param messages [Array<Hash>] array of message payloads
63
+ # @param messages [Array<String>] array of message payloads as JSON strings
64
+ # @param headers [Array<String>, nil] optional array of headers as JSON strings (must match messages length)
46
65
  # @param delay [Integer] delay in seconds before messages become visible
47
- # @return [Array<Integer>] array of message IDs
66
+ # @return [Array<String>] array of message IDs
67
+ # @raise [ArgumentError] if headers array length doesn't match messages length
48
68
  #
49
- # @example
50
- # ids = client.send_batch("orders", [
51
- # { order_id: 1 },
52
- # { order_id: 2 },
53
- # { order_id: 3 }
69
+ # @example Basic batch produce
70
+ # ids = client.produce_batch("orders", [
71
+ # '{"order_id":1}',
72
+ # '{"order_id":2}',
73
+ # '{"order_id":3}'
54
74
  # ])
55
- def send_batch(
75
+ #
76
+ # @example With headers (one per message)
77
+ # ids = client.produce_batch("orders",
78
+ # ['{"order_id":1}', '{"order_id":2}'],
79
+ # headers: ['{"priority":"high"}', '{"priority":"low"}'])
80
+ #
81
+ # @example With headers and delay
82
+ # ids = client.produce_batch("orders",
83
+ # ['{"order_id":1}', '{"order_id":2}'],
84
+ # headers: ['{"trace_id":"a"}', '{"trace_id":"b"}'],
85
+ # delay: 60)
86
+ def produce_batch(
56
87
  queue_name,
57
88
  messages,
89
+ headers: nil,
58
90
  delay: 0
59
91
  )
60
92
  validate_queue_name!(queue_name)
61
93
  return [] if messages.empty?
62
94
 
95
+ if headers && headers.length != messages.length
96
+ raise ArgumentError,
97
+ "headers array length (#{headers.length}) must match messages array length (#{messages.length})"
98
+ end
99
+
63
100
  # Use PostgreSQL array parameter binding for security
64
101
  # PG gem will properly encode the array values
65
102
  result = with_connection do |conn|
66
103
  # Create array encoder for proper PostgreSQL array formatting
67
104
  encoder = PG::TextEncoder::Array.new
68
- encoded_array = encoder.encode(messages)
105
+ encoded_messages = encoder.encode(messages)
69
106
 
70
- conn.exec_params(
71
- 'SELECT * FROM pgmq.send_batch($1::text, $2::jsonb[], $3::integer)',
72
- [queue_name, encoded_array, delay]
73
- )
107
+ if headers
108
+ encoded_headers = encoder.encode(headers)
109
+ conn.exec_params(
110
+ 'SELECT * FROM pgmq.send_batch($1::text, $2::jsonb[], $3::jsonb[], $4::integer)',
111
+ [queue_name, encoded_messages, encoded_headers, delay]
112
+ )
113
+ else
114
+ conn.exec_params(
115
+ 'SELECT * FROM pgmq.send_batch($1::text, $2::jsonb[], $3::integer)',
116
+ [queue_name, encoded_messages, delay]
117
+ )
118
+ end
74
119
  end
75
120
 
76
121
  result.map { |row| row['send_batch'] }
@@ -10,20 +10,21 @@ module PGMQ
10
10
  # Creates a new queue
11
11
  #
12
12
  # @param queue_name [String] name of the queue to create
13
- # @return [void]
13
+ # @return [Boolean] true if queue was created, false if it already existed
14
14
  # @raise [PGMQ::Errors::InvalidQueueNameError] if queue name is invalid
15
15
  # @raise [PGMQ::Errors::ConnectionError] if database operation fails
16
16
  #
17
17
  # @example
18
- # client.create("orders")
18
+ # client.create("orders") # => true (created)
19
+ # client.create("orders") # => false (already exists)
19
20
  def create(queue_name)
20
21
  validate_queue_name!(queue_name)
21
22
 
22
23
  with_connection do |conn|
24
+ existed = queue_exists?(conn, queue_name)
23
25
  conn.exec_params('SELECT pgmq.create($1::text)', [queue_name])
26
+ !existed
24
27
  end
25
-
26
- nil
27
28
  end
28
29
 
29
30
  # Creates a partitioned queue
@@ -33,13 +34,13 @@ module PGMQ
33
34
  # @param queue_name [String] name of the queue
34
35
  # @param partition_interval [String] partition interval (e.g., "daily", "10000")
35
36
  # @param retention_interval [String] retention interval (e.g., "7 days", "100000")
36
- # @return [void]
37
+ # @return [Boolean] true if queue was created, false if it already existed
37
38
  #
38
39
  # @example
39
40
  # client.create_partitioned("big_queue",
40
41
  # partition_interval: "daily",
41
42
  # retention_interval: "7 days"
42
- # )
43
+ # ) # => true
43
44
  def create_partitioned(
44
45
  queue_name,
45
46
  partition_interval: '10000',
@@ -48,30 +49,30 @@ module PGMQ
48
49
  validate_queue_name!(queue_name)
49
50
 
50
51
  with_connection do |conn|
52
+ existed = queue_exists?(conn, queue_name)
51
53
  conn.exec_params(
52
54
  'SELECT pgmq.create_partitioned($1::text, $2::text, $3::text)',
53
55
  [queue_name, partition_interval, retention_interval]
54
56
  )
57
+ !existed
55
58
  end
56
-
57
- nil
58
59
  end
59
60
 
60
61
  # Creates an unlogged queue for higher throughput (no crash recovery)
61
62
  #
62
63
  # @param queue_name [String] name of the queue
63
- # @return [void]
64
+ # @return [Boolean] true if queue was created, false if it already existed
64
65
  #
65
66
  # @example
66
- # client.create_unlogged("fast_queue")
67
+ # client.create_unlogged("fast_queue") # => true
67
68
  def create_unlogged(queue_name)
68
69
  validate_queue_name!(queue_name)
69
70
 
70
71
  with_connection do |conn|
72
+ existed = queue_exists?(conn, queue_name)
71
73
  conn.exec_params('SELECT pgmq.create_unlogged($1::text)', [queue_name])
74
+ !existed
72
75
  end
73
-
74
- nil
75
76
  end
76
77
 
77
78
  # Drops a queue and its archive table
@@ -107,6 +108,21 @@ module PGMQ
107
108
 
108
109
  result.map { |row| QueueMetadata.new(row) }
109
110
  end
111
+
112
+ private
113
+
114
+ # Checks if a queue exists in the pgmq.meta table
115
+ #
116
+ # @param conn [PG::Connection] database connection
117
+ # @param queue_name [String] name of the queue to check
118
+ # @return [Boolean] true if queue exists, false otherwise
119
+ def queue_exists?(conn, queue_name)
120
+ result = conn.exec_params(
121
+ 'SELECT 1 FROM pgmq.meta WHERE queue_name = $1 LIMIT 1',
122
+ [queue_name]
123
+ )
124
+ result.ntuples.positive?
125
+ end
110
126
  end
111
127
  end
112
128
  end
data/lib/pgmq/client.rb CHANGED
@@ -14,7 +14,7 @@ module PGMQ
14
14
  # password: 'postgres'
15
15
  # )
16
16
  # client.create('my_queue')
17
- # msg_id = client.send('my_queue', { data: 'value' })
17
+ # msg_id = client.produce('my_queue', '{"data":"value"}')
18
18
  # msg = client.read('my_queue', vt: 30)
19
19
  # client.delete('my_queue', msg.msg_id)
20
20
  #
@@ -27,7 +27,7 @@ module PGMQ
27
27
  # Include functional modules (order matters for discoverability)
28
28
  include Transaction # Transaction support (already existed)
29
29
  include QueueManagement # Queue lifecycle (create, drop, list)
30
- include Producer # Message sending operations
30
+ include Producer # Message producing operations
31
31
  include Consumer # Single-queue reading operations
32
32
  include MultiQueue # Multi-queue operations
33
33
  include MessageLifecycle # Message state transitions (pop, delete, archive)
@@ -12,13 +12,13 @@ module PGMQ
12
12
  #
13
13
  # @example Atomic multi-queue operations
14
14
  # client.transaction do |txn|
15
- # txn.send("orders", { order_id: 123 })
16
- # txn.send("notifications", { type: "order_created" })
15
+ # txn.produce("orders", '{"order_id":123}')
16
+ # txn.produce("notifications", '{"type":"order_created"}')
17
17
  # end
18
18
  #
19
19
  # @example Automatic rollback on error
20
20
  # client.transaction do |txn|
21
- # txn.send("orders", { order_id: 123 })
21
+ # txn.produce("orders", '{"order_id":123}')
22
22
  # raise "Error" # Both operations rolled back
23
23
  # end
24
24
  module Transaction
@@ -33,7 +33,7 @@ module PGMQ
33
33
  #
34
34
  # @example
35
35
  # client.transaction do |txn|
36
- # msg_id = txn.send("queue", { data: "test" })
36
+ # msg_id = txn.produce("queue", '{"data":"test"}')
37
37
  # txn.delete("queue", msg_id)
38
38
  # end
39
39
  def transaction
@@ -67,19 +67,6 @@ module PGMQ
67
67
  @parent.respond_to?(method, true) ? @parent.__send__(method, ...) : super
68
68
  end
69
69
 
70
- # Override Object#send to call parent's send method
71
- # @param queue_name [String] queue name
72
- # @param message [String] message as JSON string
73
- # @param delay [Integer] delay in seconds
74
- # @return [String] message ID
75
- def send(
76
- queue_name,
77
- message,
78
- delay: 0
79
- )
80
- @parent.send(queue_name, message, delay: delay)
81
- end
82
-
83
70
  # Check if method exists on parent
84
71
  # @param method [Symbol] method name
85
72
  # @param include_private [Boolean] include private methods
data/lib/pgmq/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PGMQ
4
4
  # Current version of the pgmq-ruby gem
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.0'
6
6
  end
data/lib/pgmq.rb CHANGED
@@ -33,7 +33,7 @@ loader.eager_load
33
33
  #
34
34
  # # Basic queue operations
35
35
  # client.create('orders')
36
- # msg_id = client.send('orders', { order_id: 123 })
36
+ # msg_id = client.produce('orders', '{"order_id":123}')
37
37
  # msg = client.read('orders', vt: 30)
38
38
  # client.delete('orders', msg.msg_id)
39
39
  # client.drop_queue('orders')
data/renovate.json CHANGED
@@ -3,16 +3,9 @@
3
3
  "extends": [
4
4
  "config:recommended"
5
5
  ],
6
+ "minimumReleaseAge": "7 days",
6
7
  "github-actions": {
7
8
  "enabled": true,
8
9
  "pinDigests": true
9
- },
10
- "packageRules": [
11
- {
12
- "matchManagers": [
13
- "github-actions"
14
- ],
15
- "minimumReleaseAge": "7 days"
16
- }
17
- ]
10
+ }
18
11
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgmq-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -115,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  requirements: []
118
- rubygems_version: 3.6.9
118
+ rubygems_version: 4.0.3
119
119
  specification_version: 4
120
120
  summary: Ruby client for PGMQ (Postgres Message Queue)
121
121
  test_files: []