trigv 1.0.0 → 1.1.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/CHANGELOG.md +30 -0
- data/README.md +27 -11
- data/lib/trigv/client.rb +37 -32
- data/lib/trigv/errors.rb +41 -5
- data/lib/trigv/validation.rb +1 -1
- data/lib/trigv/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bec937b624e4c89520252f25849322469e32f51a74dadb4fe98d5aff5bce3756
|
|
4
|
+
data.tar.gz: 0cd521c1fffedd723fc607e23a865749a41e9c167f4e7804e867b48fb0d52427
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c48be29cd4ca018bc3a475ddd7113fc43558e0629dbd1a3088898f54f737c61a356eff543654ad9917e4b9376cf35bedf2c2d9e95f3018172a752a8a72505b0
|
|
7
|
+
data.tar.gz: 1853fdeac0f0b339133f666db263af0abc40e6a60a8e665526646cd5ea09e5ebf312b27e1ebee17ceccf5c74dc789d15e5c6b856b40a99196ec2e6c21ab9a732
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.1] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Changed: README and package metadata link to trigv.com docs
|
|
13
|
+
|
|
14
|
+
## [1.1.0] - 2026-11-01
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Default `base_url` is now `https://api.trigv.com`; paths are `/v1/events` and `/v1/connection`. A base URL ending in `/api` keeps working.
|
|
19
|
+
- `public_id` values are 26 character ULIDs (no `evt_` prefix).
|
|
20
|
+
- Rate limit is 60 requests per minute per workspace.
|
|
21
|
+
- Invalid JSON bodies return 422 and map to `Trigv::ValidationError`.
|
|
22
|
+
- A `Retry-After` longer than the client `timeout` is not waited for: the 409, 429 or 503 error is raised at once.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `Trigv::ConflictError` (409) and `Trigv::ServiceUnavailableError` (503), retried with `Retry-After` when an `idempotency_key` is set.
|
|
27
|
+
- `request_id` on every API error (from the `X-Request-ID` header).
|
|
28
|
+
- `request_id` on `Trigv::SendEventResult` and `Trigv::ConnectionResult` (`nil` if the header is missing).
|
|
29
|
+
|
|
30
|
+
### Deprecated
|
|
31
|
+
|
|
32
|
+
- `Trigv::RateLimitError#retryable` (always `true`), the `retryable:` argument (ignored) and `Trigv::Errors.monthly_limit_message?` (always `false`) are kept for compatibility.
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
|
|
36
|
+
- Monthly event cap handling.
|
|
37
|
+
|
|
8
38
|
## [1.0.0] - 2026-07-07
|
|
9
39
|
|
|
10
40
|
### Added
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# trigv
|
|
2
2
|
|
|
3
|
+
[Docs](https://trigv.com/docs/sdks/ruby/) | [API reference](https://trigv.com/docs/api/) | [Free tools](https://trigv.com/tools/) | [Get an API key](https://app.trigv.com/register)
|
|
4
|
+
|
|
3
5
|
Official Trigv SDK for Ruby. Send notification events from scripts, servers, and CI pipelines.
|
|
4
6
|
|
|
5
7
|
## What Trigv is
|
|
@@ -11,7 +13,7 @@ Trigv delivers developer notifications to your team's devices. Your backend send
|
|
|
11
13
|
Add to your Gemfile:
|
|
12
14
|
|
|
13
15
|
```ruby
|
|
14
|
-
gem 'trigv', '~>
|
|
16
|
+
gem 'trigv', '~> 1.1'
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
Then run:
|
|
@@ -57,21 +59,22 @@ result = client.send_event(
|
|
|
57
59
|
)
|
|
58
60
|
|
|
59
61
|
puts result.event.public_id
|
|
62
|
+
puts result.request_id # X-Request-ID header, or nil if missing
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
## Event options
|
|
63
66
|
|
|
64
67
|
| Field | Required | Description |
|
|
65
68
|
|-------|:--------:|-------------|
|
|
66
|
-
| `channel` | Yes | Channel slug (e.g. `general
|
|
67
|
-
| `title` | Yes | Notification title |
|
|
68
|
-
| `description` | No | Body text |
|
|
69
|
-
| `image_url` | No |
|
|
69
|
+
| `channel` | Yes | Channel slug (e.g. `general`, max 120 characters) |
|
|
70
|
+
| `title` | Yes | Notification title (max 255 characters) |
|
|
71
|
+
| `description` | No | Body text (max 1000 characters) |
|
|
72
|
+
| `image_url` | No | Image URL (max 2048 characters; not stored server-side) |
|
|
70
73
|
| `url` | No | Destination URL for the notification (max 2048 characters; not stored server-side) |
|
|
71
74
|
| `level` | No | `info`, `success`, `warning`, `error` (default: `info`) |
|
|
72
75
|
| `delivery_urgency` | No | `standard` or `time_sensitive` (default: `standard`) |
|
|
73
|
-
| `event_type` | No | Free-form label (e.g. `deploy.completed
|
|
74
|
-
| `idempotency_key` | No | Dedup key per workspace |
|
|
76
|
+
| `event_type` | No | Free-form label (e.g. `deploy.completed`, max 120 characters) |
|
|
77
|
+
| `idempotency_key` | No | Dedup key per workspace (max 190 characters) |
|
|
75
78
|
|
|
76
79
|
## Levels
|
|
77
80
|
|
|
@@ -87,7 +90,7 @@ puts result.event.public_id
|
|
|
87
90
|
|
|
88
91
|
## Idempotency
|
|
89
92
|
|
|
90
|
-
When you set `idempotency_key`, retries with the same key return the existing event (`duplicate: true`, HTTP 200) without billing again.
|
|
93
|
+
When you set `idempotency_key`, retries with the same key return the existing event (`duplicate: true`, HTTP 200, message `already stored`) without billing again. A 409 or 503 is retried with the same key, honouring `Retry-After`.
|
|
91
94
|
|
|
92
95
|
```ruby
|
|
93
96
|
result = client.send_event(
|
|
@@ -108,17 +111,29 @@ rescue Trigv::ValidationError => e
|
|
|
108
111
|
puts e.errors
|
|
109
112
|
rescue Trigv::NotFoundError
|
|
110
113
|
puts 'Channel not found'
|
|
111
|
-
rescue Trigv::RateLimitError => e
|
|
112
|
-
puts
|
|
114
|
+
rescue Trigv::RateLimitError, Trigv::ConflictError, Trigv::ServiceUnavailableError => e
|
|
115
|
+
puts "Retry in #{e.retry_after_seconds}s (request id #{e.request_id})"
|
|
113
116
|
end
|
|
114
117
|
```
|
|
115
118
|
|
|
119
|
+
| Error | HTTP | Retried (with `idempotency_key`) |
|
|
120
|
+
|-------|------|:---:|
|
|
121
|
+
| `Trigv::AuthenticationError` | 401 | No |
|
|
122
|
+
| `Trigv::AuthorizationError` | 403 | No |
|
|
123
|
+
| `Trigv::NotFoundError` | 404 | No |
|
|
124
|
+
| `Trigv::ConflictError` | 409, same key being delivered | Yes |
|
|
125
|
+
| `Trigv::ValidationError` | 422 | No |
|
|
126
|
+
| `Trigv::RateLimitError` | 429, 60 requests per minute per workspace | Yes |
|
|
127
|
+
| `Trigv::ServiceUnavailableError` | 503, push unavailable | Yes |
|
|
128
|
+
|
|
129
|
+
Retries wait for `Retry-After`. If `Retry-After` is longer than the client `timeout`, the error is raised at once. 502 and 504 responses, network errors and timeouts are also retried (with an `idempotency_key`), and so is `verify_connection`. Every error has `request_id`, the `X-Request-ID` header. Successful results have `request_id` too (`nil` if missing). Quote it to support.
|
|
130
|
+
|
|
116
131
|
## Configuration
|
|
117
132
|
|
|
118
133
|
| Option | Default | Description |
|
|
119
134
|
|--------|---------|-------------|
|
|
120
135
|
| `api_key` | `TRIGV_API_KEY` env | Workspace API key |
|
|
121
|
-
| `base_url` | `https://api.trigv.com
|
|
136
|
+
| `base_url` | `https://api.trigv.com` | API base URL. A URL ending in `/api` still works |
|
|
122
137
|
| `timeout` | `30` | Request timeout (seconds) |
|
|
123
138
|
| `max_retries` | `2` | Retries for retryable errors |
|
|
124
139
|
|
|
@@ -127,6 +142,7 @@ end
|
|
|
127
142
|
```ruby
|
|
128
143
|
connection = client.verify_connection
|
|
129
144
|
puts connection.workspace.name
|
|
145
|
+
puts connection.request_id # X-Request-ID header, or nil if missing
|
|
130
146
|
```
|
|
131
147
|
|
|
132
148
|
## Examples
|
data/lib/trigv/client.rb
CHANGED
|
@@ -12,11 +12,11 @@ module Trigv
|
|
|
12
12
|
keyword_init: true
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
SendEventResult = Struct.new(:event, :duplicate, keyword_init: true)
|
|
15
|
+
SendEventResult = Struct.new(:event, :duplicate, :request_id, keyword_init: true)
|
|
16
16
|
|
|
17
17
|
ConnectionWorkspace = Struct.new(:public_id, :name, keyword_init: true)
|
|
18
18
|
ConnectionApiKey = Struct.new(:public_id, :name, :prefix, keyword_init: true)
|
|
19
|
-
ConnectionResult = Struct.new(:workspace, :api_key, keyword_init: true)
|
|
19
|
+
ConnectionResult = Struct.new(:workspace, :api_key, :request_id, keyword_init: true)
|
|
20
20
|
|
|
21
21
|
class Client
|
|
22
22
|
attr_reader :api_key, :base_url, :timeout, :max_retries
|
|
@@ -43,6 +43,11 @@ module Trigv
|
|
|
43
43
|
"trigv-ruby/#{VERSION}"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Keep the API key out of logs, consoles and exception reports that call inspect.
|
|
47
|
+
def inspect
|
|
48
|
+
"#<#{self.class.name} base_url=#{base_url.inspect} timeout=#{timeout} max_retries=#{max_retries}>"
|
|
49
|
+
end
|
|
50
|
+
|
|
46
51
|
def send_event(**params)
|
|
47
52
|
body = Validation.validate_send_event_params(params)
|
|
48
53
|
has_idempotency = body.key?('idempotency_key')
|
|
@@ -50,7 +55,7 @@ module Trigv
|
|
|
50
55
|
request_with_retry(:post, '/v1/events', body, allow_retry: has_idempotency) do |status, parsed, headers|
|
|
51
56
|
if [200, 202].include?(status) && parsed&.dig('event')
|
|
52
57
|
event = parse_event(parsed['event'])
|
|
53
|
-
SendEventResult.new(event: event, duplicate: status == 200)
|
|
58
|
+
SendEventResult.new(event: event, duplicate: status == 200, request_id: headers['x-request-id'])
|
|
54
59
|
else
|
|
55
60
|
error_from_response(status, parsed, headers)
|
|
56
61
|
end
|
|
@@ -60,7 +65,7 @@ module Trigv
|
|
|
60
65
|
def verify_connection
|
|
61
66
|
request_with_retry(:get, '/v1/connection', nil, allow_retry: true) do |status, parsed, headers|
|
|
62
67
|
if status == 200 && parsed&.dig('workspace') && parsed['api_key']
|
|
63
|
-
parse_connection_result(parsed)
|
|
68
|
+
parse_connection_result(parsed, headers['x-request-id'])
|
|
64
69
|
else
|
|
65
70
|
error_from_response(status, parsed, headers)
|
|
66
71
|
end
|
|
@@ -81,6 +86,9 @@ module Trigv
|
|
|
81
86
|
raise e unless retryable_error?(e)
|
|
82
87
|
|
|
83
88
|
wait = retry_wait_seconds(e, attempt)
|
|
89
|
+
# A wait longer than the timeout is not worth sleeping for: raise the real error.
|
|
90
|
+
raise e if wait > timeout && e.respond_to?(:retry_after_seconds) && e.retry_after_seconds
|
|
91
|
+
|
|
84
92
|
sleep([wait, timeout].min)
|
|
85
93
|
end
|
|
86
94
|
|
|
@@ -88,7 +96,7 @@ module Trigv
|
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
def retry_wait_seconds(error, attempt)
|
|
91
|
-
return error.retry_after_seconds if error.
|
|
99
|
+
return error.retry_after_seconds if error.respond_to?(:retry_after_seconds) && error.retry_after_seconds
|
|
92
100
|
|
|
93
101
|
(0.5 * (2**attempt)) + (rand * 0.25)
|
|
94
102
|
end
|
|
@@ -97,8 +105,8 @@ module Trigv
|
|
|
97
105
|
case error
|
|
98
106
|
when ValidationError, AuthenticationError, AuthorizationError, NotFoundError
|
|
99
107
|
false
|
|
100
|
-
when RateLimitError
|
|
101
|
-
|
|
108
|
+
when RateLimitError, ConflictError, ServiceUnavailableError
|
|
109
|
+
true
|
|
102
110
|
when ApiError
|
|
103
111
|
retryable_status?(error.status_code)
|
|
104
112
|
when NetworkError, TimeoutError
|
|
@@ -109,7 +117,7 @@ module Trigv
|
|
|
109
117
|
end
|
|
110
118
|
|
|
111
119
|
def retryable_status?(status)
|
|
112
|
-
[
|
|
120
|
+
[502, 504].include?(status)
|
|
113
121
|
end
|
|
114
122
|
|
|
115
123
|
def request_once(method, path, body)
|
|
@@ -123,8 +131,7 @@ module Trigv
|
|
|
123
131
|
[response.code.to_i, parsed, response_headers]
|
|
124
132
|
rescue Net::OpenTimeout, Net::ReadTimeout, Timeout::Error
|
|
125
133
|
raise TimeoutError
|
|
126
|
-
rescue
|
|
127
|
-
RateLimitError, ApiError, TimeoutError
|
|
134
|
+
rescue Error
|
|
128
135
|
raise
|
|
129
136
|
rescue StandardError => e
|
|
130
137
|
raise NetworkError.new(e.message, e)
|
|
@@ -166,7 +173,8 @@ module Trigv
|
|
|
166
173
|
def parse_json(text)
|
|
167
174
|
return nil if text.nil? || text.empty?
|
|
168
175
|
|
|
169
|
-
JSON.parse(text)
|
|
176
|
+
parsed = JSON.parse(text)
|
|
177
|
+
parsed.is_a?(Hash) ? parsed : nil
|
|
170
178
|
rescue JSON::ParserError
|
|
171
179
|
nil
|
|
172
180
|
end
|
|
@@ -183,7 +191,7 @@ module Trigv
|
|
|
183
191
|
)
|
|
184
192
|
end
|
|
185
193
|
|
|
186
|
-
def parse_connection_result(parsed)
|
|
194
|
+
def parse_connection_result(parsed, request_id = nil)
|
|
187
195
|
workspace_data = parsed['workspace']
|
|
188
196
|
api_key_data = parsed['api_key']
|
|
189
197
|
|
|
@@ -202,7 +210,8 @@ module Trigv
|
|
|
202
210
|
|
|
203
211
|
ConnectionResult.new(
|
|
204
212
|
workspace: ConnectionWorkspace.new(**symbolize_keys(workspace_data)),
|
|
205
|
-
api_key: ConnectionApiKey.new(**symbolize_keys(api_key_data))
|
|
213
|
+
api_key: ConnectionApiKey.new(**symbolize_keys(api_key_data)),
|
|
214
|
+
request_id: request_id
|
|
206
215
|
)
|
|
207
216
|
rescue ArgumentError, KeyError, TypeError
|
|
208
217
|
raise ValidationError.new('Invalid connection response payload', {})
|
|
@@ -214,26 +223,22 @@ module Trigv
|
|
|
214
223
|
|
|
215
224
|
def error_from_response(status, body, headers = {})
|
|
216
225
|
message = body&.fetch('message', nil) || "Trigv API returned HTTP #{status}"
|
|
226
|
+
retry_after = Retry.parse_retry_after(headers['retry-after'])
|
|
227
|
+
|
|
228
|
+
error =
|
|
229
|
+
case status
|
|
230
|
+
when 401 then AuthenticationError.new(message)
|
|
231
|
+
when 403 then AuthorizationError.new(message)
|
|
232
|
+
when 404 then NotFoundError.new(message)
|
|
233
|
+
when 409 then ConflictError.new(message, retry_after_seconds: retry_after)
|
|
234
|
+
when 422 then ValidationError.new(message, body&.fetch('errors', {}) || {})
|
|
235
|
+
when 429 then RateLimitError.new(message, retry_after_seconds: retry_after)
|
|
236
|
+
when 503 then ServiceUnavailableError.new(message, retry_after_seconds: retry_after)
|
|
237
|
+
else ApiError.new(message, status, body)
|
|
238
|
+
end
|
|
217
239
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
raise AuthenticationError, message
|
|
221
|
-
when 403
|
|
222
|
-
raise AuthorizationError, message
|
|
223
|
-
when 404
|
|
224
|
-
raise NotFoundError, message
|
|
225
|
-
when 422
|
|
226
|
-
raise ValidationError.new(message, body&.fetch('errors', {}) || {})
|
|
227
|
-
when 429
|
|
228
|
-
retry_after = Retry.parse_retry_after(headers['retry-after'])
|
|
229
|
-
raise RateLimitError.new(
|
|
230
|
-
message,
|
|
231
|
-
retryable: !Errors.monthly_limit_message?(message),
|
|
232
|
-
retry_after_seconds: retry_after
|
|
233
|
-
)
|
|
234
|
-
else
|
|
235
|
-
raise ApiError.new(message, status, body)
|
|
236
|
-
end
|
|
240
|
+
error.request_id = headers['x-request-id']
|
|
241
|
+
raise error
|
|
237
242
|
end
|
|
238
243
|
end
|
|
239
244
|
end
|
data/lib/trigv/errors.rb
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Trigv
|
|
4
4
|
class Error < StandardError
|
|
5
|
+
# Value of the X-Request-ID response header, when the API answered.
|
|
6
|
+
attr_accessor :request_id
|
|
7
|
+
|
|
5
8
|
def initialize(message = nil)
|
|
6
9
|
super
|
|
7
10
|
end
|
|
@@ -44,12 +47,42 @@ module Trigv
|
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
class RateLimitError < Error
|
|
47
|
-
attr_reader :status_code, :
|
|
50
|
+
attr_reader :status_code, :retry_after_seconds
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
# +retryable:+ is accepted and ignored so code written for 1.0 keeps working.
|
|
53
|
+
def initialize(message, retry_after_seconds: nil, retryable: true) # rubocop:disable Lint/UnusedMethodArgument
|
|
50
54
|
super(message)
|
|
51
55
|
@status_code = 429
|
|
52
|
-
@
|
|
56
|
+
@retry_after_seconds = retry_after_seconds
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Always true.
|
|
60
|
+
#
|
|
61
|
+
# @deprecated Kept so code written for 1.0 still works. Every 429 is a rate limit:
|
|
62
|
+
# wait +retry_after_seconds+ and try again. There is no monthly cap any more.
|
|
63
|
+
def retryable # rubocop:disable Naming/PredicateMethod
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# HTTP 409: another request is delivering the same idempotency_key.
|
|
69
|
+
class ConflictError < Error
|
|
70
|
+
attr_reader :status_code, :retry_after_seconds
|
|
71
|
+
|
|
72
|
+
def initialize(message, retry_after_seconds: nil)
|
|
73
|
+
super(message)
|
|
74
|
+
@status_code = 409
|
|
75
|
+
@retry_after_seconds = retry_after_seconds
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# HTTP 503: push delivery is unavailable. Retry with the same idempotency_key.
|
|
80
|
+
class ServiceUnavailableError < Error
|
|
81
|
+
attr_reader :status_code, :retry_after_seconds
|
|
82
|
+
|
|
83
|
+
def initialize(message, retry_after_seconds: nil)
|
|
84
|
+
super(message)
|
|
85
|
+
@status_code = 503
|
|
53
86
|
@retry_after_seconds = retry_after_seconds
|
|
54
87
|
end
|
|
55
88
|
end
|
|
@@ -82,8 +115,11 @@ module Trigv
|
|
|
82
115
|
module Errors
|
|
83
116
|
module_function
|
|
84
117
|
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
# Always false.
|
|
119
|
+
#
|
|
120
|
+
# @deprecated Kept so code written for 1.0 still works. The monthly event cap was removed.
|
|
121
|
+
def monthly_limit_message?(_message)
|
|
122
|
+
false
|
|
87
123
|
end
|
|
88
124
|
end
|
|
89
125
|
end
|
data/lib/trigv/validation.rb
CHANGED
data/lib/trigv/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trigv
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Trigv
|
|
@@ -26,12 +26,15 @@ files:
|
|
|
26
26
|
- lib/trigv/retry.rb
|
|
27
27
|
- lib/trigv/validation.rb
|
|
28
28
|
- lib/trigv/version.rb
|
|
29
|
-
homepage: https://
|
|
29
|
+
homepage: https://trigv.com
|
|
30
30
|
licenses:
|
|
31
31
|
- MIT
|
|
32
32
|
metadata:
|
|
33
|
-
homepage_uri: https://
|
|
33
|
+
homepage_uri: https://trigv.com
|
|
34
|
+
documentation_uri: https://trigv.com/docs/sdks/ruby/
|
|
34
35
|
source_code_uri: https://github.com/Trigv/trigv-ruby
|
|
36
|
+
bug_tracker_uri: https://github.com/Trigv/trigv-ruby/issues
|
|
37
|
+
changelog_uri: https://github.com/Trigv/trigv-ruby/blob/main/CHANGELOG.md
|
|
35
38
|
rubygems_mfa_required: 'true'
|
|
36
39
|
rdoc_options: []
|
|
37
40
|
require_paths:
|