jsonrpc-middleware 0.1.0 → 0.2.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.aiexclude +4 -0
  3. data/.claude/commands/document.md +105 -0
  4. data/.claude/docs/yard.md +602 -0
  5. data/.claude/settings.local.json +2 -1
  6. data/.env.example +5 -0
  7. data/CHANGELOG.md +22 -2
  8. data/CLAUDE.md +114 -0
  9. data/README.md +42 -102
  10. data/Rakefile +59 -1
  11. data/examples/README.md +37 -0
  12. data/examples/procedures.rb +6 -1
  13. data/examples/rack/README.md +26 -1
  14. data/examples/rack/app.rb +1 -1
  15. data/examples/rack-echo/README.md +23 -1
  16. data/examples/rack-single-file/README.md +37 -0
  17. data/examples/rack-single-file/config.ru +54 -0
  18. data/examples/rails/.gitignore +21 -0
  19. data/examples/rails/.ruby-version +1 -0
  20. data/examples/rails/Gemfile +15 -0
  21. data/examples/rails/Gemfile.lock +261 -0
  22. data/examples/rails/README.md +32 -0
  23. data/examples/rails/Rakefile +8 -0
  24. data/examples/rails/app/controllers/application_controller.rb +4 -0
  25. data/examples/rails/app/controllers/jsonrpc_controller.rb +44 -0
  26. data/examples/rails/bin/dev +4 -0
  27. data/examples/rails/bin/rails +6 -0
  28. data/examples/rails/bin/rake +6 -0
  29. data/examples/rails/bin/setup +28 -0
  30. data/examples/rails/config/application.rb +47 -0
  31. data/examples/rails/config/boot.rb +5 -0
  32. data/examples/rails/config/credentials.yml.enc +1 -0
  33. data/examples/rails/config/environment.rb +7 -0
  34. data/examples/rails/config/environments/development.rb +42 -0
  35. data/examples/rails/config/environments/production.rb +60 -0
  36. data/examples/rails/config/environments/test.rb +44 -0
  37. data/examples/rails/config/initializers/cors.rb +18 -0
  38. data/examples/rails/config/initializers/filter_parameter_logging.rb +10 -0
  39. data/examples/rails/config/initializers/inflections.rb +18 -0
  40. data/examples/rails/config/initializers/jsonrpc.rb +62 -0
  41. data/examples/rails/config/locales/en.yml +31 -0
  42. data/examples/rails/config/puma.rb +40 -0
  43. data/examples/rails/config/routes.rb +14 -0
  44. data/examples/rails/config.ru +8 -0
  45. data/examples/rails/public/robots.txt +1 -0
  46. data/examples/rails-single-file/config.ru +71 -0
  47. data/examples/sinatra-classic/Gemfile +9 -0
  48. data/examples/sinatra-classic/Gemfile.lock +95 -0
  49. data/examples/sinatra-classic/README.md +32 -0
  50. data/examples/sinatra-classic/app.rb +54 -0
  51. data/examples/sinatra-classic/config.ru +6 -0
  52. data/examples/sinatra-modular/Gemfile +9 -0
  53. data/examples/sinatra-modular/Gemfile.lock +95 -0
  54. data/examples/sinatra-modular/README.md +32 -0
  55. data/examples/sinatra-modular/app.rb +57 -0
  56. data/examples/sinatra-modular/config.ru +6 -0
  57. data/lib/jsonrpc/batch_request.rb +67 -2
  58. data/lib/jsonrpc/batch_response.rb +56 -0
  59. data/lib/jsonrpc/configuration.rb +156 -14
  60. data/lib/jsonrpc/error.rb +83 -2
  61. data/lib/jsonrpc/errors/internal_error.rb +14 -2
  62. data/lib/jsonrpc/errors/invalid_params_error.rb +13 -1
  63. data/lib/jsonrpc/errors/invalid_request_error.rb +8 -0
  64. data/lib/jsonrpc/errors/method_not_found_error.rb +8 -0
  65. data/lib/jsonrpc/errors/parse_error.rb +8 -0
  66. data/lib/jsonrpc/helpers.rb +212 -21
  67. data/lib/jsonrpc/middleware.rb +211 -5
  68. data/lib/jsonrpc/notification.rb +58 -0
  69. data/lib/jsonrpc/parser.rb +30 -0
  70. data/lib/jsonrpc/railtie.rb +57 -0
  71. data/lib/jsonrpc/request.rb +68 -1
  72. data/lib/jsonrpc/response.rb +76 -0
  73. data/lib/jsonrpc/validator.rb +67 -6
  74. data/lib/jsonrpc/version.rb +11 -1
  75. data/lib/jsonrpc.rb +53 -1
  76. metadata +49 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e61b560d7db3538454d0d5206eaea1fc2a86a01b25294c4d56de4b6237dd120
4
- data.tar.gz: 1d7255ba3a462bbc9d53efbec3ba9b242f773b2c4fa270de280b951ea3b05743
3
+ metadata.gz: ea48adea36c2a7f311bb0b5c8d1868999f9ec7b6530a2b933600f8433655b63c
4
+ data.tar.gz: 8e4abe926bb02da6155650d2296d5df0b07562de6ce08163ad433df09ac40e1d
5
5
  SHA512:
6
- metadata.gz: b3d9b2f802ac5ffd1f342c45d00e001113ebf857e09208deac8560dd75a06f6201bcc4e7a99f0184cb6c1a48497d70e922d59d7f6b476c93f530584b60fa0066
7
- data.tar.gz: b07c00f97c85c9a41f82decf00dbf58f10c2655e1011f0e3e6d5e63ff2f90cee88e310f6c892a8d49c2ec94aea5cfa7dccdf9292b7940256f82b1591823df2e2
6
+ metadata.gz: 90b63747650ce423ae7f5f881db75c0c8fceabc05e627fa0fd6647cd6b67c1445eab3a09dfc19cac3b5d62d9814e9a692b96e50e0bbdc41bd8b83d9d3582b6bc
7
+ data.tar.gz: 64c40bf854999ce3c0cdc8261a921d603ed8ec664c46a6e0d4578afbc9404e693f7b8ff5456385fdcb671867049d4e07028efa51ac48276a5adfd5c4a24b194d
data/.aiexclude ADDED
@@ -0,0 +1,4 @@
1
+ .ruby-lsp
2
+ .vscode
3
+ coverage
4
+ doc
@@ -0,0 +1,105 @@
1
+ # Document
2
+
3
+ Generates documentation for Ruby code using YARD with 100% coverage enforcement.
4
+
5
+ ## Commands
6
+
7
+ Generate documentation:
8
+ ```bash
9
+ bundle exec rake yard
10
+ ```
11
+
12
+ Format YARD comments:
13
+ ```bash
14
+ bundle exec rake yard:format
15
+ ```
16
+
17
+ Verify 100% documentation coverage:
18
+ ```bash
19
+ bundle exec rake verify_measurements
20
+ ```
21
+
22
+ Check for documentation quality issues:
23
+ ```bash
24
+ bundle exec rake yard:junk
25
+ ```
26
+
27
+ Generate coverage report:
28
+ ```bash
29
+ bundle exec rake yardstick_measure
30
+ ```
31
+
32
+ ## Configuration
33
+
34
+ Requires `.yardstick.yml` in project root with 100% threshold:
35
+ ```yaml
36
+ threshold: 100
37
+ rules:
38
+ ApiTag::Presence: { enabled: true }
39
+ ApiTag::Inclusion: { enabled: true }
40
+ ApiTag::ProtectedMethod: { enabled: true }
41
+ ApiTag::PrivateMethod: { enabled: true }
42
+ ExampleTag: { enabled: true }
43
+ ReturnTag: { enabled: true }
44
+ Summary::Presence: { enabled: true }
45
+ Summary::Delimiter: { enabled: true }
46
+ ```
47
+
48
+ ## Coverage Report
49
+
50
+ After running measurement, check detailed line-by-line issues:
51
+ ```bash
52
+ cat measurements/report.txt
53
+ ```
54
+
55
+ Report shows specific file, line number, method, and documentation issues that need fixing.
56
+
57
+ ## Documentation Standards
58
+
59
+ ### Required Tags
60
+ - Every public/private method, class, and module requires `@api public` or `@api private`
61
+ - All parameters: `@param name [Type] Description`
62
+ - All returns: `@return [Type] Description` (use `[void]` if no return)
63
+ - Examples required for all public methods: `@example Description`
64
+ - Error conditions: `@raise [ExceptionClass] When this occurs`
65
+
66
+ ### Type Notation
67
+ - Use `String` not `string`
68
+ - Arrays: `Array<String>` for string arrays
69
+ - Hashes: `Hash{String=>Object}` for hash types
70
+ - Use `Boolean` not `bool`, `Integer` not `int`
71
+ - Nullable types: `String, nil` or `String|nil`
72
+
73
+ ### Formatting Rules
74
+ - Blank lines required between YARD tag groups
75
+ - Exception: `@param` and `@option` tags can be grouped together
76
+ - Wrap class/method names in `+` markers: `+PrivateKey+`
77
+ - Cross-reference related methods: `@see #other_method`
78
+
79
+ ### Documentation Structure
80
+ ```ruby
81
+ # Brief one-line summary ending with period.
82
+ #
83
+ # @api public
84
+ #
85
+ # @example Description of example
86
+ # code_example
87
+ # result # => expected_output
88
+ #
89
+ # @param name [Type] Description
90
+ # @param other [Type] Other description
91
+ #
92
+ # @return [Type] Description
93
+ #
94
+ # @raise [ExceptionClass] When this exception occurs
95
+ #
96
+ def method_name
97
+ end
98
+ ```
99
+
100
+ ## Integration
101
+
102
+ Documentation verification is included in the quality assurance pipeline:
103
+ ```bash
104
+ bundle exec rake qa
105
+ ```
@@ -0,0 +1,602 @@
1
+ # Ruby YARD Documentation Guidelines
2
+
3
+ This document outlines the comprehensive documentation standards for Ruby projects, based on the best practices demonstrated in professional Ruby gems. These guidelines ensure 100% documentation coverage and maintainable, professional-quality code documentation.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Documentation Philosophy](#documentation-philosophy)
8
+ - [YARD Configuration](#yard-configuration)
9
+ - [Basic Documentation Structure](#basic-documentation-structure)
10
+ - [API Visibility Tags](#api-visibility-tags)
11
+ - [Method Documentation](#method-documentation)
12
+ - [Class and Module Documentation](#class-and-module-documentation)
13
+ - [Attribute Documentation](#attribute-documentation)
14
+ - [Examples](#examples)
15
+ - [Parameter and Return Documentation](#parameter-and-return-documentation)
16
+ - [Error Documentation](#error-documentation)
17
+ - [Constants Documentation](#constants-documentation)
18
+ - [Inheritance Documentation](#inheritance-documentation)
19
+ - [Quality Enforcement](#quality-enforcement)
20
+ - [Best Practices](#best-practices)
21
+
22
+ ## Documentation Philosophy
23
+
24
+ Every piece of public and private code must be documented. Documentation serves multiple purposes:
25
+ - **API Contract**: Clearly defines what methods do and how to use them
26
+ - **Maintenance**: Helps future developers (including yourself) understand the code
27
+ - **Testing Guide**: Examples serve as additional test cases
28
+ - **User Experience**: Enables IDE autocompletion and contextual help
29
+
30
+ ## YARD Configuration
31
+
32
+ Create a `.yardstick.yml` file in your project root with 100% coverage requirements:
33
+
34
+ ```yaml
35
+ threshold: 100
36
+ rules:
37
+ ApiTag::Presence:
38
+ enabled: true
39
+ ApiTag::Inclusion:
40
+ enabled: true
41
+ ApiTag::ProtectedMethod:
42
+ enabled: true
43
+ ApiTag::PrivateMethod:
44
+ enabled: true
45
+ ExampleTag:
46
+ enabled: true
47
+ ReturnTag:
48
+ enabled: true
49
+ Summary::Presence:
50
+ enabled: true
51
+ Summary::Length:
52
+ enabled: false
53
+ Summary::Delimiter:
54
+ enabled: true
55
+ Summary::SingleLine:
56
+ enabled: false
57
+ ```
58
+
59
+ ## Basic Documentation Structure
60
+
61
+ Every documented element follows this structure:
62
+
63
+ ```ruby
64
+ # Brief one-line summary ending with a period
65
+ #
66
+ # @api public|private
67
+ #
68
+ # @example Description of example
69
+ # code_example
70
+ # result # => expected_output
71
+ #
72
+ # @param name [Type] Description
73
+ # @param other_name [Type] Other description
74
+ #
75
+ # @return [Type] Description
76
+ #
77
+ # @raise [ExceptionClass] When this exception occurs
78
+ #
79
+ ```
80
+
81
+ ### YARD Tag Spacing Rules
82
+
83
+ **IMPORTANT**: All YARD tags must have a blank line between them, with the following exceptions:
84
+ - `@param` tags can be grouped together without blank lines
85
+ - `@option` tags can be grouped together without blank lines
86
+ - `@param` and `@option` tags can be grouped together without blank lines when they relate to the same parameter
87
+
88
+ This ensures readable documentation while allowing logical grouping of parameter-related tags.
89
+
90
+ ## API Visibility Tags
91
+
92
+ **REQUIRED**: Every public and private method, class, and module must have an `@api` tag.
93
+
94
+ ### Public API
95
+ ```ruby
96
+ # @api public
97
+ ```
98
+ Use for:
99
+ - Public methods intended for external use
100
+ - Public classes and modules
101
+ - Public attributes and constants
102
+
103
+ ### Private API
104
+ ```ruby
105
+ # @api private
106
+ ```
107
+ Use for:
108
+ - Private methods
109
+ - Internal implementation details
110
+ - Protected methods
111
+ - Private attributes
112
+
113
+ ## Method Documentation
114
+
115
+ ### Public Method Example
116
+ ```ruby
117
+ # Generates a pair of private and public keys
118
+ #
119
+ # @api public
120
+ #
121
+ # @example
122
+ # keypair = keygen.generate_key_pair
123
+ # keypair # #<Nostr::KeyPair:0x0000000107bd3550
124
+ # @private_key="893c4cc8088924796b41dc788f7e2f746734497010b1a9f005c1faad7074b900",
125
+ # @public_key="2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558">
126
+ #
127
+ # @return [KeyPair] An object containing a private key and a public key.
128
+ #
129
+ def generate_key_pair
130
+ # implementation
131
+ end
132
+ ```
133
+
134
+ ### Private Method Example
135
+ ```ruby
136
+ # Validates the keys
137
+ #
138
+ # @api private
139
+ #
140
+ # @param private_key [PrivateKey] 32-bytes hex-encoded private key.
141
+ # @param public_key [PublicKey] 32-bytes hex-encoded public key.
142
+ #
143
+ # @raise ArgumentError when the private key is not a +PrivateKey+
144
+ # @raise ArgumentError when the public key is not a +PublicKey+
145
+ #
146
+ # @return [void]
147
+ #
148
+ def validate_keys(private_key, public_key)
149
+ # implementation
150
+ end
151
+ ```
152
+
153
+ ### Constructor Documentation
154
+ ```ruby
155
+ # Instantiates a new Event
156
+ #
157
+ # @api public
158
+ #
159
+ # @example Instantiating a new event
160
+ # Nostr::Event.new(
161
+ # id: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
162
+ # pubkey: '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e',
163
+ # created_at: 1230981305,
164
+ # kind: 1,
165
+ # tags: [],
166
+ # content: 'Your feedback is appreciated, now pay $8',
167
+ # sig: '123ac2923b792ce730b3da34f16155470ab13c8f97f9c53eaeb334f1fb3a5dc9a7f643'
168
+ # )
169
+ #
170
+ # @param id [String|nil] 32-bytes sha256 of the the serialized event data.
171
+ # @param sig [String|nil] 64-bytes signature of the sha256 hash.
172
+ # @param pubkey [String] 32-bytes hex-encoded public key of the event creator.
173
+ # @param created_at [Integer] Date of the creation. A UNIX timestamp, in seconds.
174
+ # @param kind [Integer] The kind of the event. An integer from 0 to 3.
175
+ # @param tags [Array<Array>] An array of tags. Each tag is an array of strings.
176
+ # @param content [String] Arbitrary string.
177
+ #
178
+ def initialize(pubkey:, kind:, content:, created_at: Time.now.to_i, tags: [], id: nil, sig: nil)
179
+ # implementation
180
+ end
181
+ ```
182
+
183
+ ### Methods with Keyword Arguments
184
+ ```ruby
185
+ # Instantiates a new Filter
186
+ #
187
+ # @api public
188
+ #
189
+ # @example
190
+ # Nostr::Filter.new(
191
+ # ids: ['c24881c305c5cfb7c1168be7e9b0e150'],
192
+ # authors: ['000000001c5c45196786e79f83d21fe801549fdc98e2c26f96dcef068a5dbcd7'],
193
+ # kinds: [0, 1, 2],
194
+ # since: 1230981305,
195
+ # until: 1292190341
196
+ # )
197
+ #
198
+ # @param kwargs [Hash]
199
+ # @option kwargs [Array<String>, nil] ids A list of event ids
200
+ # @option kwargs [Array<String>, nil] authors A list of pubkeys
201
+ # @option kwargs [Array<Integer>, nil] kinds A list of a kind numbers
202
+ # @option kwargs [Integer, nil] since A timestamp, events must be newer than this
203
+ # @option kwargs [Integer, nil] until A timestamp, events must be older than this
204
+ # @option kwargs [Integer, nil] limit Maximum number of events to be returned
205
+ #
206
+ def initialize(**kwargs)
207
+ # implementation
208
+ end
209
+ ```
210
+
211
+ ## Class and Module Documentation
212
+
213
+ ### Class Documentation
214
+ ```ruby
215
+ # The only object type that exists in Nostr is an event. Events are immutable.
216
+ class Event
217
+ # class body
218
+ end
219
+ ```
220
+
221
+ ### Module Documentation
222
+ ```ruby
223
+ # Performs cryptographic operations.
224
+ class Crypto
225
+ # class body
226
+ end
227
+ ```
228
+
229
+ ### Inheritance Documentation
230
+ ```ruby
231
+ # 32-bytes lowercase hex-encoded private key
232
+ class PrivateKey < Key
233
+ # class body
234
+ end
235
+ ```
236
+
237
+ ### Namespace Module
238
+ ```ruby
239
+ # Encapsulates all the gem's logic
240
+ module Nostr
241
+ # module body
242
+ end
243
+ ```
244
+
245
+ ## Attribute Documentation
246
+
247
+ ### Reader Attributes
248
+ ```ruby
249
+ # 32-bytes hex-encoded public key of the event creator
250
+ #
251
+ # @api public
252
+ #
253
+ # @example
254
+ # event.pubkey # => '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e'
255
+ #
256
+ # @return [String]
257
+ #
258
+ attr_reader :pubkey
259
+ ```
260
+
261
+ ### Accessor Attributes
262
+ ```ruby
263
+ # 64-bytes signature of the sha256 hash of the serialized event data
264
+ #
265
+ # @api public
266
+ #
267
+ # @example Getting the event signature
268
+ # event.sig # => '058613f8d34c053294cc28b7f9e1f8f0e80fd1ac94fb20f2da6ca514e7360b39'
269
+ #
270
+ # @example Setting the event signature
271
+ # event.sig = '058613f8d34c053294cc28b7f9e1f8f0e80fd1ac94fb20f2da6ca514e7360b39'
272
+ # event.sig # => '058613f8d34c053294cc28b7f9e1f8f0e80fd1ac94fb20f2da6ca514e7360b39'
273
+ #
274
+ # @return [String|nil]
275
+ #
276
+ attr_accessor :sig
277
+ ```
278
+
279
+ ### Private Attributes
280
+ ```ruby
281
+ # The elliptic curve group. Used to generate public and private keys
282
+ #
283
+ # @api private
284
+ #
285
+ # @return [ECDSA::Group]
286
+ #
287
+ attr_reader :group
288
+ ```
289
+
290
+ ## Examples
291
+
292
+ ### Multiple Examples
293
+ ```ruby
294
+ # Subscribes to a set of events using a filter
295
+ #
296
+ # @api public
297
+ #
298
+ # @example Creating a subscription with no id and no filters
299
+ # subscription = client.subscribe
300
+ #
301
+ # @example Creating a subscription with an ID
302
+ # subscription = client.subscribe(subscription_id: 'my-subscription')
303
+ #
304
+ # @example Subscribing to all events created after a certain time
305
+ # subscription = client.subscribe(filter: Nostr::Filter.new(since: 1230981305))
306
+ #
307
+ # @param subscription_id [String] The subscription id
308
+ # @param filter [Filter] A set of attributes representing events of interest
309
+ #
310
+ # @return [Subscription] The subscription object
311
+ #
312
+ def subscribe(subscription_id: SecureRandom.hex, filter: Filter.new)
313
+ # implementation
314
+ end
315
+ ```
316
+
317
+ ### Complex Examples
318
+ ```ruby
319
+ # Allows array destructuring of the KeyPair
320
+ #
321
+ # @api public
322
+ #
323
+ # @example Implicit usage of `to_ary` for destructuring
324
+ # keypair = Nostr::KeyPair.new(
325
+ # private_key: Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d'),
326
+ # public_key: Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6'),
327
+ # )
328
+ # # The `to_ary` method can be implicitly used for array destructuring:
329
+ # private_key, public_key = keypair
330
+ # # Now `private_key` and `public_key` hold the respective values.
331
+ #
332
+ # @example Explicit usage of `to_ary`
333
+ # array_representation = keypair.to_ary
334
+ # # array_representation is now an array: [PrivateKey, PublicKey]
335
+ #
336
+ # @return [Array<PrivateKey, PublicKey>] An array containing the keys in order
337
+ #
338
+ def to_ary
339
+ # implementation
340
+ end
341
+ ```
342
+
343
+ ## Parameter and Return Documentation
344
+
345
+ ### Parameter Types
346
+ ```ruby
347
+ # @param event [Event] The event to be signed
348
+ # @param private_key [PrivateKey] 32-bytes hex-encoded private key
349
+ # @param kwargs [Hash] Configuration options
350
+ # @param subscription_id [String] The subscription id
351
+ # @param filter [Filter, nil] Optional filter for events
352
+ # @param items [Array<String>] List of string items
353
+ # @param enabled [Boolean] Whether feature is enabled
354
+ # @param count [Integer] Number of items
355
+ # @param rate [Float] Processing rate
356
+ ```
357
+
358
+ ### Return Types
359
+ ```ruby
360
+ # @return [Event] A signed event
361
+ # @return [String] The generated private key
362
+ # @return [Array<Event>] List of matching events
363
+ # @return [Hash{String=>Object}] Configuration mapping
364
+ # @return [Boolean] True if valid, false otherwise
365
+ # @return [void] This method does not return a value
366
+ # @return [String, nil] The result or nil if not found
367
+ ```
368
+
369
+ ### Complex Return Types
370
+ ```ruby
371
+ # @return [Array<PrivateKey, PublicKey>] An array containing the PrivateKey and PublicKey in that order
372
+ # @return [Hash{String=>Subscription}] Mapping of subscription IDs to objects
373
+ ```
374
+
375
+ ## Error Documentation
376
+
377
+ ### Exception Handling
378
+ ```ruby
379
+ # Validates the hex value of the private key
380
+ #
381
+ # @api private
382
+ #
383
+ # @param hex_value [String] The private key in hex format
384
+ #
385
+ # @raise InvalidKeyTypeError when the private key is not a string
386
+ # @raise InvalidKeyLengthError when the private key's length is not 64 characters
387
+ # @raise InvalidKeyFormatError when the private key is in an invalid format
388
+ #
389
+ # @return [void]
390
+ #
391
+ def validate_hex_value(hex_value)
392
+ # implementation
393
+ end
394
+ ```
395
+
396
+ ### Error Class Documentation
397
+ ```ruby
398
+ # Raised when the private key's length is not 64 characters
399
+ #
400
+ # @api public
401
+ #
402
+ class InvalidKeyLengthError < KeyValidationError
403
+ # Initializes the error
404
+ #
405
+ # @example
406
+ # InvalidKeyLengthError.new('private')
407
+ #
408
+ # @param key_kind [String] The kind of key that is invalid (public or private)
409
+ #
410
+ def initialize(key_kind)
411
+ super("Invalid #{key_kind} key length. It should have 64 characters.")
412
+ end
413
+ end
414
+ ```
415
+
416
+ ## Constants Documentation
417
+
418
+ ### Simple Constants
419
+ ```ruby
420
+ # Numeric base of the OpenSSL big number used in an event content's encryption.
421
+ #
422
+ # @return [Integer]
423
+ #
424
+ BN_BASE = 16
425
+ ```
426
+
427
+ ### Complex Constants
428
+ ```ruby
429
+ # The regular expression for hexadecimal lowercase characters
430
+ #
431
+ # @return [Regexp] The regular expression for hexadecimal lowercase characters
432
+ #
433
+ FORMAT = /^[a-f0-9]+$/
434
+ ```
435
+
436
+ ## Inheritance Documentation
437
+
438
+ ### Subclass with Custom Behavior
439
+ ```ruby
440
+ # 64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data,
441
+ # which is the same as the "id" field
442
+ class Signature < String
443
+ # Custom implementation
444
+ end
445
+ ```
446
+
447
+ ### Module Inclusion
448
+ ```ruby
449
+ # Clients can talk with relays and can subscribe to any set of events using a subscription filters.
450
+ #
451
+ # There is no sign-up or account creation for a client. Every time a client connects to a relay, it submits its
452
+ # subscription filters and the relay streams the "interested events" to the client as long as they are connected.
453
+ #
454
+ class Client
455
+ include EventEmitter
456
+
457
+ # class body
458
+ end
459
+ ```
460
+
461
+ ## Quality Enforcement
462
+
463
+ Our project enforces 100% documentation coverage using the `yardstick` gem and maintains documentation quality with the `yard:junk` task. This ensures that all code is thoroughly documented, maintainable, and easy for developers to use.
464
+
465
+ ### How it Works
466
+
467
+ 1. **Configuration**: A `.yardstick.yml` file in the project root sets the documentation coverage threshold to 100% and defines specific documentation rules.
468
+
469
+ 2. **Measurement**: The `yardstick_measure` Rake task measures the current documentation coverage against the configured rules. Run it using:
470
+
471
+ ```bash
472
+ bundle exec rake yardstick_measure
473
+ ```
474
+
475
+ This task generates a detailed report in `measurements/report.txt` that lists all documentation issues that need to be addressed.
476
+
477
+ 3. **Verification**: The `verify_measurements` Rake task checks if the coverage meets the 100% threshold. Run it using:
478
+
479
+ ```bash
480
+ bundle exec rake verify_measurements
481
+ ```
482
+
483
+ This task will fail if coverage is below 100%, displaying the current coverage percentage and indicating that documentation improvements are needed.
484
+
485
+ 4. **Review Report**: Always check the contents of `measurements/report.txt` after running measurement tasks. This file contains specific details about:
486
+ - Missing method documentation
487
+ - Incomplete parameter descriptions
488
+ - Missing return value documentation
489
+ - Missing examples
490
+ - Incorrect API tags
491
+
492
+ Each line in the report shows the file, line number, method, and specific issue that needs to be addressed to achieve 100% documentation compliance.
493
+
494
+ ### Rake Integration
495
+
496
+ The `Rakefile` integrates `yardstick` into our development workflow. The `qa` task, which runs a full suite of tests and checks, includes the documentation verification step.
497
+
498
+ ```ruby
499
+ # Rakefile
500
+ require 'yardstick/rake/measurement'
501
+ require 'yardstick/rake/verify'
502
+
503
+ yardstick_options = YAML.load_file('.yardstick.yml')
504
+
505
+ Yardstick::Rake::Measurement.new(:yardstick_measure, yardstick_options)
506
+ Yardstick::Rake::Verify.new
507
+
508
+ desc 'Test, lint and perform security and documentation audits'
509
+ task qa: %w[spec rubocop yard:junk verify_measurements bundle:audit]
510
+ ```
511
+
512
+ ### CI Integration
513
+
514
+ Continuous Integration (CI) automatically enforces our documentation standard on every commit by running:
515
+
516
+ ```bash
517
+ bundle exec rake verify_measurements
518
+ ```
519
+
520
+ This prevents any code with incomplete documentation from being merged.
521
+
522
+ ## Best Practices
523
+
524
+ ### 1. **Always Use @api Tags**
525
+ Every method, class, and module must have an `@api public` or `@api private` tag.
526
+
527
+ ### 2. **Provide Meaningful Examples**
528
+ Examples should show real usage, not trivial cases:
529
+
530
+ ```ruby
531
+ # GOOD
532
+ # @example Subscribing to text notes from the last hour
533
+ # filter = Nostr::Filter.new(
534
+ # kinds: [Nostr::EventKind::TEXT_NOTE],
535
+ # since: Time.now.to_i - 3600
536
+ # )
537
+ # subscription = client.subscribe(filter: filter)
538
+
539
+ # BAD
540
+ # @example
541
+ # client.subscribe
542
+ ```
543
+
544
+ ### 3. **Document All Parameters**
545
+ Even obvious parameters should be documented:
546
+
547
+ ```ruby
548
+ # @param id [String] The unique identifier
549
+ # @param name [String] The human-readable name
550
+ ```
551
+
552
+ ### 4. **Use Consistent Type Notation**
553
+ - `String` not `string`
554
+ - `Array<String>` for arrays of strings
555
+ - `Hash{String=>Object}` for hash types
556
+ - `Boolean` not `bool`
557
+ - `Integer` not `int`
558
+
559
+ ### 5. **Document Return Values**
560
+ Every method must have a `@return` tag, even if it returns `void`.
561
+
562
+ ### 6. **Cross-Reference Related Methods**
563
+ ```ruby
564
+ # @see #valid_sig?
565
+ # @see Crypto#check_sig!
566
+ ```
567
+
568
+ ### 7. **Use Code Formatting**
569
+ Wrap class names and method names in `+` markers:
570
+ ```ruby
571
+ # @raise ArgumentError when the private key is not a +PrivateKey+
572
+ ```
573
+
574
+ ### 8. **Document Inheritance Clearly**
575
+ When a class inherits behavior, mention it:
576
+ ```ruby
577
+ # Base error class for all signature validation errors
578
+ class SignatureValidationError < Error; end
579
+ ```
580
+
581
+ ### 9. **Version Constants**
582
+ ```ruby
583
+ # The version of the gem
584
+ VERSION = '0.7.0'
585
+ ```
586
+
587
+ ### 10. **Multi-line Examples**
588
+ For complex examples, use proper formatting:
589
+ ```ruby
590
+ # @example Creating a comprehensive filter
591
+ # filter = Nostr::Filter.new(
592
+ # kinds: [
593
+ # Nostr::EventKind::TEXT_NOTE,
594
+ # Nostr::EventKind::ENCRYPTED_DIRECT_MESSAGE
595
+ # ],
596
+ # since: Time.now.to_i - 3600, # 1 hour ago
597
+ # until: Time.now.to_i,
598
+ # limit: 20,
599
+ # )
600
+ ```
601
+
602
+ This documentation standard ensures that your Ruby projects maintain professional-quality documentation that supports both development and maintenance activities while providing excellent IDE integration and user experience.