skooma 0.3.3 → 0.3.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76912cb6b09166983cae2e1e03a104d16351116d2076e3c0b27e24491002a89b
4
- data.tar.gz: 65f728f3eda604da0eb7750f7f6cad612cd98870626c8fc766c1554336a6efe1
3
+ metadata.gz: ecc89f4b7b9744168f5847936e80a6e43d1d9ab32e31df584adaf5c85eaa27af
4
+ data.tar.gz: bc186b5d374ea4fd8703b8bb58c1dc921760e6731de266e7914035868c921b47
5
5
  SHA512:
6
- metadata.gz: 2253d20d61746403cd726d47092ba45c7614a283a1af9becfea8fcf387bcd1c9f6f4d52b48efd6ebd7284df52ac5ac944958f6997011f9b8da0b6602d5c21f0d
7
- data.tar.gz: 9b220779384f848d308708a1ccd704314ca98d8a3ffc9683fe03962f10a997bc6465835e495638a83a2f955d19191a330e524050deef0345c858255421d2fd1c
6
+ metadata.gz: 45660ab6ee53e2be9822ee9fccda2850719f10d230a120e4cf1b72855fb88e0cf263f72612e8648b0dccced1cfc08e5e08a4cc462c7f7f8b50fe5122a42a49a3
7
+ data.tar.gz: 798b59438bdd1ccf3cea2633372f581118dc979fc6c0347b5c0c98b070c4b8a3e765343606cdd756b4b3133d212cf3e2bc06a06bee23273f9a4f4687f1146f5e
data/CHANGELOG.md CHANGED
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.5] - 2025-07-31
11
+
12
+ ### Fixed
13
+
14
+ - Fix the Enforce Access mode with additional properties. ([@aburgel])
15
+ - Introduce coverage storage to fix Minitest parallel workers reports. ([@skarlcf])
16
+ - Introduce `use_patterns_for_path_matching` option to allow using `path` patterns for path matching. ([@jandouwebeekman])
17
+
18
+ ```ruby
19
+ # spec/rails_helper.rb
20
+
21
+ RSpec.configure do |config|
22
+ # To enable path patterns, pass `use_patterns_for_path_matching: true` option:
23
+ config.include Skooma::RSpec[Rails.root.join("docs", "openapi.yml"), use_patterns_for_path_matching: true], type: :request
24
+ end
25
+ ```
26
+
27
+ ## [0.3.4] - 2025-01-14
28
+
29
+ ### Added
30
+
31
+ - Experimental support for `readOnly` and `writeOnly` keywords. ([@skryukov])
32
+
33
+ ```ruby
34
+ # spec/rails_helper.rb
35
+
36
+ RSpec.configure do |config|
37
+ # To enable support for readOnly and writeOnly keywords, pass `enforce_access_modes: true` option:
38
+ config.include Skooma::RSpec[Rails.root.join("docs", "openapi.yml"), enforce_access_modes: true], type: :request
39
+ end
40
+ ```
41
+ - Support fallback parsers for vendor-specific media types. ([@pvcarrera], [@skryukov])
42
+
10
43
  ## [0.3.3] - 2024-10-14
11
44
 
12
45
  ### Fixed
@@ -124,11 +157,17 @@ and this project adheres to [Semantic Versioning].
124
157
 
125
158
  - Initial implementation. ([@skryukov])
126
159
 
160
+ [@aburgel]: https://github.com/aburgel
127
161
  [@barnaclebarnes]: https://github.com/barnaclebarnes
162
+ [@jandouwebeekman]: https://github.com/jandouwebeekman
163
+ [@pvcarrera]: https://github.com/pvcarrera
164
+ [@skarlcf]: https://github.com/skarlcf
128
165
  [@skryukov]: https://github.com/skryukov
129
166
  [@ursm]: https://github.com/ursm
130
167
 
131
- [Unreleased]: https://github.com/skryukov/skooma/compare/v0.3.3...HEAD
168
+ [Unreleased]: https://github.com/skryukov/skooma/compare/v0.3.5...HEAD
169
+ [0.3.5]: https://github.com/skryukov/skooma/compare/v0.3.4...v0.3.5
170
+ [0.3.4]: https://github.com/skryukov/skooma/compare/v0.3.3...v0.3.4
132
171
  [0.3.3]: https://github.com/skryukov/skooma/compare/v0.3.2...v0.3.3
133
172
  [0.3.2]: https://github.com/skryukov/skooma/compare/v0.3.1...v0.3.2
134
173
  [0.3.1]: https://github.com/skryukov/skooma/compare/v0.3.0...v0.3.1
data/README.md CHANGED
@@ -128,6 +128,13 @@ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, path_p
128
128
  # To enable coverage, pass `coverage: :report` option,
129
129
  # and to raise an error when an operation is not covered, pass `coverage: :strict` option:
130
130
  ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, coverage: :report], type: :request
131
+
132
+ # EXPERIMENTAL
133
+ # To enable support for readOnly and writeOnly keywords, pass `enforce_access_modes: true` option:
134
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, enforce_access_modes: true], type: :request
135
+
136
+ # To enable custom regex patterns for path parameters, pass `use_patterns_for_path_matching: true` option.
137
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, use_patterns_for_path_matching: true], type: :request
131
138
  ```
132
139
 
133
140
  #### Validate OpenAPI document
@@ -6,16 +6,38 @@ module Skooma
6
6
  DEFAULT_PARSER = ->(body, **_options) { body }
7
7
 
8
8
  def [](media_type)
9
- parsers[media_type.to_s.strip.downcase] || DEFAULT_PARSER
9
+ key = normalize_media_type(media_type)
10
+ parsers[key] ||
11
+ find_suffix_parser(key) ||
12
+ find_fallback_parser(key) ||
13
+ DEFAULT_PARSER
10
14
  end
11
15
 
12
16
  attr_accessor :parsers
13
17
 
14
18
  def register(*media_types, parser)
15
19
  media_types.each do |media_type|
16
- parsers[media_type.to_s.strip.downcase] = parser
20
+ parsers[normalize_media_type(media_type)] = parser
17
21
  end
18
22
  end
23
+
24
+ private
25
+
26
+ def normalize_media_type(media_type)
27
+ media_type.to_s.strip.downcase
28
+ end
29
+
30
+ def find_suffix_parser(media_type)
31
+ return unless media_type.include?("+")
32
+
33
+ suffix = media_type.split("+").last
34
+ parsers["application/#{suffix}"]
35
+ end
36
+
37
+ def find_fallback_parser(media_type)
38
+ type = media_type.split("/").first
39
+ parsers["#{type}/*"] || parsers["*/*"]
40
+ end
19
41
  end
20
42
  self.parsers = {}
21
43
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  class NoopCoverage
3
5
  def track_request(*)
@@ -24,7 +26,7 @@ module Skooma
24
26
  end
25
27
  end
26
28
 
27
- def self.new(schema, mode: nil, format: nil)
29
+ def self.new(schema, mode: nil, format: nil, storage: nil)
28
30
  case mode
29
31
  when nil, false
30
32
  NoopCoverage.new
@@ -35,14 +37,23 @@ module Skooma
35
37
  end
36
38
  end
37
39
 
38
- attr_reader :mode, :format, :defined_paths, :covered_paths, :schema
40
+ attr_reader :mode, :format, :defined_paths, :storage, :schema
41
+ attr_accessor :covered_paths
39
42
 
40
- def initialize(schema, mode:, format:)
43
+ def initialize(schema, mode:, format:, storage:)
41
44
  @schema = schema
42
45
  @mode = mode
43
46
  @format = format || SimpleReport
44
- @defined_paths = find_defined_paths(schema)
45
- @covered_paths = Set.new
47
+ @storage = storage || CoverageStore.new
48
+
49
+ stored_data = @storage.load_data
50
+ @defined_paths = stored_data[:defined_paths]
51
+ @covered_paths = stored_data[:covered_paths]
52
+
53
+ if @defined_paths.empty?
54
+ @defined_paths = find_defined_paths(schema)
55
+ @storage.save_data(@defined_paths, @covered_paths)
56
+ end
46
57
  end
47
58
 
48
59
  def track_request(result)
@@ -57,6 +68,7 @@ module Skooma
57
68
  end
58
69
  end
59
70
  covered_paths << operation
71
+ storage.save_data(Set.new, Set.new([operation]))
60
72
  end
61
73
 
62
74
  def uncovered_paths
@@ -68,7 +80,10 @@ module Skooma
68
80
  end
69
81
 
70
82
  def report
83
+ stored_data = storage.load_data
84
+ self.covered_paths = stored_data[:covered_paths]
71
85
  format.new(self).report
86
+ storage.clear
72
87
  exit 1 if mode == :strict && uncovered_paths.any?
73
88
  end
74
89
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ class CoverageStore
5
+ DEFAULT_FILE_PATH = File.join(Dir.pwd, "tmp", "skooma_coverage.json")
6
+
7
+ attr_reader :file_path
8
+
9
+ def initialize(file_path: DEFAULT_FILE_PATH)
10
+ @file_path = file_path
11
+ ensure_file_exists
12
+ end
13
+
14
+ def load_data
15
+ with_lock("r") do |file|
16
+ parse_data(file.read)
17
+ end
18
+ end
19
+
20
+ def save_data(new_defined_paths, new_covered_paths)
21
+ with_lock("r+") do |file|
22
+ existing_data = parse_data(file.read)
23
+ merged_data = merge_data(existing_data, new_defined_paths, new_covered_paths)
24
+
25
+ file.rewind
26
+ file.write(JSON.generate(merged_data))
27
+ file.flush
28
+ file.truncate(file.pos)
29
+ end
30
+ end
31
+
32
+ def clear
33
+ File.delete(file_path) if File.exist?(file_path)
34
+ end
35
+
36
+ private
37
+
38
+ def ensure_file_exists
39
+ FileUtils.mkdir_p(File.dirname(@file_path))
40
+ FileUtils.touch(@file_path) unless File.exist?(@file_path)
41
+ end
42
+
43
+ def parse_data(content)
44
+ return {defined_paths: Set.new, covered_paths: Set.new} if content.strip.empty?
45
+
46
+ data = JSON.parse(content, symbolize_names: true)
47
+ {
48
+ defined_paths: Set.new(data[:defined_paths]),
49
+ covered_paths: Set.new(data[:covered_paths])
50
+ }
51
+ end
52
+
53
+ def merge_data(existing_data, new_defined_paths, new_covered_paths)
54
+ {
55
+ defined_paths: (existing_data[:defined_paths] | new_defined_paths).to_a,
56
+ covered_paths: (existing_data[:covered_paths] | new_covered_paths).to_a
57
+ }
58
+ end
59
+
60
+ def with_lock(mode)
61
+ File.open(@file_path, mode) do |file|
62
+ file.flock(File::LOCK_EX)
63
+ yield(file)
64
+ ensure
65
+ file.flock(File::LOCK_UN)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -16,6 +16,9 @@ module Skooma
16
16
  Skooma::Keywords::OAS31::Dialect::OneOf,
17
17
  Skooma::Keywords::OAS31::Dialect::Discriminator,
18
18
  Skooma::Keywords::OAS31::Dialect::Xml,
19
+ Skooma::Keywords::OAS31::Dialect::Properties,
20
+ Skooma::Keywords::OAS31::Dialect::AdditionalProperties,
21
+ Skooma::Keywords::OAS31::Dialect::Required,
19
22
  Skooma::Keywords::OAS31::Dialect::ExternalDocs,
20
23
  Skooma::Keywords::OAS31::Dialect::Example
21
24
  )
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Keywords
5
+ module OAS31
6
+ module Dialect
7
+ class AdditionalProperties < JSONSkooma::Keywords::Applicator::AdditionalProperties
8
+ self.key = "additionalProperties"
9
+ self.instance_types = "object"
10
+ self.value_schema = :schema
11
+ self.depends_on = %w[properties patternProperties]
12
+
13
+ def evaluate(instance, result)
14
+ known_property_names = result.sibling(instance, "properties")&.schema_node&.keys || []
15
+ known_property_patterns = (result.sibling(instance, "patternProperties")&.schema_node&.keys || [])
16
+ .map { |pattern| Regexp.new(pattern) }
17
+
18
+ forbidden = []
19
+
20
+ if json.root.enforce_access_modes?
21
+ only_key = result.path.include?("responses") ? "writeOnly" : "readOnly"
22
+ properties_result = result.sibling(instance, "properties")
23
+ instance.each_key do |name|
24
+ res = properties_result&.children&.[](instance[name]&.path)&.[]name
25
+ forbidden << name.tap { puts "adding #{name}" } if res && annotation_exists?(res, key: only_key)
26
+ end
27
+ end
28
+
29
+ annotation = []
30
+ error = []
31
+
32
+ instance.each do |name, item|
33
+ if forbidden.include?(name) || !known_property_names.include?(name) && known_property_patterns.none? { |pattern| pattern.match?(name) }
34
+ if json.evaluate(item, result).passed?
35
+ annotation << name
36
+ else
37
+ error << name
38
+ # reset to success for the next iteration
39
+ result.success
40
+ end
41
+ end
42
+ end
43
+ return result.annotate(annotation) if error.empty?
44
+
45
+ result.failure(error)
46
+ end
47
+
48
+ private
49
+
50
+ def annotation_exists?(result, key:)
51
+ return result if result.key == key && result.annotation
52
+
53
+ result.each_children do |child|
54
+ return child if annotation_exists?(child, key: key)
55
+ end
56
+
57
+ nil
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Keywords
5
+ module OAS31
6
+ module Dialect
7
+ class Properties < JSONSkooma::Keywords::Applicator::Properties
8
+ self.key = "properties"
9
+ self.instance_types = "object"
10
+ self.value_schema = :object_of_schemas
11
+
12
+ def evaluate(instance, result)
13
+ annotation = []
14
+ err_names = []
15
+ instance.each do |name, item|
16
+ next unless json.value.key?(name)
17
+
18
+ result.call(item, name) do |subresult|
19
+ json[name].evaluate(item, subresult)
20
+ if ignored_with_only_key?(subresult)
21
+ subresult.discard
22
+ elsif subresult.passed?
23
+ annotation << name
24
+ else
25
+ err_names << name
26
+ end
27
+ end
28
+ end
29
+
30
+ return result.annotate(annotation) if err_names.empty?
31
+
32
+ result.failure("Properties #{err_names.join(", ")} are invalid")
33
+ end
34
+
35
+ private
36
+
37
+ def ignored_with_only_key?(subresult)
38
+ return false unless json.root.enforce_access_modes?
39
+
40
+ if subresult.parent.path.include?("responses")
41
+ subresult.children["readOnly"]&.value == true
42
+ else
43
+ subresult.children["writeOnly"]&.value == true
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Keywords
5
+ module OAS31
6
+ module Dialect
7
+ class Required < JSONSkooma::Keywords::Validation::Required
8
+ self.key = "required"
9
+ self.instance_types = "object"
10
+ self.depends_on = %w[properties]
11
+
12
+ def evaluate(instance, result)
13
+ missing = required_keys.reject { |key| instance.key?(key) }
14
+ return if missing.none?
15
+
16
+ if json.root.enforce_access_modes?
17
+ properties_schema = result.sibling(instance, "properties")&.schema_node || {}
18
+ only_key = result.path.include?("responses") ? "writeOnly" : "readOnly"
19
+ ignore = []
20
+ missing.each do |name|
21
+ next unless properties_schema.key?(name)
22
+
23
+ result.call(nil, name) do |subresult|
24
+ properties_schema[name].evaluate(nil, subresult)
25
+ ignore << name if annotation_exists?(subresult, key: only_key)
26
+ subresult.discard
27
+ end
28
+ end
29
+
30
+ return if (missing - ignore).none?
31
+ end
32
+
33
+ result.failure(missing_keys_message(missing))
34
+ end
35
+
36
+ private
37
+
38
+ def annotation_exists?(result, key:)
39
+ return result if result.key == key && result.annotation
40
+
41
+ result.each_children do |child|
42
+ return child if annotation_exists?(child, key: key)
43
+ end
44
+
45
+ nil
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -44,7 +44,7 @@ module Skooma
44
44
  end
45
45
  end
46
46
 
47
- def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/", path_prefix: "", **params)
47
+ def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/", path_prefix: "", enforce_access_modes: false, use_patterns_for_path_matching: false, **params)
48
48
  super()
49
49
 
50
50
  registry = create_test_registry
@@ -57,8 +57,14 @@ module Skooma
57
57
  )
58
58
  @schema = registry.schema(URI.parse("#{source_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
59
59
  @schema.path_prefix = path_prefix
60
+ @schema.enforce_access_modes = enforce_access_modes
60
61
 
61
- @coverage = Coverage.new(@schema, mode: params[:coverage], format: params[:coverage_format])
62
+ storage = Skooma::CoverageStore.new(
63
+ file_path: File.join(Dir.pwd, "tmp", "skooma_coverage_#{Digest::SHA256.hexdigest(source_uri)[0..8]}.json")
64
+ )
65
+ @coverage = Coverage.new(@schema, mode: params[:coverage], format: params[:coverage_format], storage: storage)
66
+
67
+ @schema.use_patterns_for_path_matching = use_patterns_for_path_matching
62
68
 
63
69
  include DefaultHelperMethods
64
70
  include helper_methods_module
@@ -39,7 +39,9 @@ module Skooma
39
39
  def initialize(openapi_path, **params)
40
40
  super(HelperMethods, openapi_path, **params)
41
41
 
42
- ::Minitest.after_run { coverage.report }
42
+ ::Minitest.after_run do
43
+ coverage.report
44
+ end
43
45
  end
44
46
  end
45
47
  end
@@ -13,14 +13,6 @@ module Skooma
13
13
 
14
14
  def initialize(parent_schema, value)
15
15
  super
16
- @regexp_map = json.filter_map do |path, subschema|
17
- next unless path.include?("{") && path.include?("}")
18
-
19
- path_regex = path.gsub(ROUTE_REGEXP, "(?<\\1>[^/?#]+)")
20
- path_regex = Regexp.new("\\A#{path_regex}\\z")
21
-
22
- [path, path_regex, subschema]
23
- end
24
16
  end
25
17
 
26
18
  def evaluate(instance, result)
@@ -44,11 +36,34 @@ module Skooma
44
36
 
45
37
  private
46
38
 
39
+ def regexp_map
40
+ @regexp_map ||= json.filter_map do |path, subschema|
41
+ next unless path.include?("{") && path.include?("}")
42
+
43
+ pattern_hash = if json.root.use_patterns_for_path_matching?
44
+ create_hash_of_patterns(subschema)
45
+ else
46
+ {}
47
+ end
48
+
49
+ path_regex = path.gsub(ROUTE_REGEXP) do |match|
50
+ param = match[1..-2]
51
+ if pattern_hash.key?(param)
52
+ "(?<#{param}>#{pattern_hash[param]})"
53
+ else
54
+ "(?<#{param}>[^/?#]+)"
55
+ end
56
+ end
57
+ path_regex = Regexp.new("\\A#{path_regex}\\z")
58
+
59
+ [path, path_regex, subschema]
60
+ end
61
+ end
62
+
47
63
  def find_route(instance_path)
48
64
  instance_path = instance_path.delete_prefix(json.root.path_prefix)
49
65
  return [instance_path, {}, json[instance_path]] if json.key?(instance_path)
50
-
51
- @regexp_map.reduce(nil) do |result, (path, path_regex, subschema)|
66
+ regexp_map.reduce(nil) do |result, (path, path_regex, subschema)|
52
67
  next result unless path.include?("{") && path.include?("}")
53
68
 
54
69
  match = instance_path.match(path_regex)
@@ -58,6 +73,57 @@ module Skooma
58
73
  [path, match.named_captures, subschema]
59
74
  end
60
75
  end
76
+
77
+ def get_child(parent, child_name)
78
+ if parent
79
+ parent_to_use = if parent.key?("$ref")
80
+ parent.resolve_ref(parent["$ref"])
81
+ else
82
+ parent
83
+ end
84
+ if parent_to_use.key?(child_name)
85
+ parent_to_use[child_name]
86
+ end
87
+ end
88
+ end
89
+
90
+ def create_hash_of_patterns(subschema)
91
+ output = {}
92
+ parameters = []
93
+ parameters = parameters.concat(subschema["parameters"]) if subschema["parameters"]
94
+ %w[get post put patch delete].each do |method|
95
+ parameters = parameters.concat(subschema[method]["parameters"]) if subschema[method] && subschema[method]["parameters"]
96
+ end
97
+ parameters.each do |parameter|
98
+ if get_child(parameter, "in") == "path"
99
+ pattern = "[^/?#]+"
100
+ new_pattern = get_child(parameter, "pattern")
101
+ pattern = new_pattern if new_pattern
102
+ new_pattern = get_child(get_child(parameter, "schema"), "pattern")
103
+ pattern = new_pattern if new_pattern
104
+
105
+ output[get_child(parameter, "name").to_s] = filter_pattern(pattern)
106
+ end
107
+ end
108
+ output
109
+ end
110
+
111
+ def filter_pattern(pattern)
112
+ to_return = pattern.to_s
113
+ if to_return.start_with?("^")
114
+ to_return = to_return[1..]
115
+ end
116
+ if to_return.start_with?('\A')
117
+ to_return = to_return[2..]
118
+ end
119
+ if to_return.end_with?("$")
120
+ to_return = to_return[0..-2]
121
+ end
122
+ if to_return.end_with?('\Z', '\z')
123
+ to_return = to_return[0..-3]
124
+ end
125
+ to_return
126
+ end
61
127
  end
62
128
  end
63
129
  end
@@ -39,6 +39,26 @@ module Skooma
39
39
  @path_prefix = @path_prefix.delete_suffix("/") if @path_prefix.end_with?("/")
40
40
  end
41
41
 
42
+ def enforce_access_modes=(value)
43
+ raise ArgumentError, "Enforce access modes must be a boolean" unless [true, false].include?(value)
44
+
45
+ @enforce_access_modes = value
46
+ end
47
+
48
+ def enforce_access_modes?
49
+ @enforce_access_modes
50
+ end
51
+
52
+ def use_patterns_for_path_matching=(value)
53
+ raise ArgumentError, "Use patterns for path matching must be a boolean" unless [true, false].include?(value)
54
+
55
+ @use_patterns_for_path_matching = value
56
+ end
57
+
58
+ def use_patterns_for_path_matching?
59
+ @use_patterns_for_path_matching
60
+ end
61
+
42
62
  def path_prefix
43
63
  @path_prefix || ""
44
64
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skooma
4
- VERSION = "0.3.3"
4
+ VERSION = "0.3.5"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skooma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: zeitwerk
@@ -30,14 +29,14 @@ dependencies:
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: 0.2.0
32
+ version: 0.2.5
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: 0.2.0
39
+ version: 0.2.5
41
40
  description: Apply a documentation-first approach to API development.
42
41
  email:
43
42
  - me@skryukov.dev
@@ -55,16 +54,20 @@ files:
55
54
  - lib/skooma.rb
56
55
  - lib/skooma/body_parsers.rb
57
56
  - lib/skooma/coverage.rb
57
+ - lib/skooma/coverage_store.rb
58
58
  - lib/skooma/dialects/oas_3_1.rb
59
59
  - lib/skooma/env_mapper.rb
60
60
  - lib/skooma/inflector.rb
61
61
  - lib/skooma/instance.rb
62
62
  - lib/skooma/keywords/oas_3_1.rb
63
+ - lib/skooma/keywords/oas_3_1/dialect/additional_properties.rb
63
64
  - lib/skooma/keywords/oas_3_1/dialect/any_of.rb
64
65
  - lib/skooma/keywords/oas_3_1/dialect/discriminator.rb
65
66
  - lib/skooma/keywords/oas_3_1/dialect/example.rb
66
67
  - lib/skooma/keywords/oas_3_1/dialect/external_docs.rb
67
68
  - lib/skooma/keywords/oas_3_1/dialect/one_of.rb
69
+ - lib/skooma/keywords/oas_3_1/dialect/properties.rb
70
+ - lib/skooma/keywords/oas_3_1/dialect/required.rb
68
71
  - lib/skooma/keywords/oas_3_1/dialect/xml.rb
69
72
  - lib/skooma/keywords/oas_3_1/schema.rb
70
73
  - lib/skooma/matchers/be_valid_document.rb
@@ -149,7 +152,6 @@ metadata:
149
152
  homepage_uri: https://github.com/skryukov/skooma
150
153
  source_code_uri: https://github.com/skryukov/skooma
151
154
  rubygems_mfa_required: 'true'
152
- post_install_message:
153
155
  rdoc_options: []
154
156
  require_paths:
155
157
  - lib
@@ -164,8 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
166
  - !ruby/object:Gem::Version
165
167
  version: '0'
166
168
  requirements: []
167
- rubygems_version: 3.5.17
168
- signing_key:
169
+ rubygems_version: 3.6.7
169
170
  specification_version: 4
170
171
  summary: Validate API implementations against OpenAPI documents.
171
172
  test_files: []