json_schemer 0.2.15 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +16 -5
- data/CHANGELOG.md +18 -0
- data/Gemfile.lock +23 -12
- data/README.md +77 -2
- data/bin/hostname_character_classes +42 -0
- data/bin/rake +29 -0
- data/exe/json_schemer +62 -0
- data/json_schemer.gemspec +4 -12
- data/lib/json_schemer/cached_resolver.rb +16 -0
- data/lib/json_schemer/ecma_regexp.rb +51 -0
- data/lib/json_schemer/format/hostname.rb +58 -0
- data/lib/json_schemer/format/uri_template.rb +34 -0
- data/lib/json_schemer/format.rb +37 -26
- data/lib/json_schemer/schema/base.rb +152 -112
- data/lib/json_schemer/schema/draft4.json +149 -0
- data/lib/json_schemer/schema/draft6.json +155 -0
- data/lib/json_schemer/schema/draft7.json +172 -0
- data/lib/json_schemer/version.rb +1 -1
- data/lib/json_schemer.rb +37 -18
- metadata +32 -21
- data/lib/json_schemer/cached_ref_resolver.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 460e70448c0b37c65fd4e51f046036336b17121cc716c197fe80f2e2517f45e7
|
4
|
+
data.tar.gz: f06d11eab9bb88c45a018c8913d6a5d2e33162064c9f995e5772beb811e68ed6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83549b328ba3067ed206a8bdea448e34ba274c7c5a7857d38793d74f0691339f845b0e39fdbb0ff8c3869dfbaf0dcd13380c458c80397b100a848f96443bf691
|
7
|
+
data.tar.gz: 106314824f1805e7d2306c10c696d4503b54b4d9203321e37816242c30598c3870ec40ade53dc08692ec3b2a35f18360c96211f0b8af44fd4414931da4e5b1e5
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,16 +1,27 @@
|
|
1
1
|
name: ci
|
2
2
|
on: [push, pull_request]
|
3
3
|
jobs:
|
4
|
-
|
4
|
+
test:
|
5
5
|
strategy:
|
6
|
+
fail-fast: false
|
6
7
|
matrix:
|
7
|
-
|
8
|
-
|
8
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
9
|
+
ruby: [2.5, 2.6, 2.7, 3.0, 3.1, 3.2, head, jruby, jruby-head, truffleruby, truffleruby-head]
|
10
|
+
exclude:
|
11
|
+
- os: windows-latest
|
12
|
+
ruby: truffleruby
|
13
|
+
- os: windows-latest
|
14
|
+
ruby: truffleruby-head
|
15
|
+
runs-on: ${{ matrix.os }}
|
9
16
|
steps:
|
10
17
|
- uses: actions/checkout@v2
|
11
18
|
- uses: ruby/setup-ruby@v1
|
12
19
|
with:
|
13
20
|
ruby-version: ${{ matrix.ruby }}
|
21
|
+
bundler-cache: true
|
14
22
|
- run: |
|
15
|
-
|
16
|
-
|
23
|
+
mkdir -p tmp/gems
|
24
|
+
gem build json_schemer.gemspec
|
25
|
+
gem install --local --ignore-dependencies --no-document --install-dir tmp/gems json_schemer-*.gem
|
26
|
+
rm json_schemer-*.gem
|
27
|
+
bin/rake test
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [1.0.0] - 2023-05-26
|
4
|
+
|
5
|
+
### Breaking Changes
|
6
|
+
|
7
|
+
- Ruby 2.4 is no longer supported.
|
8
|
+
- The default `regexp_resolver` is now `ruby`, which passes patterns directly to `Regexp`. The previous default, `ecma`, rewrites patterns to behave more like Javascript (ECMA-262) regular expressions:
|
9
|
+
- Beginning of string: `^` -> `\A`
|
10
|
+
- End of string: `$` -> `\z`
|
11
|
+
- Space: `\s` -> `[\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
|
12
|
+
- Non-space: `\S` -> `[^\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
|
13
|
+
- Invalid ECMA-262 regular expressions raise `JSONSchemer::InvalidEcmaRegexp` when `regexp_resolver` is set to `ecma`.
|
14
|
+
- Embedded subschemas (ie, subschemas referenced by `$id`) can only be found under "known" keywords (eg, `definitions`). Previously, the entire schema object was scanned for `$id`.
|
15
|
+
- Empty fragments are now removed from `$ref` URIs before calling `ref_resolver`.
|
16
|
+
- Refs that are fragment-only JSON pointers with special characters must use the proper encoding (eg, `"$ref": "#/definitions/some-%7Bid%7D"`).
|
17
|
+
|
18
|
+
[1.0.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v1.0.0
|
data/Gemfile.lock
CHANGED
@@ -1,24 +1,34 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
json_schemer (0.
|
5
|
-
ecma-re-validator (~> 0.2)
|
4
|
+
json_schemer (1.0.1)
|
6
5
|
hana (~> 1.3)
|
7
|
-
regexp_parser (~>
|
8
|
-
|
6
|
+
regexp_parser (~> 2.0)
|
7
|
+
simpleidn (~> 0.2)
|
9
8
|
|
10
9
|
GEM
|
11
10
|
remote: https://rubygems.org/
|
12
11
|
specs:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
docile (1.4.0)
|
13
|
+
hana (1.3.7)
|
14
|
+
minitest (5.15.0)
|
15
|
+
rake (13.0.6)
|
16
|
+
regexp_parser (2.6.1)
|
17
|
+
simplecov (0.22.0)
|
18
|
+
docile (~> 1.1)
|
19
|
+
simplecov-html (~> 0.11)
|
20
|
+
simplecov_json_formatter (~> 0.1)
|
21
|
+
simplecov-html (0.12.3)
|
22
|
+
simplecov_json_formatter (0.1.4)
|
23
|
+
simpleidn (0.2.1)
|
24
|
+
unf (~> 0.1.4)
|
25
|
+
unf (0.1.4)
|
26
|
+
unf_ext
|
27
|
+
unf (0.1.4-java)
|
28
|
+
unf_ext (0.0.8.2)
|
20
29
|
|
21
30
|
PLATFORMS
|
31
|
+
java
|
22
32
|
ruby
|
23
33
|
|
24
34
|
DEPENDENCIES
|
@@ -26,6 +36,7 @@ DEPENDENCIES
|
|
26
36
|
json_schemer!
|
27
37
|
minitest (~> 5.0)
|
28
38
|
rake (~> 13.0)
|
39
|
+
simplecov (~> 0.22)
|
29
40
|
|
30
41
|
BUNDLED WITH
|
31
|
-
2.
|
42
|
+
2.3.25
|
data/README.md
CHANGED
@@ -45,7 +45,12 @@ schemer.valid?({ 'abc' => 10 })
|
|
45
45
|
# error validation (`validate` returns an enumerator)
|
46
46
|
|
47
47
|
schemer.validate({ 'abc' => 10 }).to_a
|
48
|
-
# => [{"data"=>10,
|
48
|
+
# => [{"data"=>10,
|
49
|
+
# "data_pointer"=>"/abc",
|
50
|
+
# "schema"=>{"type"=>"integer", "minimum"=>11},
|
51
|
+
# "schema_pointer"=>"/properties/abc",
|
52
|
+
# "root_schema"=>{"type"=>"object", "properties"=>{"abc"=>{"type"=>"integer", "minimum"=>11}}},
|
53
|
+
# "type"=>"minimum"}]
|
49
54
|
|
50
55
|
# default property values
|
51
56
|
|
@@ -74,6 +79,30 @@ schemer = JSONSchemer.schema(schema)
|
|
74
79
|
|
75
80
|
schema = '{ "type": "integer" }'
|
76
81
|
schemer = JSONSchemer.schema(schema)
|
82
|
+
|
83
|
+
# schema validation
|
84
|
+
|
85
|
+
JSONSchemer.valid_schema?({ '$id' => '#valid' })
|
86
|
+
# => true
|
87
|
+
|
88
|
+
JSONSchemer.validate_schema({ '$id' => nil }).to_a
|
89
|
+
# => [{"data"=>nil,
|
90
|
+
# "data_pointer"=>"/$id",
|
91
|
+
# "schema"=>{"type"=>"string", "format"=>"uri-reference"},
|
92
|
+
# "schema_pointer"=>"/properties/$id",
|
93
|
+
# "root_schema"=>{...meta schema},
|
94
|
+
# "type"=>"string"}]
|
95
|
+
|
96
|
+
JSONSchemer.schema({ '$id' => '#valid' }).valid_schema?
|
97
|
+
# => true
|
98
|
+
|
99
|
+
JSONSchemer.schema({ '$id' => nil }).validate_schema.to_a
|
100
|
+
# => [{"data"=>nil,
|
101
|
+
# "data_pointer"=>"/$id",
|
102
|
+
# "schema"=>{"type"=>"string", "format"=>"uri-reference"},
|
103
|
+
# "schema_pointer"=>"/properties/$id",
|
104
|
+
# "root_schema"=>{...meta schema},
|
105
|
+
# "type"=>"string"}]
|
77
106
|
```
|
78
107
|
|
79
108
|
## Options
|
@@ -92,14 +121,60 @@ JSONSchemer.schema(
|
|
92
121
|
# default: false
|
93
122
|
insert_property_defaults: true,
|
94
123
|
|
124
|
+
# modify properties during validation. You can pass one Proc or a list of Procs to modify data.
|
125
|
+
# Proc/[Proc]
|
126
|
+
# default: nil
|
127
|
+
before_property_validation: proc do |data, property, property_schema, _parent|
|
128
|
+
data[property] ||= 42
|
129
|
+
end,
|
130
|
+
|
131
|
+
# modify properties after validation. You can pass one Proc or a list of Procs to modify data.
|
132
|
+
# Proc/[Proc]
|
133
|
+
# default: nil
|
134
|
+
after_property_validation: proc do |data, property, property_schema, _parent|
|
135
|
+
data[property] = Date.iso8601(data[property]) if property_schema.is_a?(Hash) && property_schema['format'] == 'date'
|
136
|
+
end,
|
137
|
+
|
95
138
|
# resolve external references
|
96
139
|
# 'net/http'/proc/lambda/respond_to?(:call)
|
97
140
|
# 'net/http': proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
|
98
141
|
# default: proc { |uri| raise UnknownRef, uri.to_s }
|
99
|
-
ref_resolver: 'net/http'
|
142
|
+
ref_resolver: 'net/http',
|
143
|
+
|
144
|
+
# use different method to match regexes
|
145
|
+
# 'ruby'/'ecma'/proc/lambda/respond_to?(:call)
|
146
|
+
# 'ruby': proc { |pattern| Regexp.new(pattern) }
|
147
|
+
# default: 'ruby'
|
148
|
+
regexp_resolver: proc do |pattern|
|
149
|
+
RE2::Regexp.new(pattern)
|
150
|
+
end
|
100
151
|
)
|
101
152
|
```
|
102
153
|
|
154
|
+
## CLI
|
155
|
+
|
156
|
+
The `json_schemer` executable takes a JSON schema file as the first argument followed by one or more JSON data files to validate. If there are any validation errors, it outputs them and returns an error code.
|
157
|
+
|
158
|
+
Validation errors are output as single-line JSON objects. The `--errors` option can be used to limit the number of errors returned or prevent output entirely (and fail fast).
|
159
|
+
|
160
|
+
The schema or data can also be read from stdin using `-`.
|
161
|
+
|
162
|
+
```
|
163
|
+
% json_schemer --help
|
164
|
+
Usage:
|
165
|
+
json_schemer [options] <schema> <data>...
|
166
|
+
json_schemer [options] <schema> -
|
167
|
+
json_schemer [options] - <data>...
|
168
|
+
json_schemer -h | --help
|
169
|
+
json_schemer --version
|
170
|
+
|
171
|
+
Options:
|
172
|
+
-e, --errors MAX Maximum number of errors to output
|
173
|
+
Use "0" to validate with no output
|
174
|
+
-h, --help Show help
|
175
|
+
-v, --version Show version
|
176
|
+
```
|
177
|
+
|
103
178
|
## Development
|
104
179
|
|
105
180
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.1
|
7
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.2
|
8
|
+
|
9
|
+
csv_options = { :col_sep => ';', :skip_blanks => true, :skip_lines => /\A#/ }
|
10
|
+
|
11
|
+
unicode_data = URI('https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt')
|
12
|
+
derived_joining_type = URI('https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedJoiningType.txt')
|
13
|
+
|
14
|
+
# https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values
|
15
|
+
virama_canonical_combining_class = '9'
|
16
|
+
|
17
|
+
virama_codes = CSV.new(unicode_data.read, **csv_options).select do |code, _name, _category, canonical_combining_class|
|
18
|
+
canonical_combining_class == virama_canonical_combining_class
|
19
|
+
end.map(&:first)
|
20
|
+
|
21
|
+
# https://www.unicode.org/reports/tr44/#Default_Values
|
22
|
+
# https://www.unicode.org/reports/tr44/#Derived_Extracted
|
23
|
+
codes_by_joining_type = CSV.new(derived_joining_type.read, **csv_options).group_by do |_code, joining_type|
|
24
|
+
joining_type.gsub(/#.+/, '').strip
|
25
|
+
end.transform_values do |rows|
|
26
|
+
rows.map do |code, _joining_type|
|
27
|
+
code.strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def codes_to_character_class(codes)
|
32
|
+
characters = codes.map do |code|
|
33
|
+
code.gsub(/(\h+)/, '\u{\1}').gsub('..', '-')
|
34
|
+
end
|
35
|
+
"[#{characters.join}]"
|
36
|
+
end
|
37
|
+
|
38
|
+
puts "VIRAMA_CHARACTER_CLASS = '#{codes_to_character_class(virama_codes)}'"
|
39
|
+
|
40
|
+
codes_by_joining_type.slice('L', 'D', 'T', 'R').each do |joining_type, codes|
|
41
|
+
puts "JOINING_TYPE_#{joining_type}_CHARACTER_CLASS = '#{codes_to_character_class(codes)}'"
|
42
|
+
end
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/exe/json_schemer
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'optparse'
|
5
|
+
require 'pathname'
|
6
|
+
require 'json_schemer'
|
7
|
+
|
8
|
+
parser = OptionParser.new('Usage:', 32, ' ')
|
9
|
+
parser.separator(" #{parser.program_name} [options] <schema> <data>...")
|
10
|
+
parser.separator(" #{parser.program_name} [options] <schema> -")
|
11
|
+
parser.separator(" #{parser.program_name} [options] - <data>...")
|
12
|
+
parser.separator(" #{parser.program_name} -h | --help")
|
13
|
+
parser.separator(" #{parser.program_name} --version")
|
14
|
+
parser.separator('')
|
15
|
+
parser.separator('Options:')
|
16
|
+
parser.on('-e', '--errors MAX', Integer, 'Maximum number of errors to output', 'Use "0" to validate with no output')
|
17
|
+
parser.on_tail('-h', '--help', 'Show help')
|
18
|
+
parser.on_tail('-v', '--version', 'Show version')
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
parser.parse!(:into => options)
|
22
|
+
|
23
|
+
if options[:help]
|
24
|
+
$stdout.puts(parser)
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
if options[:version]
|
29
|
+
$stdout.puts("#{parser.program_name} #{JSONSchemer::VERSION}")
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
if ARGV.size == 0
|
34
|
+
$stderr.puts("#{parser.program_name}: no schema or data")
|
35
|
+
exit(false)
|
36
|
+
end
|
37
|
+
|
38
|
+
if ARGV.size == 1
|
39
|
+
$stderr.puts("#{parser.program_name}: no data")
|
40
|
+
exit(false)
|
41
|
+
end
|
42
|
+
|
43
|
+
if ARGV.count('-') > 1
|
44
|
+
$stderr.puts("#{parser.program_name}: multiple stdin")
|
45
|
+
exit(false)
|
46
|
+
end
|
47
|
+
|
48
|
+
errors = 0
|
49
|
+
schema = ARGF.file.is_a?(File) ? Pathname.new(ARGF.file.path) : ARGF.file.read
|
50
|
+
schemer = JSONSchemer.schema(schema)
|
51
|
+
|
52
|
+
while ARGV.any?
|
53
|
+
data = JSON.parse(ARGF.skip.file.read)
|
54
|
+
schemer.validate(data).each do |error|
|
55
|
+
exit(false) if options[:errors] == 0
|
56
|
+
errors += 1
|
57
|
+
$stdout.puts(JSON.generate(error))
|
58
|
+
exit(false) if options[:errors] == errors
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
exit(errors.zero?)
|
data/json_schemer.gemspec
CHANGED
@@ -20,22 +20,14 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.required_ruby_version = '
|
23
|
+
spec.required_ruby_version = '>= 2.5'
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler", "~> 2.0"
|
26
26
|
spec.add_development_dependency "rake", "~> 13.0"
|
27
27
|
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
+
spec.add_development_dependency "simplecov", "~> 0.22"
|
28
29
|
|
29
|
-
# spec.add_development_dependency "benchmark-ips", "~> 2.7.2"
|
30
|
-
# spec.add_development_dependency "jschema", "~> 0.2.1"
|
31
|
-
# spec.add_development_dependency "json-schema", "~> 2.8.0"
|
32
|
-
# spec.add_development_dependency "json_schema", "~> 0.17.0"
|
33
|
-
# spec.add_development_dependency "json_validation", "~> 0.1.0"
|
34
|
-
# spec.add_development_dependency "jsonschema", "~> 2.0.2"
|
35
|
-
# spec.add_development_dependency "rj_schema", "~> 0.2.0"
|
36
|
-
|
37
|
-
spec.add_runtime_dependency "ecma-re-validator", "~> 0.2"
|
38
30
|
spec.add_runtime_dependency "hana", "~> 1.3"
|
39
|
-
spec.add_runtime_dependency "
|
40
|
-
spec.add_runtime_dependency "
|
31
|
+
spec.add_runtime_dependency "regexp_parser", "~> 2.0"
|
32
|
+
spec.add_runtime_dependency "simpleidn", "~> 0.2"
|
41
33
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
class CachedResolver
|
4
|
+
def initialize(&resolver)
|
5
|
+
@resolver = resolver
|
6
|
+
@cache = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(*args)
|
10
|
+
@cache[args] = @resolver.call(*args) unless @cache.key?(args)
|
11
|
+
@cache[args]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class CachedRefResolver < CachedResolver; end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
class EcmaRegexp
|
4
|
+
class Syntax < Regexp::Syntax::Base
|
5
|
+
# regexp_parser >= 2.3.0 uses syntax classes directly instead of instances
|
6
|
+
# :nocov:
|
7
|
+
SYNTAX = respond_to?(:implements) ? self : new
|
8
|
+
# :nocov:
|
9
|
+
SYNTAX.implements :anchor, Anchor::Extended
|
10
|
+
SYNTAX.implements :assertion, Assertion::All
|
11
|
+
# literal %i[number] to support regexp_parser < 2.2.0 (Backreference::Plain)
|
12
|
+
SYNTAX.implements :backref, %i[number] + Backreference::Name
|
13
|
+
# :meta_sequence, :bell, and :escape are not supported in ecma
|
14
|
+
SYNTAX.implements :escape, Escape::Basic + (Escape::Control - %i[meta_sequence]) + (Escape::ASCII - %i[bell escape]) + Escape::Unicode + Escape::Meta + Escape::Hex + Escape::Octal
|
15
|
+
SYNTAX.implements :property, UnicodeProperty::All
|
16
|
+
SYNTAX.implements :nonproperty, UnicodeProperty::All
|
17
|
+
# :comment is not supported in ecma
|
18
|
+
SYNTAX.implements :free_space, (FreeSpace::All - %i[comment])
|
19
|
+
SYNTAX.implements :group, Group::Basic + Group::Named + Group::Passive
|
20
|
+
SYNTAX.implements :literal, Literal::All
|
21
|
+
SYNTAX.implements :meta, Meta::Extended
|
22
|
+
SYNTAX.implements :quantifier, Quantifier::Greedy + Quantifier::Reluctant + Quantifier::Interval + Quantifier::IntervalReluctant
|
23
|
+
SYNTAX.implements :set, CharacterSet::Basic
|
24
|
+
SYNTAX.implements :type, CharacterType::Extended
|
25
|
+
end
|
26
|
+
|
27
|
+
RUBY_EQUIVALENTS = {
|
28
|
+
:anchor => {
|
29
|
+
:bol => '\A',
|
30
|
+
:eol => '\z'
|
31
|
+
},
|
32
|
+
:type => {
|
33
|
+
:space => '[\t\r\n\f\v\uFEFF\u2029\p{Zs}]',
|
34
|
+
:nonspace => '[^\t\r\n\f\v\uFEFF\u2029\p{Zs}]'
|
35
|
+
}
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def ruby_equivalent(pattern)
|
40
|
+
Regexp::Scanner.scan(pattern).map do |type, token, text|
|
41
|
+
Syntax::SYNTAX.check!(*Syntax::SYNTAX.normalize(type, token))
|
42
|
+
RUBY_EQUIVALENTS.dig(type, token) || text
|
43
|
+
rescue Regexp::Syntax::NotImplementedError
|
44
|
+
raise InvalidEcmaRegexp, "invalid token #{text.inspect} (#{type}:#{token}) in #{pattern.inspect}"
|
45
|
+
end.join
|
46
|
+
rescue Regexp::Scanner::ScannerError
|
47
|
+
raise InvalidEcmaRegexp, "invalid pattern #{pattern.inspect}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
module Format
|
4
|
+
module Hostname
|
5
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#section-2.1
|
6
|
+
MARKS = '\p{Mn}\p{Mc}'
|
7
|
+
LETTER_DIGITS = "\\p{Ll}\\p{Lu}\\p{Lo}\\p{Nd}\\p{Lm}#{MARKS}"
|
8
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#section-2.6
|
9
|
+
EXCEPTIONS_PVALID = '\u{06FD}\u{06FE}\u{0F0B}\u{3007}' # \u{00DF}\u{03C2} covered by \p{Ll}
|
10
|
+
EXCEPTIONS_DISALLOWED = '\u{0640}\u{07FA}\u{302E}\u{302F}\u{3031}\u{3032}\u{3033}\u{3034}\u{3035}\u{303B}'
|
11
|
+
LABEL_CHARACTER_CLASS = "[#{LETTER_DIGITS}#{EXCEPTIONS_PVALID}&&[^#{EXCEPTIONS_DISALLOWED}]]"
|
12
|
+
# https://datatracker.ietf.org/doc/html/rfc5891#section-4.2.3.2
|
13
|
+
LEADING_CHARACTER_CLASS = "[#{LABEL_CHARACTER_CLASS}&&[^#{MARKS}]]"
|
14
|
+
LABEL_REGEX_STRING = "#{LEADING_CHARACTER_CLASS}([#{LABEL_CHARACTER_CLASS}\-]*#{LABEL_CHARACTER_CLASS})?"
|
15
|
+
HOSTNAME_REGEX = /\A(#{LABEL_REGEX_STRING}\.)*#{LABEL_REGEX_STRING}\z/i.freeze
|
16
|
+
# bin/hostname_character_classes
|
17
|
+
VIRAMA_CHARACTER_CLASS = '[\u{094D}\u{09CD}\u{0A4D}\u{0ACD}\u{0B4D}\u{0BCD}\u{0C4D}\u{0CCD}\u{0D3B}\u{0D3C}\u{0D4D}\u{0DCA}\u{0E3A}\u{0EBA}\u{0F84}\u{1039}\u{103A}\u{1714}\u{1715}\u{1734}\u{17D2}\u{1A60}\u{1B44}\u{1BAA}\u{1BAB}\u{1BF2}\u{1BF3}\u{2D7F}\u{A806}\u{A82C}\u{A8C4}\u{A953}\u{A9C0}\u{AAF6}\u{ABED}\u{10A3F}\u{11046}\u{11070}\u{1107F}\u{110B9}\u{11133}\u{11134}\u{111C0}\u{11235}\u{112EA}\u{1134D}\u{11442}\u{114C2}\u{115BF}\u{1163F}\u{116B6}\u{1172B}\u{11839}\u{1193D}\u{1193E}\u{119E0}\u{11A34}\u{11A47}\u{11A99}\u{11C3F}\u{11D44}\u{11D45}\u{11D97}\u{11F41}\u{11F42}]'
|
18
|
+
JOINING_TYPE_L_CHARACTER_CLASS = '[\u{A872}\u{10ACD}\u{10AD7}\u{10D00}\u{10FCB}]'
|
19
|
+
JOINING_TYPE_D_CHARACTER_CLASS = '[\u{0620}\u{0626}\u{0628}\u{062A}-\u{062E}\u{0633}-\u{063F}\u{0641}-\u{0647}\u{0649}-\u{064A}\u{066E}-\u{066F}\u{0678}-\u{0687}\u{069A}-\u{06BF}\u{06C1}-\u{06C2}\u{06CC}\u{06CE}\u{06D0}-\u{06D1}\u{06FA}-\u{06FC}\u{06FF}\u{0712}-\u{0714}\u{071A}-\u{071D}\u{071F}-\u{0727}\u{0729}\u{072B}\u{072D}-\u{072E}\u{074E}-\u{0758}\u{075C}-\u{076A}\u{076D}-\u{0770}\u{0772}\u{0775}-\u{0777}\u{077A}-\u{077F}\u{07CA}-\u{07EA}\u{0841}-\u{0845}\u{0848}\u{084A}-\u{0853}\u{0855}\u{0860}\u{0862}-\u{0865}\u{0868}\u{0886}\u{0889}-\u{088D}\u{08A0}-\u{08A9}\u{08AF}-\u{08B0}\u{08B3}-\u{08B8}\u{08BA}-\u{08C8}\u{1807}\u{1820}-\u{1842}\u{1843}\u{1844}-\u{1878}\u{1887}-\u{18A8}\u{18AA}\u{A840}-\u{A871}\u{10AC0}-\u{10AC4}\u{10AD3}-\u{10AD6}\u{10AD8}-\u{10ADC}\u{10ADE}-\u{10AE0}\u{10AEB}-\u{10AEE}\u{10B80}\u{10B82}\u{10B86}-\u{10B88}\u{10B8A}-\u{10B8B}\u{10B8D}\u{10B90}\u{10BAD}-\u{10BAE}\u{10D01}-\u{10D21}\u{10D23}\u{10F30}-\u{10F32}\u{10F34}-\u{10F44}\u{10F51}-\u{10F53}\u{10F70}-\u{10F73}\u{10F76}-\u{10F81}\u{10FB0}\u{10FB2}-\u{10FB3}\u{10FB8}\u{10FBB}-\u{10FBC}\u{10FBE}-\u{10FBF}\u{10FC1}\u{10FC4}\u{10FCA}\u{1E900}-\u{1E943}]'
|
20
|
+
JOINING_TYPE_T_CHARACTER_CLASS = '[\u{00AD}\u{0300}-\u{036F}\u{0483}-\u{0487}\u{0488}-\u{0489}\u{0591}-\u{05BD}\u{05BF}\u{05C1}-\u{05C2}\u{05C4}-\u{05C5}\u{05C7}\u{0610}-\u{061A}\u{061C}\u{064B}-\u{065F}\u{0670}\u{06D6}-\u{06DC}\u{06DF}-\u{06E4}\u{06E7}-\u{06E8}\u{06EA}-\u{06ED}\u{070F}\u{0711}\u{0730}-\u{074A}\u{07A6}-\u{07B0}\u{07EB}-\u{07F3}\u{07FD}\u{0816}-\u{0819}\u{081B}-\u{0823}\u{0825}-\u{0827}\u{0829}-\u{082D}\u{0859}-\u{085B}\u{0898}-\u{089F}\u{08CA}-\u{08E1}\u{08E3}-\u{0902}\u{093A}\u{093C}\u{0941}-\u{0948}\u{094D}\u{0951}-\u{0957}\u{0962}-\u{0963}\u{0981}\u{09BC}\u{09C1}-\u{09C4}\u{09CD}\u{09E2}-\u{09E3}\u{09FE}\u{0A01}-\u{0A02}\u{0A3C}\u{0A41}-\u{0A42}\u{0A47}-\u{0A48}\u{0A4B}-\u{0A4D}\u{0A51}\u{0A70}-\u{0A71}\u{0A75}\u{0A81}-\u{0A82}\u{0ABC}\u{0AC1}-\u{0AC5}\u{0AC7}-\u{0AC8}\u{0ACD}\u{0AE2}-\u{0AE3}\u{0AFA}-\u{0AFF}\u{0B01}\u{0B3C}\u{0B3F}\u{0B41}-\u{0B44}\u{0B4D}\u{0B55}-\u{0B56}\u{0B62}-\u{0B63}\u{0B82}\u{0BC0}\u{0BCD}\u{0C00}\u{0C04}\u{0C3C}\u{0C3E}-\u{0C40}\u{0C46}-\u{0C48}\u{0C4A}-\u{0C4D}\u{0C55}-\u{0C56}\u{0C62}-\u{0C63}\u{0C81}\u{0CBC}\u{0CBF}\u{0CC6}\u{0CCC}-\u{0CCD}\u{0CE2}-\u{0CE3}\u{0D00}-\u{0D01}\u{0D3B}-\u{0D3C}\u{0D41}-\u{0D44}\u{0D4D}\u{0D62}-\u{0D63}\u{0D81}\u{0DCA}\u{0DD2}-\u{0DD4}\u{0DD6}\u{0E31}\u{0E34}-\u{0E3A}\u{0E47}-\u{0E4E}\u{0EB1}\u{0EB4}-\u{0EBC}\u{0EC8}-\u{0ECE}\u{0F18}-\u{0F19}\u{0F35}\u{0F37}\u{0F39}\u{0F71}-\u{0F7E}\u{0F80}-\u{0F84}\u{0F86}-\u{0F87}\u{0F8D}-\u{0F97}\u{0F99}-\u{0FBC}\u{0FC6}\u{102D}-\u{1030}\u{1032}-\u{1037}\u{1039}-\u{103A}\u{103D}-\u{103E}\u{1058}-\u{1059}\u{105E}-\u{1060}\u{1071}-\u{1074}\u{1082}\u{1085}-\u{1086}\u{108D}\u{109D}\u{135D}-\u{135F}\u{1712}-\u{1714}\u{1732}-\u{1733}\u{1752}-\u{1753}\u{1772}-\u{1773}\u{17B4}-\u{17B5}\u{17B7}-\u{17BD}\u{17C6}\u{17C9}-\u{17D3}\u{17DD}\u{180B}-\u{180D}\u{180F}\u{1885}-\u{1886}\u{18A9}\u{1920}-\u{1922}\u{1927}-\u{1928}\u{1932}\u{1939}-\u{193B}\u{1A17}-\u{1A18}\u{1A1B}\u{1A56}\u{1A58}-\u{1A5E}\u{1A60}\u{1A62}\u{1A65}-\u{1A6C}\u{1A73}-\u{1A7C}\u{1A7F}\u{1AB0}-\u{1ABD}\u{1ABE}\u{1ABF}-\u{1ACE}\u{1B00}-\u{1B03}\u{1B34}\u{1B36}-\u{1B3A}\u{1B3C}\u{1B42}\u{1B6B}-\u{1B73}\u{1B80}-\u{1B81}\u{1BA2}-\u{1BA5}\u{1BA8}-\u{1BA9}\u{1BAB}-\u{1BAD}\u{1BE6}\u{1BE8}-\u{1BE9}\u{1BED}\u{1BEF}-\u{1BF1}\u{1C2C}-\u{1C33}\u{1C36}-\u{1C37}\u{1CD0}-\u{1CD2}\u{1CD4}-\u{1CE0}\u{1CE2}-\u{1CE8}\u{1CED}\u{1CF4}\u{1CF8}-\u{1CF9}\u{1DC0}-\u{1DFF}\u{200B}\u{200E}-\u{200F}\u{202A}-\u{202E}\u{2060}-\u{2064}\u{206A}-\u{206F}\u{20D0}-\u{20DC}\u{20DD}-\u{20E0}\u{20E1}\u{20E2}-\u{20E4}\u{20E5}-\u{20F0}\u{2CEF}-\u{2CF1}\u{2D7F}\u{2DE0}-\u{2DFF}\u{302A}-\u{302D}\u{3099}-\u{309A}\u{A66F}\u{A670}-\u{A672}\u{A674}-\u{A67D}\u{A69E}-\u{A69F}\u{A6F0}-\u{A6F1}\u{A802}\u{A806}\u{A80B}\u{A825}-\u{A826}\u{A82C}\u{A8C4}-\u{A8C5}\u{A8E0}-\u{A8F1}\u{A8FF}\u{A926}-\u{A92D}\u{A947}-\u{A951}\u{A980}-\u{A982}\u{A9B3}\u{A9B6}-\u{A9B9}\u{A9BC}-\u{A9BD}\u{A9E5}\u{AA29}-\u{AA2E}\u{AA31}-\u{AA32}\u{AA35}-\u{AA36}\u{AA43}\u{AA4C}\u{AA7C}\u{AAB0}\u{AAB2}-\u{AAB4}\u{AAB7}-\u{AAB8}\u{AABE}-\u{AABF}\u{AAC1}\u{AAEC}-\u{AAED}\u{AAF6}\u{ABE5}\u{ABE8}\u{ABED}\u{FB1E}\u{FE00}-\u{FE0F}\u{FE20}-\u{FE2F}\u{FEFF}\u{FFF9}-\u{FFFB}\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}-\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}-\u{10AE6}\u{10D24}-\u{10D27}\u{10EAB}-\u{10EAC}\u{10EFD}-\u{10EFF}\u{10F46}-\u{10F50}\u{10F82}-\u{10F85}\u{11001}\u{11038}-\u{11046}\u{11070}\u{11073}-\u{11074}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}-\u{110BA}\u{110C2}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}-\u{11181}\u{111B6}-\u{111BE}\u{111C9}-\u{111CC}\u{111CF}\u{1122F}-\u{11231}\u{11234}\u{11236}-\u{11237}\u{1123E}\u{11241}\u{112DF}\u{112E3}-\u{112EA}\u{11300}-\u{11301}\u{1133B}-\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{1145E}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}-\u{114C0}\u{114C2}-\u{114C3}\u{115B2}-\u{115B5}\u{115BC}-\u{115BD}\u{115BF}-\u{115C0}\u{115DC}-\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}-\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{1182F}-\u{11837}\u{11839}-\u{1183A}\u{1193B}-\u{1193C}\u{1193E}\u{11943}\u{119D4}-\u{119D7}\u{119DA}-\u{119DB}\u{119E0}\u{11A01}-\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}-\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}-\u{11CB3}\u{11CB5}-\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}-\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{11D90}-\u{11D91}\u{11D95}\u{11D97}\u{11EF3}-\u{11EF4}\u{11F00}-\u{11F01}\u{11F36}-\u{11F3A}\u{11F40}\u{11F42}\u{13430}-\u{1343F}\u{13440}\u{13447}-\u{13455}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F4F}\u{16F8F}-\u{16F92}\u{16FE4}\u{1BC9D}-\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1CF00}-\u{1CF2D}\u{1CF30}-\u{1CF46}\u{1D167}-\u{1D169}\u{1D173}-\u{1D17A}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}-\u{1E024}\u{1E026}-\u{1E02A}\u{1E08F}\u{1E130}-\u{1E136}\u{1E2AE}\u{1E2EC}-\u{1E2EF}\u{1E4EC}-\u{1E4EF}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{1E94B}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]'
|
21
|
+
JOINING_TYPE_R_CHARACTER_CLASS = '[\u{0622}-\u{0625}\u{0627}\u{0629}\u{062F}-\u{0632}\u{0648}\u{0671}-\u{0673}\u{0675}-\u{0677}\u{0688}-\u{0699}\u{06C0}\u{06C3}-\u{06CB}\u{06CD}\u{06CF}\u{06D2}-\u{06D3}\u{06D5}\u{06EE}-\u{06EF}\u{0710}\u{0715}-\u{0719}\u{071E}\u{0728}\u{072A}\u{072C}\u{072F}\u{074D}\u{0759}-\u{075B}\u{076B}-\u{076C}\u{0771}\u{0773}-\u{0774}\u{0778}-\u{0779}\u{0840}\u{0846}-\u{0847}\u{0849}\u{0854}\u{0856}-\u{0858}\u{0867}\u{0869}-\u{086A}\u{0870}-\u{0882}\u{088E}\u{08AA}-\u{08AC}\u{08AE}\u{08B1}-\u{08B2}\u{08B9}\u{10AC5}\u{10AC7}\u{10AC9}-\u{10ACA}\u{10ACE}-\u{10AD2}\u{10ADD}\u{10AE1}\u{10AE4}\u{10AEF}\u{10B81}\u{10B83}-\u{10B85}\u{10B89}\u{10B8C}\u{10B8E}-\u{10B8F}\u{10B91}\u{10BA9}-\u{10BAC}\u{10D22}\u{10F33}\u{10F54}\u{10F74}-\u{10F75}\u{10FB4}-\u{10FB6}\u{10FB9}-\u{10FBA}\u{10FBD}\u{10FC2}-\u{10FC3}\u{10FC9}]'
|
22
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.1
|
23
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.2
|
24
|
+
ZERO_WIDTH_VIRAMA = "#{VIRAMA_CHARACTER_CLASS}[\\u{200C}\\u{200D}]"
|
25
|
+
ZERO_WIDTH_NON_JOINER_JOINING_TYPE = "[#{JOINING_TYPE_L_CHARACTER_CLASS}#{JOINING_TYPE_D_CHARACTER_CLASS}]#{JOINING_TYPE_T_CHARACTER_CLASS}*\\u{200C}#{JOINING_TYPE_T_CHARACTER_CLASS}*[#{JOINING_TYPE_R_CHARACTER_CLASS}#{JOINING_TYPE_D_CHARACTER_CLASS}]"
|
26
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.3
|
27
|
+
MIDDLE_DOT = '\u{006C}\u{00B7}\u{006C}'
|
28
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.4
|
29
|
+
GREEK_LOWER_NUMERAL_SIGN = '\u{0375}\p{Greek}'
|
30
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.5
|
31
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.6
|
32
|
+
HEBREW_PUNCTUATION = '\p{Hebrew}[\u{05F3}\u{05F4}]'
|
33
|
+
CONTEXT_REGEX = /(#{ZERO_WIDTH_VIRAMA}|#{ZERO_WIDTH_NON_JOINER_JOINING_TYPE}|#{MIDDLE_DOT}|#{GREEK_LOWER_NUMERAL_SIGN}|#{HEBREW_PUNCTUATION})/.freeze
|
34
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.7
|
35
|
+
KATAKANA_MIDDLE_DOT_REGEX = /\u{30FB}/.freeze
|
36
|
+
KATAKANA_MIDDLE_DOT_CONTEXT_REGEX = /[\p{Hiragana}\p{Katakana}\p{Han}]/.freeze
|
37
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.8
|
38
|
+
# https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.9
|
39
|
+
ARABIC_INDIC_DIGITS_REGEX = /[\u{0660}-\u{0669}]/.freeze
|
40
|
+
ARABIC_EXTENDED_DIGITS_REGEX = /[\u{06F0}-\u{06F9}]/.freeze
|
41
|
+
|
42
|
+
def valid_hostname?(data)
|
43
|
+
data.split('.').map do |a_label|
|
44
|
+
return false if a_label.size > 63
|
45
|
+
u_label = SimpleIDN.to_unicode(a_label)
|
46
|
+
# https://datatracker.ietf.org/doc/html/rfc5891#section-4.2.3.1
|
47
|
+
return false if u_label.slice(2, 2) == '--'
|
48
|
+
return false if ARABIC_INDIC_DIGITS_REGEX.match?(u_label) && ARABIC_EXTENDED_DIGITS_REGEX.match?(u_label)
|
49
|
+
u_label.gsub!(CONTEXT_REGEX, 'ok')
|
50
|
+
u_label.gsub!(KATAKANA_MIDDLE_DOT_REGEX, 'ok') if KATAKANA_MIDDLE_DOT_CONTEXT_REGEX.match?(u_label)
|
51
|
+
u_label
|
52
|
+
end.join('.').match?(HOSTNAME_REGEX)
|
53
|
+
rescue SimpleIDN::ConversionError
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
module Format
|
4
|
+
module URITemplate
|
5
|
+
# https://datatracker.ietf.org/doc/html/rfc6570
|
6
|
+
PCT_ENCODED = '%\h{2}' # pct-encoded = "%" HEXDIG HEXDIG
|
7
|
+
EXPLODE = '\*' # explode = "*"
|
8
|
+
MAX_LENGTH = '[1-9]\d{0,3}' # max-length = %x31-39 0*3DIGIT ; positive integer < 10000
|
9
|
+
PREFIX = ":#{MAX_LENGTH}" # prefix = ":" max-length
|
10
|
+
MODIFIER_LEVEL4 = "#{PREFIX}|#{EXPLODE}" # modifier-level4 = prefix / explode
|
11
|
+
VARCHAR = "(\\w|#{PCT_ENCODED})" # varchar = ALPHA / DIGIT / "_" / pct-encoded
|
12
|
+
VARNAME = "#{VARCHAR}(\\.?#{VARCHAR})*" # varname = varchar *( ["."] varchar )
|
13
|
+
VARSPEC = "#{VARNAME}(#{MODIFIER_LEVEL4})?" # varspec = varname [ modifier-level4 ]
|
14
|
+
VARIABLE_LIST = "#{VARSPEC}(,#{VARSPEC})*" # variable-list = varspec *( "," varspec )
|
15
|
+
OPERATOR = '[+#./;?&=,!@|]' # operator = op-level2 / op-level3 / op-reserve
|
16
|
+
# op-level2 = "+" / "#"
|
17
|
+
# op-level3 = "." / "/" / ";" / "?" / "&"
|
18
|
+
# op-reserve = "=" / "," / "!" / "@" / "|"
|
19
|
+
EXPRESSION = "{#{OPERATOR}?#{VARIABLE_LIST}}" # expression = "{" [ operator ] variable-list "}"
|
20
|
+
LITERALS = "[^\\x00-\\x20\\x7F\"%'<>\\\\^`{|}]|#{PCT_ENCODED}" # literals = %x21 / %x23-24 / %x26 / %x28-3B / %x3D / %x3F-5B
|
21
|
+
# / %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate
|
22
|
+
# / pct-encoded
|
23
|
+
# ; any Unicode character except: CTL, SP,
|
24
|
+
# ; DQUOTE, "'", "%" (aside from pct-encoded),
|
25
|
+
# ; "<", ">", "\", "^", "`", "{", "|", "}"
|
26
|
+
URI_TEMPLATE = "(#{LITERALS}|#{EXPRESSION})*" # URI-Template = *( literals / expression )
|
27
|
+
URI_TEMPLATE_REGEX = /\A#{URI_TEMPLATE}\z/
|
28
|
+
|
29
|
+
def valid_uri_template?(data)
|
30
|
+
URI_TEMPLATE_REGEX.match?(data)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|