pgmq-ruby 0.1.0 → 0.3.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.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGMQ
4
+ class Client
5
+ # Message sending operations
6
+ #
7
+ # This module handles sending messages to queues, both individual messages
8
+ # and batches. Users must serialize messages to JSON strings themselves.
9
+ module Producer
10
+ # Sends a message to a queue
11
+ #
12
+ # @param queue_name [String] name of the queue
13
+ # @param message [String] message as JSON string (for PostgreSQL JSONB)
14
+ # @param delay [Integer] delay in seconds before message becomes visible
15
+ # @return [String] message ID as string
16
+ #
17
+ # @example
18
+ # msg_id = client.send("orders", '{"order_id":123,"total":99.99}')
19
+ #
20
+ # @example With delay
21
+ # msg_id = client.send("orders", '{"data":"value"}', delay: 60)
22
+ #
23
+ # @note Users must serialize to JSON themselves. Higher-level frameworks
24
+ # should handle serialization.
25
+ def send(
26
+ queue_name,
27
+ message,
28
+ delay: 0
29
+ )
30
+ validate_queue_name!(queue_name)
31
+
32
+ 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
+ )
37
+ end
38
+
39
+ result[0]['send']
40
+ end
41
+
42
+ # Sends multiple messages to a queue in a batch
43
+ #
44
+ # @param queue_name [String] name of the queue
45
+ # @param messages [Array<Hash>] array of message payloads
46
+ # @param delay [Integer] delay in seconds before messages become visible
47
+ # @return [Array<Integer>] array of message IDs
48
+ #
49
+ # @example
50
+ # ids = client.send_batch("orders", [
51
+ # { order_id: 1 },
52
+ # { order_id: 2 },
53
+ # { order_id: 3 }
54
+ # ])
55
+ def send_batch(
56
+ queue_name,
57
+ messages,
58
+ delay: 0
59
+ )
60
+ validate_queue_name!(queue_name)
61
+ return [] if messages.empty?
62
+
63
+ # Use PostgreSQL array parameter binding for security
64
+ # PG gem will properly encode the array values
65
+ result = with_connection do |conn|
66
+ # Create array encoder for proper PostgreSQL array formatting
67
+ encoder = PG::TextEncoder::Array.new
68
+ encoded_array = encoder.encode(messages)
69
+
70
+ conn.exec_params(
71
+ 'SELECT * FROM pgmq.send_batch($1::text, $2::jsonb[], $3::integer)',
72
+ [queue_name, encoded_array, delay]
73
+ )
74
+ end
75
+
76
+ result.map { |row| row['send_batch'] }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGMQ
4
+ class Client
5
+ # Queue management operations (create, drop, list)
6
+ #
7
+ # This module handles all queue lifecycle operations including creating queues
8
+ # (standard, partitioned, unlogged), dropping queues, and listing existing queues.
9
+ module QueueManagement
10
+ # Creates a new queue
11
+ #
12
+ # @param queue_name [String] name of the queue to create
13
+ # @return [void]
14
+ # @raise [PGMQ::Errors::InvalidQueueNameError] if queue name is invalid
15
+ # @raise [PGMQ::Errors::ConnectionError] if database operation fails
16
+ #
17
+ # @example
18
+ # client.create("orders")
19
+ def create(queue_name)
20
+ validate_queue_name!(queue_name)
21
+
22
+ with_connection do |conn|
23
+ conn.exec_params('SELECT pgmq.create($1::text)', [queue_name])
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ # Creates a partitioned queue
30
+ #
31
+ # Requires pg_partman extension to be installed
32
+ #
33
+ # @param queue_name [String] name of the queue
34
+ # @param partition_interval [String] partition interval (e.g., "daily", "10000")
35
+ # @param retention_interval [String] retention interval (e.g., "7 days", "100000")
36
+ # @return [void]
37
+ #
38
+ # @example
39
+ # client.create_partitioned("big_queue",
40
+ # partition_interval: "daily",
41
+ # retention_interval: "7 days"
42
+ # )
43
+ def create_partitioned(
44
+ queue_name,
45
+ partition_interval: '10000',
46
+ retention_interval: '100000'
47
+ )
48
+ validate_queue_name!(queue_name)
49
+
50
+ with_connection do |conn|
51
+ conn.exec_params(
52
+ 'SELECT pgmq.create_partitioned($1::text, $2::text, $3::text)',
53
+ [queue_name, partition_interval, retention_interval]
54
+ )
55
+ end
56
+
57
+ nil
58
+ end
59
+
60
+ # Creates an unlogged queue for higher throughput (no crash recovery)
61
+ #
62
+ # @param queue_name [String] name of the queue
63
+ # @return [void]
64
+ #
65
+ # @example
66
+ # client.create_unlogged("fast_queue")
67
+ def create_unlogged(queue_name)
68
+ validate_queue_name!(queue_name)
69
+
70
+ with_connection do |conn|
71
+ conn.exec_params('SELECT pgmq.create_unlogged($1::text)', [queue_name])
72
+ end
73
+
74
+ nil
75
+ end
76
+
77
+ # Drops a queue and its archive table
78
+ #
79
+ # @param queue_name [String] name of the queue to drop
80
+ # @return [Boolean] true if queue was dropped
81
+ #
82
+ # @example
83
+ # client.drop_queue("old_queue")
84
+ def drop_queue(queue_name)
85
+ validate_queue_name!(queue_name)
86
+
87
+ result = with_connection do |conn|
88
+ conn.exec_params('SELECT pgmq.drop_queue($1::text)', [queue_name])
89
+ end
90
+
91
+ return false if result.ntuples.zero?
92
+
93
+ result[0]['drop_queue'] == 't'
94
+ end
95
+
96
+ # Lists all queues
97
+ #
98
+ # @return [Array<PGMQ::QueueMetadata>] array of queue metadata objects
99
+ #
100
+ # @example
101
+ # queues = client.list_queues
102
+ # queues.each { |q| puts q.queue_name }
103
+ def list_queues
104
+ result = with_connection do |conn|
105
+ conn.exec('SELECT * FROM pgmq.list_queues()')
106
+ end
107
+
108
+ result.map { |row| QueueMetadata.new(row) }
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGMQ
4
+ # Low-level client for interacting with PGMQ (Postgres Message Queue)
5
+ #
6
+ # This is a thin wrapper around PGMQ SQL functions. For higher-level
7
+ # abstractions (job processing, retries, Rails integration), use pgmq-framework.
8
+ #
9
+ # @example Basic usage
10
+ # client = PGMQ::Client.new(
11
+ # host: 'localhost',
12
+ # dbname: 'mydb',
13
+ # user: 'postgres',
14
+ # password: 'postgres'
15
+ # )
16
+ # client.create('my_queue')
17
+ # msg_id = client.send('my_queue', { data: 'value' })
18
+ # msg = client.read('my_queue', vt: 30)
19
+ # client.delete('my_queue', msg.msg_id)
20
+ #
21
+ # @example With Rails/ActiveRecord (reuses Rails connection pool)
22
+ # client = PGMQ::Client.new(-> { ActiveRecord::Base.connection.raw_connection })
23
+ #
24
+ # @example With connection string
25
+ # client = PGMQ::Client.new('postgres://localhost/mydb')
26
+ class Client
27
+ # Include functional modules (order matters for discoverability)
28
+ include Transaction # Transaction support (already existed)
29
+ include QueueManagement # Queue lifecycle (create, drop, list)
30
+ include Producer # Message sending operations
31
+ include Consumer # Single-queue reading operations
32
+ include MultiQueue # Multi-queue operations
33
+ include MessageLifecycle # Message state transitions (pop, delete, archive)
34
+ include Maintenance # Queue maintenance (purge, detach_archive)
35
+ include Metrics # Monitoring and metrics
36
+
37
+ # Default visibility timeout in seconds
38
+ DEFAULT_VT = 30
39
+
40
+ # @return [PGMQ::Connection] the connection manager
41
+ attr_reader :connection
42
+
43
+ # Creates a new PGMQ client
44
+ #
45
+ # @param conn_params [String, Hash, Proc, PGMQ::Connection, nil] connection parameters
46
+ # @param pool_size [Integer] connection pool size
47
+ # @param pool_timeout [Integer] connection pool timeout in seconds
48
+ # @param auto_reconnect [Boolean] automatically reconnect on connection errors (default: true)
49
+ #
50
+ # @example Connection string
51
+ # client = PGMQ::Client.new('postgres://user:pass@localhost/db')
52
+ #
53
+ # @example Connection hash
54
+ # client = PGMQ::Client.new(host: 'localhost', dbname: 'mydb', user: 'postgres')
55
+ #
56
+ # @example Inject existing connection (for Rails)
57
+ # client = PGMQ::Client.new(-> { ActiveRecord::Base.connection.raw_connection })
58
+ #
59
+ # @example Disable auto-reconnect
60
+ # client = PGMQ::Client.new(auto_reconnect: false)
61
+ def initialize(
62
+ conn_params = nil,
63
+ pool_size: Connection::DEFAULT_POOL_SIZE,
64
+ pool_timeout: Connection::DEFAULT_POOL_TIMEOUT,
65
+ auto_reconnect: true
66
+ )
67
+ @connection = if conn_params.is_a?(Connection)
68
+ conn_params
69
+ else
70
+ Connection.new(
71
+ conn_params,
72
+ pool_size: pool_size,
73
+ pool_timeout: pool_timeout,
74
+ auto_reconnect: auto_reconnect
75
+ )
76
+ end
77
+ end
78
+
79
+ # Closes all connections in the pool
80
+ # @return [void]
81
+ def close
82
+ @connection.close
83
+ end
84
+
85
+ # Returns connection pool statistics
86
+ #
87
+ # @return [Hash] statistics about the connection pool
88
+ # @example
89
+ # stats = client.stats
90
+ # # => { size: 5, available: 3 }
91
+ def stats
92
+ @connection.stats
93
+ end
94
+
95
+ private
96
+
97
+ # Executes a block with a database connection
98
+ # @yield [PG::Connection] database connection
99
+ # @return [Object] result of the block
100
+ def with_connection(&)
101
+ @connection.with_connection(&)
102
+ end
103
+
104
+ # Validates a queue name
105
+ # @param queue_name [String] queue name to validate
106
+ # @return [void]
107
+ # @raise [PGMQ::Errors::InvalidQueueNameError] if name is invalid
108
+ def validate_queue_name!(queue_name)
109
+ if queue_name.nil? || queue_name.to_s.strip.empty?
110
+ raise(
111
+ Errors::InvalidQueueNameError,
112
+ 'Queue name cannot be empty'
113
+ )
114
+ end
115
+
116
+ # PGMQ creates tables with prefixes (pgmq.q_<name>, pgmq.a_<name>)
117
+ # PostgreSQL has a 63-character limit for identifiers, but PGMQ enforces 48
118
+ # to account for prefixes and potential suffixes
119
+ if queue_name.to_s.length >= 48
120
+ raise(
121
+ Errors::InvalidQueueNameError,
122
+ "Queue name '#{queue_name}' exceeds maximum length of 48 characters " \
123
+ "(current length: #{queue_name.to_s.length})"
124
+ )
125
+ end
126
+
127
+ # PostgreSQL identifier rules: start with letter or underscore,
128
+ # contain only letters, digits, underscores
129
+ return if queue_name.to_s.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*\z/)
130
+
131
+ raise(
132
+ Errors::InvalidQueueNameError,
133
+ "Invalid queue name '#{queue_name}': must start with a letter or underscore " \
134
+ 'and contain only letters, digits, and underscores'
135
+ )
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pg'
4
+ require 'connection_pool'
5
+
6
+ module PGMQ
7
+ # Manages database connections for PGMQ
8
+ #
9
+ # Supports multiple connection strategies:
10
+ # - Connection strings
11
+ # - Hash of connection parameters
12
+ # - Callable objects (for Rails ActiveRecord integration)
13
+ #
14
+ # @example With connection string
15
+ # conn = PGMQ::Connection.new("postgres://localhost/mydb")
16
+ #
17
+ # @example With connection hash
18
+ # conn = PGMQ::Connection.new(host: 'localhost', dbname: 'mydb')
19
+ #
20
+ # @example With Rails ActiveRecord (reuses Rails connection pool)
21
+ # conn = PGMQ::Connection.new(-> { ActiveRecord::Base.connection.raw_connection })
22
+ class Connection
23
+ # Default connection pool size
24
+ DEFAULT_POOL_SIZE = 5
25
+
26
+ # Default connection pool timeout in seconds
27
+ DEFAULT_POOL_TIMEOUT = 5
28
+
29
+ # @return [ConnectionPool] the connection pool
30
+ attr_reader :pool
31
+
32
+ # Creates a new connection manager
33
+ #
34
+ # @param conn_params [String, Hash, Proc] connection parameters or callable
35
+ # @param pool_size [Integer] size of the connection pool
36
+ # @param pool_timeout [Integer] connection pool timeout in seconds
37
+ # @param auto_reconnect [Boolean] automatically reconnect on connection errors
38
+ # @raise [PGMQ::Errors::ConfigurationError] if conn_params is nil or invalid
39
+ def initialize(
40
+ conn_params,
41
+ pool_size: DEFAULT_POOL_SIZE,
42
+ pool_timeout: DEFAULT_POOL_TIMEOUT,
43
+ auto_reconnect: true
44
+ )
45
+ if conn_params.nil?
46
+ raise(
47
+ PGMQ::Errors::ConfigurationError,
48
+ 'Connection parameters are required'
49
+ )
50
+ end
51
+
52
+ @conn_params = normalize_connection_params(conn_params)
53
+ @pool_size = pool_size
54
+ @pool_timeout = pool_timeout
55
+ @auto_reconnect = auto_reconnect
56
+ @pool = create_pool
57
+ end
58
+
59
+ # Executes a block with a connection from the pool
60
+ #
61
+ # @yield [PG::Connection] database connection
62
+ # @return [Object] result of the block
63
+ # @raise [PGMQ::Errors::ConnectionError] if connection fails
64
+ def with_connection
65
+ retries = @auto_reconnect ? 1 : 0
66
+ attempts = 0
67
+
68
+ begin
69
+ @pool.with do |conn|
70
+ # Health check: verify connection is alive
71
+ verify_connection!(conn) if @auto_reconnect
72
+
73
+ yield conn
74
+ end
75
+ rescue PG::Error => e
76
+ attempts += 1
77
+
78
+ # If connection error and auto_reconnect enabled, try once more
79
+ retry if attempts <= retries && connection_lost_error?(e)
80
+
81
+ raise PGMQ::Errors::ConnectionError, "Database connection error: #{e.message}"
82
+ rescue ConnectionPool::TimeoutError => e
83
+ raise PGMQ::Errors::ConnectionError, "Connection pool timeout: #{e.message}"
84
+ rescue ConnectionPool::PoolShuttingDownError => e
85
+ raise PGMQ::Errors::ConnectionError, "Connection pool is closed: #{e.message}"
86
+ end
87
+ end
88
+
89
+ # Closes all connections in the pool
90
+ # @return [void]
91
+ def close
92
+ @pool.shutdown { |conn| conn.close unless conn.finished? }
93
+ end
94
+
95
+ # Returns connection pool statistics
96
+ #
97
+ # @return [Hash] statistics about the connection pool
98
+ # @example
99
+ # stats = connection.stats
100
+ # # => { size: 5, available: 3 }
101
+ def stats
102
+ {
103
+ size: @pool_size,
104
+ available: @pool.available
105
+ }
106
+ end
107
+
108
+ private
109
+
110
+ # Checks if the error indicates a lost connection
111
+ # @param error [PG::Error] the error to check
112
+ # @return [Boolean] true if connection was lost
113
+ def connection_lost_error?(error)
114
+ # Common connection lost errors
115
+ lost_connection_messages = [
116
+ 'server closed the connection',
117
+ 'connection not open',
118
+ 'no connection to the server',
119
+ 'terminating connection',
120
+ 'connection to server was lost',
121
+ 'could not receive data from server'
122
+ ]
123
+
124
+ message = error.message.downcase
125
+ lost_connection_messages.any? { |pattern| message.include?(pattern) }
126
+ end
127
+
128
+ # Verifies a connection is alive and working
129
+ # @param conn [PG::Connection] connection to verify
130
+ # @raise [PG::Error] if connection is not working
131
+ def verify_connection!(conn)
132
+ # Quick check - is connection object in bad state?
133
+ return unless conn.finished?
134
+
135
+ # Connection is finished/closed, try to reset it
136
+ conn.reset
137
+ end
138
+
139
+ # Normalizes various connection parameter formats
140
+ # @param params [String, Hash, Proc]
141
+ # @return [Hash, Proc]
142
+ # @raise [PGMQ::Errors::ConfigurationError] if params format is invalid
143
+ def normalize_connection_params(params)
144
+ return params if params.respond_to?(:call) # Callable (e.g., proc for Rails)
145
+ return parse_connection_string(params) if params.is_a?(String)
146
+ return params if params.is_a?(Hash) && !params.empty?
147
+
148
+ raise PGMQ::Errors::ConfigurationError, 'Invalid connection parameters format'
149
+ end
150
+
151
+ # Parses a PostgreSQL connection string
152
+ # @param conn_string [String] connection string (e.g., "postgres://user:pass@host/db")
153
+ # @return [Hash] connection parameters
154
+ def parse_connection_string(conn_string)
155
+ # PG::Connection.conninfo_parse is available in pg >= 0.20
156
+ if PG::Connection.respond_to?(:conninfo_parse)
157
+ PG::Connection.conninfo_parse(conn_string).each_with_object({}) do |info, hash|
158
+ hash[info[:keyword].to_sym] = info[:val] if info[:val]
159
+ end
160
+ else
161
+ # Fallback: pass the string directly and let PG handle it
162
+ { conninfo: conn_string }
163
+ end
164
+ rescue PG::Error => e
165
+ raise PGMQ::Errors::ConfigurationError, "Invalid connection string: #{e.message}"
166
+ end
167
+
168
+ # Creates the connection pool
169
+ # @return [ConnectionPool]
170
+ def create_pool
171
+ params = @conn_params
172
+
173
+ ConnectionPool.new(size: @pool_size, timeout: @pool_timeout) do
174
+ create_connection(params)
175
+ end
176
+ rescue StandardError => e
177
+ raise PGMQ::Errors::ConnectionError, "Failed to create connection pool: #{e.message}"
178
+ end
179
+
180
+ # Creates a single database connection
181
+ # @param params [Hash, Proc] connection parameters or callable
182
+ # @return [PG::Connection]
183
+ def create_connection(params)
184
+ # If we have a callable (e.g., for Rails), call it to get the connection
185
+ return params.call if params.respond_to?(:call)
186
+
187
+ # Create new connection from parameters
188
+ # Low-level library: return all values as strings from PostgreSQL
189
+ # No automatic type conversion - let higher-level frameworks handle parsing
190
+ # conn.type_map_for_results intentionally NOT set
191
+ PG.connect(params[:conninfo] || params)
192
+ rescue PG::Error => e
193
+ raise PGMQ::Errors::ConnectionError, "Failed to connect to database: #{e.message}"
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGMQ
4
+ # PGMQ errors namespace
5
+ module Errors
6
+ # Base error class for all PGMQ errors
7
+ class BaseError < StandardError; end
8
+
9
+ # Raised when connection to PostgreSQL fails or is lost
10
+ class ConnectionError < BaseError; end
11
+
12
+ # Raised when a queue operation is attempted on a non-existent queue
13
+ class QueueNotFoundError < BaseError; end
14
+
15
+ # Raised when a message cannot be found
16
+ class MessageNotFoundError < BaseError; end
17
+
18
+ # Raised when message serialization fails
19
+ class SerializationError < BaseError; end
20
+
21
+ # Raised when message deserialization fails
22
+ class DeserializationError < BaseError; end
23
+
24
+ # Raised when configuration is invalid
25
+ class ConfigurationError < BaseError; end
26
+
27
+ # Raised when an invalid queue name is provided
28
+ class InvalidQueueNameError < BaseError; end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGMQ
4
+ # Represents a message read from a PGMQ queue
5
+ #
6
+ # Returns raw values from PostgreSQL without transformation.
7
+ # Higher-level frameworks should handle parsing, deserialization, etc.
8
+ #
9
+ # @example Reading a message (raw values)
10
+ # msg = client.read("my_queue", vt: 30)
11
+ # puts msg.msg_id # => "123" (String from PG)
12
+ # puts msg.read_ct # => "1" (String from PG)
13
+ # puts msg.enqueued_at # => "2025-01-15 10:30:00+00" (String from PG)
14
+ # puts msg.vt # => "2025-01-15 10:30:30+00" (String from PG)
15
+ # puts msg.message # => "{\"order_id\":456}" (Raw JSONB string)
16
+ # puts msg.headers # => "{\"trace_id\":\"abc123\"}" (Raw JSONB string, optional)
17
+ # puts msg.queue_name # => "my_queue" (only present for multi-queue operations)
18
+ class Message < Data.define(
19
+ :msg_id, :read_ct, :enqueued_at, :vt, :message, :headers, :queue_name
20
+ )
21
+ class << self
22
+ # Creates a new Message from a database row
23
+ # @param row [Hash] database row from PG result
24
+ # @return [Message]
25
+ def new(row, **)
26
+ # Return raw values as-is from PostgreSQL
27
+ # No parsing, no deserialization, no transformation
28
+ # The pg gem returns JSONB as String by default
29
+ super(
30
+ msg_id: row['msg_id'],
31
+ read_ct: row['read_ct'],
32
+ enqueued_at: row['enqueued_at'],
33
+ vt: row['vt'],
34
+ message: row['message'],
35
+ headers: row['headers'], # JSONB column for metadata (optional)
36
+ queue_name: row['queue_name'] # nil for single-queue operations
37
+ )
38
+ end
39
+ end
40
+
41
+ # Alias for msg_id (common in messaging systems)
42
+ # @return [String]
43
+ alias id msg_id
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ module PGMQ
6
+ # Represents metrics for a PGMQ queue
7
+ #
8
+ # @example Getting queue metrics
9
+ # metrics = client.metrics("my_queue")
10
+ # puts metrics.queue_length # => 42
11
+ # puts metrics.oldest_msg_age_sec # => 3600
12
+ class Metrics < Data.define(
13
+ :queue_name,
14
+ :queue_length,
15
+ :newest_msg_age_sec,
16
+ :oldest_msg_age_sec,
17
+ :total_messages,
18
+ :scrape_time
19
+ )
20
+ class << self
21
+ # Creates a new Metrics object from a database row
22
+ # @param row [Hash] database row from PG result
23
+ # @return [Metrics]
24
+ def new(row, **)
25
+ # Return raw values as-is from PostgreSQL
26
+ super(
27
+ queue_name: row['queue_name'],
28
+ queue_length: row['queue_length'],
29
+ newest_msg_age_sec: row['newest_msg_age_sec'],
30
+ oldest_msg_age_sec: row['oldest_msg_age_sec'],
31
+ total_messages: row['total_messages'],
32
+ scrape_time: row['scrape_time']
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ module PGMQ
6
+ # Represents metadata about a PGMQ queue
7
+ #
8
+ # @example Listing queues
9
+ # queues = client.list_queues
10
+ # queues.each do |q|
11
+ # puts "#{q.queue_name} (partitioned: #{q.is_partitioned})"
12
+ # end
13
+ class QueueMetadata < Data.define(:queue_name, :created_at, :is_partitioned, :is_unlogged)
14
+ class << self
15
+ # Creates a new QueueMetadata object from a database row
16
+ # @param row [Hash] database row from PG result
17
+ # @return [QueueMetadata]
18
+ def new(row, **)
19
+ # Return raw values as-is from PostgreSQL
20
+ super(
21
+ queue_name: row['queue_name'],
22
+ created_at: row['created_at'],
23
+ is_partitioned: row['is_partitioned'],
24
+ is_unlogged: row['is_unlogged']
25
+ )
26
+ end
27
+ end
28
+
29
+ # Alias for is_partitioned
30
+ # @return [String] 't' or 'f' from PostgreSQL
31
+ alias partitioned? is_partitioned
32
+
33
+ # Alias for is_unlogged
34
+ # @return [String] 't' or 'f' from PostgreSQL
35
+ alias unlogged? is_unlogged
36
+ end
37
+ end