spec_forge 0.6.0 → 0.7.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -2
  3. data/README.md +133 -8
  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/callbacks.rb +9 -0
  8. data/lib/spec_forge/cli/docs/generate.rb +72 -0
  9. data/lib/spec_forge/cli/docs.rb +92 -0
  10. data/lib/spec_forge/cli/init.rb +39 -7
  11. data/lib/spec_forge/cli/new.rb +13 -3
  12. data/lib/spec_forge/cli/run.rb +12 -4
  13. data/lib/spec_forge/cli/serve.rb +155 -0
  14. data/lib/spec_forge/cli.rb +14 -6
  15. data/lib/spec_forge/configuration.rb +2 -2
  16. data/lib/spec_forge/context/store.rb +23 -40
  17. data/lib/spec_forge/core_ext/array.rb +27 -0
  18. data/lib/spec_forge/documentation/builder.rb +383 -0
  19. data/lib/spec_forge/documentation/document/operation.rb +47 -0
  20. data/lib/spec_forge/documentation/document/parameter.rb +22 -0
  21. data/lib/spec_forge/documentation/document/request_body.rb +24 -0
  22. data/lib/spec_forge/documentation/document/response.rb +39 -0
  23. data/lib/spec_forge/documentation/document/response_body.rb +27 -0
  24. data/lib/spec_forge/documentation/document.rb +48 -0
  25. data/lib/spec_forge/documentation/generators/base.rb +81 -0
  26. data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
  27. data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
  28. data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
  29. data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
  30. data/lib/spec_forge/documentation/generators.rb +17 -0
  31. data/lib/spec_forge/documentation/loader/cache.rb +138 -0
  32. data/lib/spec_forge/documentation/loader.rb +159 -0
  33. data/lib/spec_forge/documentation/openapi/base.rb +33 -0
  34. data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
  35. data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
  36. data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
  37. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
  38. data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
  39. data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
  40. data/lib/spec_forge/documentation/openapi.rb +23 -0
  41. data/lib/spec_forge/documentation.rb +27 -0
  42. data/lib/spec_forge/error.rb +17 -0
  43. data/lib/spec_forge/factory.rb +2 -2
  44. data/lib/spec_forge/filter.rb +3 -4
  45. data/lib/spec_forge/forge.rb +5 -4
  46. data/lib/spec_forge/http/backend.rb +2 -0
  47. data/lib/spec_forge/http/request.rb +14 -3
  48. data/lib/spec_forge/loader.rb +14 -24
  49. data/lib/spec_forge/normalizer/default.rb +51 -0
  50. data/lib/spec_forge/normalizer/definition.rb +248 -0
  51. data/lib/spec_forge/normalizer/validators.rb +99 -0
  52. data/lib/spec_forge/normalizer.rb +356 -199
  53. data/lib/spec_forge/normalizers/_shared.yml +74 -0
  54. data/lib/spec_forge/normalizers/configuration.yml +23 -0
  55. data/lib/spec_forge/normalizers/constraint.yml +8 -0
  56. data/lib/spec_forge/normalizers/expectation.yml +47 -0
  57. data/lib/spec_forge/normalizers/factory.yml +12 -0
  58. data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
  59. data/lib/spec_forge/normalizers/global_context.yml +28 -0
  60. data/lib/spec_forge/normalizers/spec.yml +50 -0
  61. data/lib/spec_forge/runner/adapter.rb +183 -0
  62. data/lib/spec_forge/runner/debug_proxy.rb +3 -3
  63. data/lib/spec_forge/runner/state.rb +4 -5
  64. data/lib/spec_forge/runner.rb +40 -124
  65. data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
  66. data/lib/spec_forge/spec/expectation.rb +7 -3
  67. data/lib/spec_forge/spec.rb +13 -58
  68. data/lib/spec_forge/version.rb +1 -1
  69. data/lib/spec_forge.rb +30 -23
  70. data/lib/templates/openapi.yml.tt +22 -0
  71. data/lib/templates/redoc.html.tt +28 -0
  72. data/lib/templates/swagger.html.tt +59 -0
  73. metadata +92 -14
  74. data/lib/spec_forge/normalizer/configuration.rb +0 -90
  75. data/lib/spec_forge/normalizer/constraint.rb +0 -60
  76. data/lib/spec_forge/normalizer/expectation.rb +0 -105
  77. data/lib/spec_forge/normalizer/factory.rb +0 -78
  78. data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
  79. data/lib/spec_forge/normalizer/global_context.rb +0 -88
  80. data/lib/spec_forge/normalizer/spec.rb +0 -97
  81. /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
  82. /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
  83. /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
@@ -12,21 +12,24 @@ module SpecForge
12
12
  # @example In code
13
13
  # constraint = Constraint.new(
14
14
  # status: 200,
15
- # json: {name: "matcher.eq" => "John"}
15
+ # headers: {response_header: "kind_of.string"},
16
+ # json: {name: {"matcher.eq" => "John"}}
16
17
  # )
17
18
  #
18
- class Constraint < Data.define(:status, :json) # :xml, :html
19
+ class Constraint < Data.define(:status, :headers, :json) # :xml, :html
19
20
  #
20
21
  # Creates a new constraint
21
22
  #
22
23
  # @param status [Integer, String] The expected HTTP status code, or reference to one
24
+ # @param headers [Hash] The expected headers with matchers
23
25
  # @param json [Hash, Array] The expected JSON with matchers
24
26
  #
25
27
  # @return [Constraint] A new constraint instance
26
28
  #
27
- def initialize(status:, json: {})
29
+ def initialize(status:, headers: {}, json: {})
28
30
  super(
29
31
  status: Attribute.from(status),
32
+ headers: Attribute.from(headers),
30
33
  json: Attribute.from(json)
31
34
  )
32
35
  end
@@ -58,7 +61,8 @@ module SpecForge
58
61
  def as_matchers
59
62
  {
60
63
  status: status.resolve_as_matcher,
61
- json: resolve_json_matcher
64
+ json: resolve_json_matcher,
65
+ headers: resolve_hash_matcher(headers)
62
66
  }
63
67
  end
64
68
 
@@ -108,11 +112,15 @@ module SpecForge
108
112
  def resolve_json_matcher
109
113
  case json
110
114
  when HashLike
111
- json.transform_values(&:resolve_as_matcher).stringify_keys
115
+ resolve_hash_matcher(json)
112
116
  else
113
117
  json.resolve_as_matcher
114
118
  end
115
119
  end
120
+
121
+ def resolve_hash_matcher(hash)
122
+ hash.transform_values(&:resolve_as_matcher).stringify_keys
123
+ end
116
124
  end
117
125
  end
118
126
  end
@@ -15,7 +15,10 @@ module SpecForge
15
15
  # json:
16
16
  # name: kind_of.string
17
17
  #
18
- class Expectation < Data.define(:id, :name, :line_number, :debug, :store_as, :constraints)
18
+ class Expectation < Data.define(
19
+ :id, :name, :line_number,
20
+ :debug, :store_as, :documentation, :constraints
21
+ )
19
22
  #
20
23
  # @return [Boolean] True if debugging is enabled
21
24
  #
@@ -34,14 +37,15 @@ module SpecForge
34
37
  # @param line_number [Integer] Line number in source
35
38
  # @param debug [Boolean] Whether to enable debugging
36
39
  # @param store_as [String] Unique Context::Store identifier
40
+ # @param documentation [Boolean] Whether to include in documentation generation
37
41
  # @param expect [Hash] Expected constraints
38
42
  #
39
43
  # @return [Expectation] A new expectation instance
40
44
  #
41
- def initialize(id:, name:, line_number:, debug:, store_as:, expect:)
45
+ def initialize(id:, name:, line_number:, debug:, store_as:, expect:, documentation:)
42
46
  constraints = Constraint.new(**expect)
43
47
 
44
- super(id:, name:, line_number:, debug:, store_as:, constraints:)
48
+ super(id:, name:, line_number:, debug:, store_as:, documentation:, constraints:)
45
49
  end
46
50
 
47
51
  #
@@ -14,61 +14,15 @@ module SpecForge
14
14
  # - expect:
15
15
  # status: 200
16
16
  #
17
- class Spec
17
+ class Spec < Data.define(
18
+ :id, :name, :file_path, :file_name, :line_number,
19
+ :debug, :documentation, :expectations
20
+ )
18
21
  #
19
22
  # @return [Boolean] True if debugging is enabled
20
23
  #
21
24
  attr_predicate :debug
22
25
 
23
- #
24
- # Unique identifier for this spec
25
- #
26
- # @return [String] The spec ID
27
- #
28
- attr_reader :id
29
-
30
- #
31
- # Human-readable name for this spec
32
- #
33
- # @return [String] The spec name
34
- #
35
- attr_reader :name
36
-
37
- #
38
- # Absolute path to the file containing this spec
39
- #
40
- # @return [String] The file path
41
- #
42
- attr_reader :file_path
43
-
44
- #
45
- # Base name of the file without path or extension
46
- #
47
- # @return [String] The file name
48
- #
49
- attr_reader :file_name
50
-
51
- #
52
- # Whether to enable debugging for this spec
53
- #
54
- # @return [Boolean] Debug flag
55
- #
56
- attr_reader :debug
57
-
58
- #
59
- # Line number in the source file where this spec is defined
60
- #
61
- # @return [Integer] The line number
62
- #
63
- attr_reader :line_number
64
-
65
- #
66
- # The expectations to test for this spec
67
- #
68
- # @return [Array<Expectation>] The expectations
69
- #
70
- attr_accessor :expectations
71
-
72
26
  #
73
27
  # Creates a new spec instance
74
28
  #
@@ -78,18 +32,18 @@ module SpecForge
78
32
  # @param file_name [String] Base name of file
79
33
  # @param debug [Boolean] Whether to enable debugging
80
34
  # @param line_number [Integer] Line number in source
35
+ # @param documentation [Boolean] Whether to include in documentation generation
81
36
  # @param expectations [Array<Hash>] Expectation configurations
82
37
  #
83
38
  # @return [Spec] A new spec instance
84
39
  #
85
- def initialize(id:, name:, file_path:, file_name:, debug:, line_number:, expectations:)
86
- @id = id
87
- @name = name
88
- @file_path = file_path
89
- @file_name = file_name
90
- @debug = debug
91
- @line_number = line_number
92
- @expectations = expectations.map { |e| Expectation.new(**e) }
40
+ def initialize(
41
+ id:, name:, file_path:, file_name:, line_number:,
42
+ debug:, documentation:, expectations:
43
+ )
44
+ expectations = expectations.map { |e| Expectation.new(**e) }
45
+
46
+ super
93
47
  end
94
48
 
95
49
  #
@@ -104,6 +58,7 @@ module SpecForge
104
58
  file_name:,
105
59
  debug:,
106
60
  line_number:,
61
+ documentation:,
107
62
  expectations: expectations.map(&:to_h)
108
63
  }
109
64
  end
@@ -4,5 +4,5 @@ module SpecForge
4
4
  #
5
5
  # Current version of SpecForge
6
6
  #
7
- VERSION = "0.6.0"
7
+ VERSION = "0.7.0"
8
8
  end
data/lib/spec_forge.rb CHANGED
@@ -5,15 +5,19 @@ require "logger"
5
5
  require "active_support"
6
6
  require "active_support/core_ext"
7
7
  require "commander"
8
- require "everythingrb"
8
+ require "everythingrb/prelude"
9
+ require "everythingrb/all"
9
10
  require "factory_bot"
10
11
  require "faker"
11
12
  require "faraday"
12
13
  require "mime/types"
14
+ require "openapi3_parser"
13
15
  require "pathname"
14
16
  require "rspec"
17
+ require "sem_version"
15
18
  require "singleton"
16
19
  require "thor"
20
+ require "webrick"
17
21
  require "yaml"
18
22
 
19
23
  #
@@ -57,28 +61,8 @@ module SpecForge
57
61
  # @param expectation_name [String, nil] Optional name of expectation to run
58
62
  #
59
63
  def run(file_name: nil, spec_name: nil, expectation_name: nil)
60
- # Load spec_helper.rb
61
- forge_helper = SpecForge.forge_path.join("forge_helper.rb")
62
- require_relative forge_helper if File.exist?(forge_helper)
63
-
64
- # Validate in case anything was changed
65
- configuration.validate
66
-
67
- # Load factories
68
- Factory.load_and_register
69
-
70
- # Load the specs from their files and create forges from them
71
- forges = Loader.load_from_files.map { |f| Forge.new(*f) }
72
-
73
- # Filter out the specs and expectations
74
- forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
75
-
76
- # Tell the user that we filtered if we did
77
- Filter.announce(forges, file_name:, spec_name:, expectation_name:)
78
-
79
- # Define and run everything
80
- Runner.define(forges)
81
- Runner.run
64
+ forges = Runner.prepare(file_name:, spec_name:, expectation_name:)
65
+ Runner.run(forges, exit_on_finish: true)
82
66
  end
83
67
 
84
68
  #
@@ -99,6 +83,15 @@ module SpecForge
99
83
  @forge_path ||= root.join("spec_forge")
100
84
  end
101
85
 
86
+ #
87
+ # Returns SpecForge's openapi directory
88
+ #
89
+ # @return [Pathname] The spec_forge openapi directory path
90
+ #
91
+ def openapi_path
92
+ @openapi_path ||= forge_path.join("openapi")
93
+ end
94
+
102
95
  #
103
96
  # Returns SpecForge's configuration
104
97
  #
@@ -168,6 +161,19 @@ module SpecForge
168
161
  def register_callback(name, &)
169
162
  Callbacks.register(name, &)
170
163
  end
164
+
165
+ #
166
+ # Generates a unique ID for an object based on hash and object_id
167
+ #
168
+ # @param object [Object] The object to generate an ID for
169
+ #
170
+ # @return [String] A unique ID string
171
+ #
172
+ # @private
173
+ #
174
+ def generate_id(object)
175
+ "#{object.hash.abs.to_s(36)}_#{object.object_id.to_s(36)}"
176
+ end
171
177
  end
172
178
  end
173
179
 
@@ -178,6 +184,7 @@ require_relative "spec_forge/cli"
178
184
  require_relative "spec_forge/configuration"
179
185
  require_relative "spec_forge/context"
180
186
  require_relative "spec_forge/core_ext"
187
+ require_relative "spec_forge/documentation"
181
188
  require_relative "spec_forge/error"
182
189
  require_relative "spec_forge/factory"
183
190
  require_relative "spec_forge/filter"
@@ -0,0 +1,22 @@
1
+ info:
2
+ title: My API
3
+ version: 1.0.0
4
+ description: A description of My API
5
+ contact:
6
+ name: My API Team
7
+ email: api@example.com
8
+ license:
9
+ name: MIT
10
+ url: https://opensource.org/licenses/MIT
11
+
12
+ # servers:
13
+ # - url: https://api.example.com/v1
14
+ # description: Production
15
+ # - url: https://staging-api.example.com/v1
16
+ # description: Staging
17
+ #
18
+ # tags:
19
+ # - name: users
20
+ # description: User account management
21
+ # - name: posts
22
+ # description: Blog post management
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>Redoc</title>
6
+ <!-- needed for adaptive design -->
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
10
+
11
+ <!--
12
+ Redoc doesn't change outer page styles
13
+ -->
14
+ <style>
15
+ body {
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+ </style>
20
+ </head>
21
+
22
+ <body>
23
+ <redoc spec-url="<%= spec_url %>"></redoc>
24
+ <script src=" https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js">
25
+ </script>
26
+ </body>
27
+
28
+ </html>
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Swagger UI</title>
7
+ <style>
8
+ html {
9
+ box-sizing: border-box;
10
+ overflow: -moz-scrollbars-vertical;
11
+ overflow-y: scroll;
12
+ }
13
+
14
+ *,
15
+ *:before,
16
+ *:after {
17
+ box-sizing: inherit;
18
+ }
19
+
20
+ body {
21
+ margin: 0;
22
+ background: #fafafa;
23
+ }
24
+ </style>
25
+
26
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui.min.css"
27
+ integrity="sha512-wWpxfn2bFvPwxuqDyiJbVB0WR3ffSqJNMMryNP07frPJ1h5Xg9HIDMV1wRr1rpxT5E+KTxDrKTuWfGb1RcV8SA=="
28
+ crossorigin="anonymous" referrerpolicy="no-referrer" />
29
+ </head>
30
+
31
+ <body>
32
+ <div id="swagger-ui"></div>
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-bundle.min.js"
34
+ integrity="sha512-nEy/zRjIvuFMSr5ljsQUaUW4l7DoSHz8+SRybclmCjCh3MeF9UaooWYdr/SqjGCiyi4RIvBvn9DxCCV0ZDhiNA=="
35
+ crossorigin="anonymous" referrerpolicy="no-referrer"></script>
36
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-standalone-preset.min.js"
37
+ integrity="sha512-yJlD9FXQ7YaxAKXhviHSt/0KqWDCkLFdCnk0Ti23HXDMEQtHLAAWMHZ+POglC1mx/MOUB//h8kci3U1JYrywpQ=="
38
+ crossorigin="anonymous" referrerpolicy="no-referrer"></script>
39
+
40
+ <script>
41
+ window.onload = function () {
42
+ const ui = SwaggerUIBundle({
43
+ url: "<%= spec_url %>",
44
+ dom_id: "#swagger-ui",
45
+ deepLinking: true,
46
+ presets: [
47
+ SwaggerUIBundle.presets.apis,
48
+ SwaggerUIStandalonePreset
49
+ ],
50
+ plugins: [
51
+ SwaggerUIBundle.plugins.DownloadUrl
52
+ ],
53
+ layout: "StandaloneLayout"
54
+ });
55
+ };
56
+ </script>
57
+ </body>
58
+
59
+ </html>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spec_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-24 00:00:00.000000000 Z
11
+ date: 2025-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.2'
47
+ version: '0.8'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.2'
54
+ version: '0.8'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: factory_bot
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.6'
111
+ - !ruby/object:Gem::Dependency
112
+ name: openapi3_parser
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.10.1
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.10.1
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +136,20 @@ dependencies:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
138
  version: '3.13'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sem_version
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.0'
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: thor
127
155
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +164,20 @@ dependencies:
136
164
  - - "~>"
137
165
  - !ruby/object:Gem::Version
138
166
  version: '1.3'
167
+ - !ruby/object:Gem::Dependency
168
+ name: webrick
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.9'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '1.9'
139
181
  description: Write API tests in YAML without sacrificing power. SpecForge combines
140
182
  RSpec's matcher system, Faker's data generation, and factory patterns into a clean,
141
183
  declarative syntax that eliminates boilerplate while preserving control over your
@@ -179,9 +221,12 @@ files:
179
221
  - lib/spec_forge/cli.rb
180
222
  - lib/spec_forge/cli/actions.rb
181
223
  - lib/spec_forge/cli/command.rb
224
+ - lib/spec_forge/cli/docs.rb
225
+ - lib/spec_forge/cli/docs/generate.rb
182
226
  - lib/spec_forge/cli/init.rb
183
227
  - lib/spec_forge/cli/new.rb
184
228
  - lib/spec_forge/cli/run.rb
229
+ - lib/spec_forge/cli/serve.rb
185
230
  - lib/spec_forge/configuration.rb
186
231
  - lib/spec_forge/context.rb
187
232
  - lib/spec_forge/context/callbacks.rb
@@ -189,7 +234,32 @@ files:
189
234
  - lib/spec_forge/context/store.rb
190
235
  - lib/spec_forge/context/variables.rb
191
236
  - lib/spec_forge/core_ext.rb
237
+ - lib/spec_forge/core_ext/array.rb
192
238
  - lib/spec_forge/core_ext/rspec.rb
239
+ - lib/spec_forge/documentation.rb
240
+ - lib/spec_forge/documentation/builder.rb
241
+ - lib/spec_forge/documentation/document.rb
242
+ - lib/spec_forge/documentation/document/operation.rb
243
+ - lib/spec_forge/documentation/document/parameter.rb
244
+ - lib/spec_forge/documentation/document/request_body.rb
245
+ - lib/spec_forge/documentation/document/response.rb
246
+ - lib/spec_forge/documentation/document/response_body.rb
247
+ - lib/spec_forge/documentation/generators.rb
248
+ - lib/spec_forge/documentation/generators/base.rb
249
+ - lib/spec_forge/documentation/generators/openapi.rb
250
+ - lib/spec_forge/documentation/generators/openapi/base.rb
251
+ - lib/spec_forge/documentation/generators/openapi/error_formatter.rb
252
+ - lib/spec_forge/documentation/generators/openapi/v3_0.rb
253
+ - lib/spec_forge/documentation/loader.rb
254
+ - lib/spec_forge/documentation/loader/cache.rb
255
+ - lib/spec_forge/documentation/openapi.rb
256
+ - lib/spec_forge/documentation/openapi/base.rb
257
+ - lib/spec_forge/documentation/openapi/v3_0/example.rb
258
+ - lib/spec_forge/documentation/openapi/v3_0/media_type.rb
259
+ - lib/spec_forge/documentation/openapi/v3_0/operation.rb
260
+ - lib/spec_forge/documentation/openapi/v3_0/response.rb
261
+ - lib/spec_forge/documentation/openapi/v3_0/schema.rb
262
+ - lib/spec_forge/documentation/openapi/v3_0/tag.rb
193
263
  - lib/spec_forge/error.rb
194
264
  - lib/spec_forge/factory.rb
195
265
  - lib/spec_forge/filter.rb
@@ -202,14 +272,19 @@ files:
202
272
  - lib/spec_forge/loader.rb
203
273
  - lib/spec_forge/matchers.rb
204
274
  - lib/spec_forge/normalizer.rb
205
- - lib/spec_forge/normalizer/configuration.rb
206
- - lib/spec_forge/normalizer/constraint.rb
207
- - lib/spec_forge/normalizer/expectation.rb
208
- - lib/spec_forge/normalizer/factory.rb
209
- - lib/spec_forge/normalizer/factory_reference.rb
210
- - lib/spec_forge/normalizer/global_context.rb
211
- - lib/spec_forge/normalizer/spec.rb
275
+ - lib/spec_forge/normalizer/default.rb
276
+ - lib/spec_forge/normalizer/definition.rb
277
+ - lib/spec_forge/normalizer/validators.rb
278
+ - lib/spec_forge/normalizers/_shared.yml
279
+ - lib/spec_forge/normalizers/configuration.yml
280
+ - lib/spec_forge/normalizers/constraint.yml
281
+ - lib/spec_forge/normalizers/expectation.yml
282
+ - lib/spec_forge/normalizers/factory.yml
283
+ - lib/spec_forge/normalizers/factory_reference.yml
284
+ - lib/spec_forge/normalizers/global_context.yml
285
+ - lib/spec_forge/normalizers/spec.yml
212
286
  - lib/spec_forge/runner.rb
287
+ - lib/spec_forge/runner/adapter.rb
213
288
  - lib/spec_forge/runner/callbacks.rb
214
289
  - lib/spec_forge/runner/debug_proxy.rb
215
290
  - lib/spec_forge/runner/listener.rb
@@ -220,9 +295,12 @@ files:
220
295
  - lib/spec_forge/spec/expectation/constraint.rb
221
296
  - lib/spec_forge/type.rb
222
297
  - lib/spec_forge/version.rb
223
- - lib/templates/forge_helper.tt
224
- - lib/templates/new_factory.tt
225
- - lib/templates/new_spec.tt
298
+ - lib/templates/forge_helper.rb.tt
299
+ - lib/templates/new_factory.yml.tt
300
+ - lib/templates/new_spec.yml.tt
301
+ - lib/templates/openapi.yml.tt
302
+ - lib/templates/redoc.html.tt
303
+ - lib/templates/swagger.html.tt
226
304
  homepage: https://github.com/itsthedevman/spec_forge
227
305
  licenses:
228
306
  - MIT
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpecForge
4
- class Normalizer
5
- #
6
- # Normalizes configuration hash structure for SpecForge
7
- #
8
- # Ensures that the global configuration has the correct structure
9
- # and default values for all required settings.
10
- #
11
- class Configuration < Normalizer
12
- #
13
- # Defines the normalized structure for configuration validation
14
- #
15
- # Specifies validation rules for configuration attributes:
16
- # - Enforces specific data types
17
- # - Provides default values
18
- # - Supports alternative key names
19
- #
20
- # @return [Hash] Configuration attribute validation rules
21
- #
22
- STRUCTURE = {
23
- base_url: SHARED_ATTRIBUTES[:base_url].except(:default), # Make it required
24
- headers: SHARED_ATTRIBUTES[:headers],
25
- query: SHARED_ATTRIBUTES[:query],
26
- factories: {
27
- type: Hash,
28
- default: {},
29
- structure: {
30
- auto_discover: {
31
- type: [TrueClass, FalseClass],
32
- default: true
33
- },
34
- paths: {
35
- type: Array,
36
- default: []
37
- }
38
- }
39
- },
40
- on_debug: {
41
- type: Proc
42
- }
43
- }.freeze
44
- end
45
-
46
- # On Normalizer
47
- class << self
48
- #
49
- # Generates an empty configuration hash
50
- #
51
- # @return [Hash] Default configuration hash
52
- #
53
- def default_configuration
54
- Configuration.default
55
- end
56
-
57
- #
58
- # Normalizes a configuration hash with validation
59
- #
60
- # @param input [Hash] The hash to normalize
61
- #
62
- # @return [Hash] A normalized hash with defaults applied
63
- #
64
- # @raise [Error::InvalidStructureError] If validation fails
65
- #
66
- def normalize_configuration!(input)
67
- raise_errors! do
68
- normalize_configuration(input)
69
- end
70
- end
71
-
72
- #
73
- # Normalize a configuration hash
74
- #
75
- # @param configuration [Hash] Configuration hash
76
- #
77
- # @return [Array] [normalized_hash, errors]
78
- #
79
- # @private
80
- #
81
- def normalize_configuration(configuration)
82
- if !Type.hash?(configuration)
83
- raise Error::InvalidTypeError.new(configuration, Hash, for: "configuration")
84
- end
85
-
86
- Normalizer::Configuration.new("configuration", configuration).normalize
87
- end
88
- end
89
- end
90
- end