spec_forge 0.6.0 → 0.7.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -8
  3. data/README.md +135 -10
  4. data/flake.lock +3 -3
  5. data/flake.nix +3 -3
  6. data/lib/spec_forge/attribute/factory.rb +1 -1
  7. data/lib/spec_forge/attribute/transform.rb +1 -1
  8. data/lib/spec_forge/callbacks.rb +9 -0
  9. data/lib/spec_forge/cli/docs/generate.rb +72 -0
  10. data/lib/spec_forge/cli/docs.rb +92 -0
  11. data/lib/spec_forge/cli/init.rb +39 -7
  12. data/lib/spec_forge/cli/new.rb +13 -3
  13. data/lib/spec_forge/cli/run.rb +12 -4
  14. data/lib/spec_forge/cli/serve.rb +156 -0
  15. data/lib/spec_forge/cli.rb +14 -6
  16. data/lib/spec_forge/configuration.rb +13 -9
  17. data/lib/spec_forge/context/store.rb +23 -40
  18. data/lib/spec_forge/core_ext/array.rb +27 -0
  19. data/lib/spec_forge/documentation/builder.rb +383 -0
  20. data/lib/spec_forge/documentation/document/operation.rb +47 -0
  21. data/lib/spec_forge/documentation/document/parameter.rb +22 -0
  22. data/lib/spec_forge/documentation/document/request_body.rb +24 -0
  23. data/lib/spec_forge/documentation/document/response.rb +39 -0
  24. data/lib/spec_forge/documentation/document/response_body.rb +27 -0
  25. data/lib/spec_forge/documentation/document.rb +48 -0
  26. data/lib/spec_forge/documentation/generators/base.rb +81 -0
  27. data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
  28. data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
  29. data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
  30. data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
  31. data/lib/spec_forge/documentation/generators.rb +17 -0
  32. data/lib/spec_forge/documentation/loader/cache.rb +138 -0
  33. data/lib/spec_forge/documentation/loader.rb +159 -0
  34. data/lib/spec_forge/documentation/openapi/base.rb +33 -0
  35. data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
  36. data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
  37. data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
  38. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
  39. data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
  40. data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
  41. data/lib/spec_forge/documentation/openapi.rb +23 -0
  42. data/lib/spec_forge/documentation.rb +27 -0
  43. data/lib/spec_forge/error.rb +17 -0
  44. data/lib/spec_forge/factory.rb +2 -2
  45. data/lib/spec_forge/filter.rb +3 -4
  46. data/lib/spec_forge/forge.rb +5 -4
  47. data/lib/spec_forge/http/backend.rb +5 -0
  48. data/lib/spec_forge/http/request.rb +14 -3
  49. data/lib/spec_forge/loader.rb +14 -24
  50. data/lib/spec_forge/normalizer/default.rb +51 -0
  51. data/lib/spec_forge/normalizer/definition.rb +248 -0
  52. data/lib/spec_forge/normalizer/validators.rb +99 -0
  53. data/lib/spec_forge/normalizer.rb +356 -199
  54. data/lib/spec_forge/normalizers/_shared.yml +76 -0
  55. data/lib/spec_forge/normalizers/configuration.yml +23 -0
  56. data/lib/spec_forge/normalizers/constraint.yml +8 -0
  57. data/lib/spec_forge/normalizers/expectation.yml +47 -0
  58. data/lib/spec_forge/normalizers/factory.yml +12 -0
  59. data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
  60. data/lib/spec_forge/normalizers/global_context.yml +28 -0
  61. data/lib/spec_forge/normalizers/spec.yml +50 -0
  62. data/lib/spec_forge/runner/adapter.rb +181 -0
  63. data/lib/spec_forge/runner/debug_proxy.rb +44 -42
  64. data/lib/spec_forge/runner/state.rb +4 -5
  65. data/lib/spec_forge/runner.rb +40 -124
  66. data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
  67. data/lib/spec_forge/spec/expectation.rb +7 -3
  68. data/lib/spec_forge/spec.rb +13 -58
  69. data/lib/spec_forge/version.rb +1 -1
  70. data/lib/spec_forge.rb +30 -23
  71. data/lib/templates/openapi.yml.tt +22 -0
  72. data/lib/templates/redoc.html.tt +28 -0
  73. data/lib/templates/swagger.html.tt +59 -0
  74. metadata +92 -14
  75. data/lib/spec_forge/normalizer/configuration.rb +0 -90
  76. data/lib/spec_forge/normalizer/constraint.rb +0 -60
  77. data/lib/spec_forge/normalizer/expectation.rb +0 -105
  78. data/lib/spec_forge/normalizer/factory.rb +0 -78
  79. data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
  80. data/lib/spec_forge/normalizer/global_context.rb +0 -88
  81. data/lib/spec_forge/normalizer/spec.rb +0 -97
  82. /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
  83. /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
  84. /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b2d5790d4797a63b8fbad4dd296b9c9beb657b30a98bda5fab8ca7c635abd5a
4
- data.tar.gz: b22a8f664dd676846e703f5312383c9c9b1b9cabab52b2cc3c8a8d28830490c0
3
+ metadata.gz: 9891353d8acd7d64ec4b2690e4e91d68c0d455913b8ca81c7f9e410859f51774
4
+ data.tar.gz: 7ff9aa712f7d21a75ae8a383175702ed8973b25e2935458bd75dd713f59b6fb3
5
5
  SHA512:
6
- metadata.gz: 858e7dfd4546bc34e7dd8f899d99b5cf03d26cfbe9042f238de44566e2981a82bcd06803a0319f6d937d7c509dc3c02e8a962415c8e6fbb8e3c515c693c5e176
7
- data.tar.gz: 3b246fda2d4bbbc5ce12fa0f683e27e1cb0e2950fe3b7c03b978f6d88662d1b65b0037b00931fe598415ee3d3c8477f7f76fd3f523547e061f86ac000e617d52
6
+ metadata.gz: 0637e96a8739579015af17ee0a6c8442b51c9fd189b2a220b7828ae68f0784760aad4d5312eabf450f18ee7e6db2789dad3fb55327cee0aa19f3a8e9b88be243
7
+ data.tar.gz: 3d10cc2cafb76d2d14b42e104fba62a5e8ec35d70cb92f6769e3fcde9611d9949d707fdc287caae52454e04d6542c507274e357bb38f41090792efa40e5d6436
data/CHANGELOG.md CHANGED
@@ -15,7 +15,159 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
15
  ### Removed
16
16
  -->
17
17
 
18
- ## [Unreleased]
18
+ ## [0.7.1] - 12025-10-08
19
+
20
+ ### Added
21
+
22
+ - **Configuration alias**: `base_path` can now be used as an alias for `base_url` in spec definitions
23
+ - **Improved debug configuration**: New `config.on_debug { }` block syntax for cleaner configuration
24
+
25
+ ```ruby
26
+ # New cleaner syntax (recommended)
27
+ config.on_debug { binding.pry }
28
+
29
+ # Old syntax still works with deprecation warning
30
+ config.on_debug = -> { binding.pry }
31
+ ```
32
+
33
+ ### Changed
34
+
35
+ - **Fixed path handling in HTTP requests**: Paths with leading slashes (e.g., `/users`) now properly append to base URLs instead of replacing the entire path
36
+ - Previously, a base URL like `http://api.example.com/v1` with path `/users` would incorrectly resolve to `http://api.example.com/users`
37
+ - Now correctly resolves to `http://api.example.com/v1/users`
38
+ - This matches user expectations and common API patterns
39
+ - **Note**: If you need to replace the entire path, use absolute URLs like `https://api.example.com/users`
40
+ - **CI/CD improvements**: Updated to GitHub Actions checkout v5 and streamlined Ruby version testing
41
+
42
+ ### Fixed
43
+
44
+ - **Transform.join resolution**: Now properly generates fresh values instead of using cached results
45
+ - **Test adapter optimization**: Removed JSON class validation check to allow matchers to be used
46
+
47
+ ### Deprecated
48
+
49
+ - `config.on_debug = proc` syntax - use `config.on_debug { }` block syntax instead
50
+
51
+ ## [0.7.0] - 12025-06-22
52
+
53
+ ### Added
54
+
55
+ #### 🚀 Documentation-First Architecture
56
+
57
+ **The Big Picture**: SpecForge now generates OpenAPI documentation from your tests automatically!
58
+
59
+ - **Primary Documentation Workflow**: New `docs` command (now the default!) generates OpenAPI specs from test execution
60
+
61
+ - Smart caching system with `--fresh` flag for forced regeneration
62
+ - Multiple output formats: YAML (default) or JSON via `--format`
63
+ - Custom output paths with `--output` option
64
+ - Built-in OpenAPI 3.0.4 validation with detailed, helpful error messages
65
+ - Optional validation skip with `--skip-validation` for faster iterations
66
+
67
+ - **Live Documentation Server**: `spec_forge serve` command for immediate feedback
68
+
69
+ - Local web server with Swagger UI (default) or Redoc (`--ui redoc`)
70
+ - Configurable port with `--port` (defaults to 8080)
71
+ - Auto-generated HTML templates for both UI options
72
+ - Perfect for development and API review workflows
73
+
74
+ - **Flexible Configuration System**:
75
+ - Directory-based config: `config/components/`, `config/paths/`, etc.
76
+ - Template-based initialization with sensible defaults
77
+ - Enhanced YAML merging with `$ref` support
78
+ - Full OpenAPI customization through configuration files
79
+
80
+ #### 🧪 Enhanced Testing Capabilities
81
+
82
+ - **HTTP Header Testing**: Comprehensive header validation
83
+
84
+ ```yaml
85
+ headers:
86
+ Content-Type: "application/json"
87
+ X-Request-ID: /^[0-9a-f-]{36}$/
88
+ Cache-Control:
89
+ matcher.and:
90
+ - matcher.include: "max-age="
91
+ - matcher.include: "private"
92
+ ```
93
+
94
+ - **Flexible Store System**: Store anything, access everything
95
+
96
+ - OpenStruct-based entries for maximum flexibility
97
+ - Custom data via callbacks (config, metadata, computed values)
98
+ - Same familiar `store.id.attribute` syntax
99
+ - Perfect for complex test scenarios and feature flags
100
+
101
+ - **Documentation Control**: Fine-grained control over what gets documented
102
+ - New `documentation: true/false` attribute for specs and expectations
103
+ - Exclude test-only scenarios from API docs while keeping functionality
104
+
105
+ #### ⚙️ Architecture Improvements
106
+
107
+ - **YAML-Driven Normalizers**: Configuration over code
108
+
109
+ - Structure definitions in `lib/spec_forge/normalizers/*.yml`
110
+ - Powerful `reference:` system for reusable components
111
+ - Wildcard support (`*`) for catch-all schemas
112
+ - Centralized validation logic in dedicated module
113
+
114
+ - **Enhanced CLI Experience**:
115
+
116
+ - Improved `init` command with `--skip-openapi` and `--skip-factories` flags
117
+ - Better help text and examples throughout
118
+ - Clearer error messages with actionable context
119
+
120
+ - **Developer Utilities**:
121
+ - `Array#to_merged_h` for cleaner hash merging
122
+ - Unified `.normalize!(input, using:)` API across normalizers
123
+ - Separated test preparation (`Runner.prepare`) from execution
124
+
125
+ ### Changed
126
+
127
+ #### 🎯 User Experience Overhaul
128
+
129
+ - **New Default Behavior**: `spec_forge` without arguments now shows help instead of running tests
130
+
131
+ - **Breaking Change**: Use `spec_forge docs` for documentation or `spec_forge run` for test-only execution
132
+ - Safer default that guides users to the right command for their needs
133
+
134
+ - **Streamlined Commands**:
135
+ - Better command organization and help text
136
+ - Consistent flag naming across commands
137
+ - Enhanced error handling with helpful suggestions
138
+
139
+ #### 🏗️ Internal Refactoring
140
+
141
+ - **Normalizer Architecture**: YAML-based instead of class-heavy approach
142
+
143
+ - Consolidated shared definitions in `_shared.yml`
144
+ - Easier maintenance and extension
145
+ - Better error context with attribute path tracking
146
+
147
+ - **Test Execution Pipeline**:
148
+
149
+ - Clean separation between test preparation and execution
150
+ - Enhanced RSpec adapter pattern
151
+ - Better reusability for documentation generation
152
+
153
+ - **HTTP & Store Improvements**:
154
+ - Automatic header value string conversion
155
+ - Simplified store entry structure with OpenStruct flexibility
156
+ - Enhanced request/response handling
157
+
158
+ ### Removed
159
+
160
+ - **Legacy Architecture**: Individual normalizer class files (replaced with YAML config)
161
+
162
+ ---
163
+
164
+ **Migration Notes**:
165
+
166
+ - Update any scripts using bare `spec_forge` - now shows help instead of running tests
167
+ - Use `spec_forge docs` for documentation generation or `spec_forge run` for testing
168
+ - Store access patterns remain the same, but internal structure is more flexible
169
+
170
+ ## [0.6.0] - 12025-03-25
19
171
 
20
172
  ### Added
21
173
 
@@ -26,6 +178,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
178
  - `Context::Variables` for managing variables with overlay support
27
179
  - `Context::Store` for storing the results of the tests
28
180
  - Added support for defining and referencing global variables
181
+
29
182
  ```yaml
30
183
  global:
31
184
  variables:
@@ -37,13 +190,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
37
190
  query:
38
191
  api_version: "global.variables.api_version"
39
192
  ```
193
+
40
194
  - Added compound matcher support via `matcher.and` for combining multiple matchers
41
195
  ```yaml
42
196
  email:
43
197
  matcher.and:
44
- - kind_of.string
45
- - /@/
46
- - matcher.end_with: ".com"
198
+ - kind_of.string
199
+ - /@/
200
+ - matcher.end_with: ".com"
47
201
  ```
48
202
  - Added custom RSpec matcher `have_size` for checking an object's size via `matcher.have_size`
49
203
  - Added new `Loader` class for improved spec file processing
@@ -51,6 +205,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51
205
  - Added normalizer for global context validation
52
206
  - Added line number tracking for specs and expectations
53
207
  - Added support for defining and referencing callbacks
208
+
54
209
  ```ruby
55
210
  # Configuration level
56
211
  SpecForge.configure do |config|
@@ -63,14 +218,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
63
218
  # Module level (no aliases)
64
219
  SpecForge.register_callback("callback_name") { |context| }
65
220
  ```
221
+
66
222
  Once defined, callbacks can be referenced in spec files via the global context
223
+
67
224
  ```yaml
68
225
  global:
69
226
  callbacks:
70
- - before: callback_name
71
- after: cleanup_database_state
227
+ - before: callback_name
228
+ after: cleanup_database_state
72
229
  ```
230
+
73
231
  - Added support for storing and retrieving test data via the `store_as` directive and `store` attribute
232
+
74
233
  ```yaml
75
234
  create_user:
76
235
  path: "/users"
@@ -90,6 +249,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
90
249
  - expect:
91
250
  status: 200
92
251
  ```
252
+
93
253
  - Added `UndefinedMatcherError` for clearer error messaging when invalid matchers are used
94
254
  - Enhanced debugging capabilities with improved DebugProxy methods and store access
95
255
  - Added HTTP status descriptions for better error messages
@@ -140,7 +300,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
140
300
  variables:
141
301
  users:
142
302
  factory.user:
143
- size: 10 # Creates 10 user records
303
+ size: 10 # Creates 10 user records
144
304
  ```
145
305
  - All FactoryBot list methods now supported:
146
306
  - `create_list` (default)
@@ -199,6 +359,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
199
359
  ### Added
200
360
 
201
361
  - Core Infrastructure
362
+
202
363
  - Configuration management
203
364
  - User input validation and normalization
204
365
  - Factory registration
@@ -208,11 +369,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
208
369
  - Debugging tooling
209
370
 
210
371
  - CLI
372
+
211
373
  - Project initialization (`init`)
212
374
  - Spec/factory generation (`new`)
213
375
  - Test execution (`run`)
214
376
 
215
377
  - Attributes
378
+
216
379
  - Chainable attribute handling (through `Chainable`)
217
380
  - Expanded attribute handling (through `Parameterized`)
218
381
  - Factory (with chainable support) - `factories.`
@@ -231,7 +394,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
231
394
 
232
395
  - Initial commit
233
396
 
234
- [unreleased]: https://github.com/itsthedevman/spec_forge/compare/v0.5.0...HEAD
397
+ [unreleased]: https://github.com/itsthedevman/spec_forge/compare/v0.7.1...HEAD
398
+ [0.7.1]: https://github.com/itsthedevman/spec_forge/compare/v0.7.0...v0.7.1
399
+ [0.7.0]: https://github.com/itsthedevman/spec_forge/compare/v0.6.0...v0.7.0
400
+ [0.6.0]: https://github.com/itsthedevman/spec_forge/compare/v0.5.0...v0.6.0
235
401
  [0.5.0]: https://github.com/itsthedevman/spec_forge/compare/v0.4.0...v0.5.0
236
402
  [0.4.0]: https://github.com/itsthedevman/spec_forge/compare/v0.3.2...v0.4.0
237
403
  [0.3.2]: https://github.com/itsthedevman/spec_forge/compare/v0.3.0...v0.3.2
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # SpecForge
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/spec_forge.svg)](https://badge.fury.io/rb/spec_forge)
4
- ![Ruby Version](https://img.shields.io/badge/ruby-3.3.7-ruby)
4
+ ![Ruby Version](https://img.shields.io/badge/ruby-3.2+-ruby)
5
5
  [![Tests](https://github.com/itsthedevman/spec_forge/actions/workflows/main.yml/badge.svg)](https://github.com/itsthedevman/spec_forge/actions/workflows/main.yml)
6
6
 
7
- > Note: The code in this repository represents the latest development version with new features and improvements that are being prepared for future releases. For the current stable version, check out [v0.6.0](https://github.com/itsthedevman/spec_forge/releases/tag/v0.6.0) on GitHub releases.
7
+ > Note: The code in this repository represents the latest development version with new features and improvements that are being prepared for future releases. For the current stable version, check out [v0.7.1](https://github.com/itsthedevman/spec_forge/releases/tag/v0.7.1) on GitHub releases.
8
8
 
9
- Write API tests in YAML that read like documentation:
9
+ Write API tests in YAML that read like documentation and generate OpenAPI specifications:
10
10
 
11
11
  ```yaml
12
12
  show_user:
@@ -25,22 +25,39 @@ show_user:
25
25
  matcher.and:
26
26
  - kind_of.string
27
27
  - /@/
28
+ headers:
29
+ Content-Type: "application/json"
30
+ X-Request-ID: /^[0-9a-f-]{36}$/
28
31
  ```
29
32
 
30
- That's a complete test. No Ruby code, no configuration files, no HTTP client setup - just a clear description of what you're testing. Under the hood, you get all the power of RSpec's matchers, Faker's data generation, and FactoryBot's test objects.
33
+ That's a complete test that validates your API and creates OpenAPI documentation. No Ruby code, no configuration files, no HTTP client setup - just clear, executable specifications.
31
34
 
32
35
  ## Why SpecForge?
33
36
 
34
- 1. **Living Documentation**: Your tests should serve as clear, readable documentation of your API's behavior.
35
- 2. **Reduce Boilerplate**: Write tests without repetitive setup code and HTTP configuration.
36
- 3. **Quick Setup**: Start testing APIs in minutes instead of spending hours on test infrastructure.
37
- 4. **Gradual Adoption**: Use alongside your existing test suite, introducing it incrementally where it makes sense.
38
- 5. **Developer & QA Collaboration**: Create a testing format that everyone can understand and maintain, regardless of Ruby expertise.
37
+ **For Testing:**
38
+
39
+ - **Reduce Boilerplate**: Write tests without repetitive setup code and HTTP configuration
40
+ - **Quick Setup**: Start testing APIs in minutes instead of spending hours on test infrastructure
41
+ - **Clear Syntax**: Tests that everyone can read and understand, regardless of Ruby expertise
42
+
43
+ **For Documentation:**
44
+
45
+ - **OpenAPI Generation**: Generate OpenAPI specifications from your test structure, with full customization through configuration files
46
+ - **Living Documentation**: Your tests ensure the documentation always matches your actual API behavior
47
+ - **Professional Output**: View your API docs in Swagger UI or Redoc with minimal setup
48
+
49
+ **For Teams:**
50
+
51
+ - **Developer & QA Collaboration**: Create specifications that both developers and QA can maintain
52
+ - **Gradual Adoption**: Use alongside your existing test suite, introducing it incrementally where it makes sense
39
53
 
40
54
  ## Key Features
41
55
 
56
+ - **Automatic Documentation Generation**: Transform tests into OpenAPI specifications with customizable configuration
57
+ - **Live Documentation Server**: Local development server for viewing generated documentation
42
58
  - **YAML-Based Tests**: Write clear, declarative tests that read like documentation
43
59
  - **RSpec Integration**: Leverage all the power of RSpec matchers and expectations
60
+ - **Header Testing**: Comprehensive HTTP header validation with compound matchers
44
61
  - **FactoryBot Integration**: Generate test data with FactoryBot integration
45
62
  - **Faker Integration**: Create realistic test data with Faker
46
63
  - **Variable System**: Define and reference variables for dynamic test data
@@ -49,6 +66,23 @@ That's a complete test. No Ruby code, no configuration files, no HTTP client set
49
66
  - **Global Variables**: Define shared configuration at the file level
50
67
  - **Callback System**: Hook into the test lifecycle using Ruby for setup, teardown, and much more!
51
68
 
69
+ ## Quick Start
70
+
71
+ Get started with SpecForge in 3 commands:
72
+
73
+ ```bash
74
+ # 1. Initialize SpecForge
75
+ spec_forge init
76
+
77
+ # 2. Create your first test
78
+ spec_forge new spec users
79
+
80
+ # 3. View your documentation
81
+ spec_forge serve
82
+ ```
83
+
84
+ Then visit `http://localhost:8080` to see your API documentation!
85
+
52
86
  ## When Not to Use SpecForge
53
87
 
54
88
  Consider alternatives when you need:
@@ -92,12 +126,103 @@ Create your first test:
92
126
  spec_forge new spec users
93
127
  ```
94
128
 
95
- Run your tests:
129
+ Generate documentation (default command):
130
+
131
+ ```bash
132
+ spec_forge
133
+ ```
134
+
135
+ Or start the live documentation server:
136
+
137
+ ```bash
138
+ spec_forge serve
139
+ ```
140
+
141
+ Run tests only (no documentation):
96
142
 
97
143
  ```bash
98
144
  spec_forge run
99
145
  ```
100
146
 
147
+ ## Documentation Workflow
148
+
149
+ SpecForge provides multiple ways to work with your API documentation:
150
+
151
+ ```bash
152
+ # Generate OpenAPI specifications
153
+ spec_forge docs # Smart caching
154
+ spec_forge docs --fresh # Force regeneration
155
+ spec_forge docs --format json # Output as JSON instead of YAML
156
+
157
+ # View documentation in browser
158
+ spec_forge serve # Generate if needed + serve
159
+ spec_forge serve --fresh # Force regeneration + serve
160
+ spec_forge serve --ui redoc # Use Redoc instead
161
+ spec_forge serve --port 3001 # Custom port
162
+
163
+ # Traditional testing
164
+ spec_forge run # Pure testing mode
165
+ spec_forge run users:show_user # Run specific tests
166
+ ```
167
+
168
+ ## Example: Complete User API
169
+
170
+ ```yaml
171
+ # spec_forge/specs/users.yml
172
+ global:
173
+ variables:
174
+ admin_role: "admin"
175
+
176
+ list_users:
177
+ path: /users
178
+ expectations:
179
+ - expect:
180
+ status: 200
181
+ headers:
182
+ Content-Type: "application/json"
183
+ json:
184
+ users:
185
+ matcher.have_size:
186
+ be.greater_than: 0
187
+
188
+ create_user:
189
+ path: /users
190
+ method: POST
191
+ variables:
192
+ username: faker.internet.username
193
+ email: faker.internet.email
194
+ body:
195
+ name: variables.username
196
+ email: variables.email
197
+ role: global.variables.admin_role
198
+ store_as: new_user
199
+ expectations:
200
+ - expect:
201
+ status: 201
202
+ headers:
203
+ Location: /\/users\/\d+/
204
+ json:
205
+ id: kind_of.integer
206
+ name: variables.username
207
+ email: variables.email
208
+ role: global.variables.admin_role
209
+
210
+ show_user:
211
+ path: /users/{id}
212
+ query:
213
+ id: store.new_user.body.id
214
+ expectations:
215
+ - expect:
216
+ status: 200
217
+ json:
218
+ id: store.new_user.body.id
219
+ name: store.new_user.body.name
220
+ email: store.new_user.body.email
221
+ role: global.variables.admin_role
222
+ ```
223
+
224
+ This automatically generates a complete OpenAPI specification with all endpoints, request/response schemas, and examples!
225
+
101
226
  ## Documentation
102
227
 
103
228
  For comprehensive documentation, visit the [SpecForge Wiki](https://github.com/itsthedevman/spec_forge/wiki) which includes:
data/flake.lock CHANGED
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "nixpkgs": {
22
22
  "locked": {
23
- "lastModified": 1742422364,
24
- "narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=",
23
+ "lastModified": 1750365781,
24
+ "narHash": "sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50=",
25
25
  "owner": "NixOS",
26
26
  "repo": "nixpkgs",
27
- "rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc",
27
+ "rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54",
28
28
  "type": "github"
29
29
  },
30
30
  "original": {
data/flake.nix CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- description = "Ruby 3.4.2 development environment";
2
+ description = "Ruby 3.2 development environment";
3
3
 
4
4
  inputs = {
5
5
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -20,8 +20,8 @@
20
20
  {
21
21
  devShells.default = pkgs.mkShell {
22
22
  buildInputs = with pkgs; [
23
- (ruby_3_4.override {
24
- jemallocSupport = true;
23
+ (ruby_3_2.override {
24
+ jemallocSupport = false;
25
25
  docSupport = false;
26
26
  })
27
27
 
@@ -80,7 +80,7 @@ module SpecForge
80
80
  super
81
81
 
82
82
  # Check the arguments before preparing them
83
- arguments[:keyword] = Normalizer.normalize_factory_reference!(arguments[:keyword])
83
+ arguments[:keyword] = Normalizer.normalize!(arguments[:keyword], using: :factory_reference)
84
84
 
85
85
  prepare_arguments!
86
86
  end
@@ -59,7 +59,7 @@ module SpecForge
59
59
  case function
60
60
  when "join"
61
61
  # Technically supports any attribute, but I ain't gonna test all them edge cases
62
- arguments[:positional].resolved.join
62
+ arguments[:positional].resolve.join
63
63
  end
64
64
  end
65
65
  end
@@ -36,6 +36,15 @@ module SpecForge
36
36
  instance[name.to_s] = block
37
37
  end
38
38
 
39
+ #
40
+ # Deregisters a callback
41
+ #
42
+ # @param name [String, Symbol] The name of the callback
43
+ #
44
+ def deregister(name)
45
+ instance.delete(name.to_s)
46
+ end
47
+
39
48
  #
40
49
  # Checks if a callback is registered for the given event
41
50
  #
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecForge
4
+ class CLI
5
+ class Docs < Command
6
+ #
7
+ # Shared functionality for generating OpenAPI documentation
8
+ #
9
+ # This module contains the core logic for running tests, extracting endpoint
10
+ # data, and generating OpenAPI specifications. It's used by both the Docs
11
+ # and Serve commands to avoid duplication.
12
+ #
13
+ module Generate
14
+ #
15
+ # Generates OpenAPI documentation and writes it to disk
16
+ #
17
+ # Runs the documentation generation pipeline: executes tests, extracts
18
+ # endpoint data, generates OpenAPI spec, validates it, and writes the
19
+ # output file in the specified format.
20
+ #
21
+ # @return [Pathname] The path to the generated documentation file
22
+ #
23
+ def generate_documentation
24
+ generator = Documentation::Generators::OpenAPI["3.0"]
25
+ output = generator.generate(use_cache: !options.fresh)
26
+
27
+ generator.validate!(output) unless options.skip_validation
28
+
29
+ # Determine output format and path
30
+ file_format = determine_file_format
31
+ file_path = determine_output_path(file_format)
32
+
33
+ content =
34
+ if file_format == "json"
35
+ JSON.pretty_generate(output)
36
+ else
37
+ output.to_yaml(stringify_names: true)
38
+ end
39
+
40
+ ::File.write(file_path, content)
41
+
42
+ file_path
43
+ end
44
+
45
+ private
46
+
47
+ def determine_file_format
48
+ file_format = options.format&.downcase || "yml"
49
+ validate_format!(file_format)
50
+
51
+ file_format
52
+ end
53
+
54
+ def validate_format!(format)
55
+ return if VALID_FORMATS.include?(format)
56
+
57
+ raise ArgumentError,
58
+ "Invalid format #{format.in_quotes}. Valid formats: #{VALID_FORMATS.join_map(", ", &:in_quotes)}"
59
+ end
60
+
61
+ def determine_output_path(format)
62
+ if options.output
63
+ Pathname.new(options.output)
64
+ else
65
+ extension = (format == "json") ? "json" : "yml"
66
+ SpecForge.openapi_path.join("generated", "openapi.#{extension}")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end