ctx_parser 0.2.9 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README-zh.md +3 -0
- data/README.md +5 -0
- data/lib/ctx_parser/commands.rb +9 -4
- data/lib/ctx_parser/profile.rb +6 -3
- data/lib/ctx_parser/result.rb +13 -3
- data/lib/ctx_parser/values.rb +15 -0
- data/lib/ctx_parser/version.rb +1 -1
- metadata +29 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 207ae07b7f1f12d8d8a91ce6ea1a89fc39bb2c566f3d1cebe289ab71012aadd5
|
|
4
|
+
data.tar.gz: 1249139af57dd39b634ad948028af664af72eea4af7e5cb9a0137e4117f8eecd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43e613f54ffa2b660d9badc6e2ce1195ed9fbf5b3fd864c8b85102c0cfae4eb9aa3cac477c7e7c9e92116d3c3e40234ce58e0e6c08db1757106c78a1c0e976f4
|
|
7
|
+
data.tar.gz: d8a2b623628265babbcaedc0f3d15b29257945917e6e4db13d6958680457c1d4f3028066d416c77ff2c2d29a3da8dde5d7520da920d8a40b8ca3794a7247bf04
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ Changes to the public contract are recorded here. Versions follow Semantic Versi
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.4.0 (2026-10-29)
|
|
8
|
+
|
|
9
|
+
- Add immutable `covered_scopes` to Result, including equality, hashing, pattern matching and `with` semantics.
|
|
10
|
+
- Centralize single-line command validation in `Commands.command_text` and strict UTC timestamp parsing in `Values.timestamp`.
|
|
11
|
+
|
|
12
|
+
## 0.3.0 (2026-10-29)
|
|
13
|
+
|
|
14
|
+
- Add `Result#partial?` and `#available?`; reject records on failed or unsupported results.
|
|
15
|
+
|
|
7
16
|
## 0.2.9 (2026-10-24)
|
|
8
17
|
|
|
9
18
|
- Correct dependency documentation to match the unconstrained ipaddr and digest declarations; applications select and validate their runtime dependency combination.
|
data/README-zh.md
CHANGED
|
@@ -122,3 +122,6 @@ CI 在 Linux、macOS 上执行测试、lint、示例、构建和隔离安装。`
|
|
|
122
122
|
## 许可证
|
|
123
123
|
|
|
124
124
|
MIT,见 [LICENSE](LICENSE)。
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
`Result#partial?` identifies partial parsing; `#available?` is true for complete or partial results. Failed and unsupported results cannot contain records. Availability does not establish completeness or network health.
|
data/README.md
CHANGED
|
@@ -122,3 +122,8 @@ Publish dependency gems first. A Git tag, a successful build, or a workflow file
|
|
|
122
122
|
## License
|
|
123
123
|
|
|
124
124
|
MIT. See [LICENSE](LICENSE).
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
`Result#partial?` identifies partial parsing; `#available?` is true for complete or partial results. Failed and unsupported results cannot contain records. Availability does not establish completeness or network health.
|
|
128
|
+
|
|
129
|
+
本次使用 ctx_parser 0.4 系列契约;`Result` 新增 `covered_scopes`,单行命令统一由 `CtxParser::Commands.command_text` 校验。跨模块约定见 [ctx_runner 契约迁移](https://github.com/gatework/ctx_runner/blob/main/docs/contracts-2026-10-29.md)。
|
data/lib/ctx_parser/commands.rb
CHANGED
|
@@ -7,6 +7,14 @@ module CtxParser
|
|
|
7
7
|
class Commands
|
|
8
8
|
attr_reader :entries, :by_dimension, :parsers
|
|
9
9
|
|
|
10
|
+
# 查询文本的唯一值边界;复制后冻结,不冻结调用方提供的字符串。
|
|
11
|
+
def self.command_text(value)
|
|
12
|
+
unless value.is_a?(String) && !value.strip.empty? && !/[\x00-\x1f\x7f]/.match?(value)
|
|
13
|
+
raise ArgumentError, "One command without control characters is required"
|
|
14
|
+
end
|
|
15
|
+
value.frozen? ? value : value.dup.freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
10
18
|
# 校验并冻结受支持的维度到命令声明。
|
|
11
19
|
def initialize(entries, multi_command: false)
|
|
12
20
|
unless entries.is_a?(Array) && entries.any?
|
|
@@ -36,13 +44,10 @@ module CtxParser
|
|
|
36
44
|
dimensions.uniq == dimensions && (@multi_command || dimensions.none? { |name| @by_dimension.key?(name) })
|
|
37
45
|
raise ArgumentError, "Command dimensions must be unique nonempty symbols"
|
|
38
46
|
end
|
|
39
|
-
|
|
40
|
-
raise ArgumentError, "Command must be nonempty text without control characters"
|
|
41
|
-
end
|
|
47
|
+
command = self.class.command_text(command)
|
|
42
48
|
raise ArgumentError, "Command is already declared" if @parsers.key?(command)
|
|
43
49
|
raise ArgumentError, "Command parser must inherit from Parser" unless parser.is_a?(Class) && parser < Parser
|
|
44
50
|
|
|
45
|
-
command = command.dup.freeze
|
|
46
51
|
dimensions = dimensions.dup.freeze
|
|
47
52
|
dimensions.each do |name|
|
|
48
53
|
# 索引数组由本实例持有,构建完成后统一冻结;追加不复制已有命令。
|
data/lib/ctx_parser/profile.rb
CHANGED
|
@@ -45,7 +45,10 @@ module CtxParser
|
|
|
45
45
|
|
|
46
46
|
# 校验静态目录中的单命令文本,不接受控制字符。
|
|
47
47
|
def self.valid_command?(value)
|
|
48
|
-
|
|
48
|
+
Commands.command_text(value)
|
|
49
|
+
true
|
|
50
|
+
rescue ArgumentError
|
|
51
|
+
false
|
|
49
52
|
end
|
|
50
53
|
private_class_method :valid_command?
|
|
51
54
|
|
|
@@ -147,11 +150,11 @@ module CtxParser
|
|
|
147
150
|
# 查询快照不冻结调用方的数组或字符串。
|
|
148
151
|
def validated_queries(declaration)
|
|
149
152
|
queries = declaration.is_a?(Array) ? declaration : [ declaration ]
|
|
150
|
-
unless queries.any? && queries.uniq == queries
|
|
153
|
+
unless queries.any? && queries.uniq == queries
|
|
151
154
|
raise ArgumentError, "Parser dimension must declare one or more unique commands"
|
|
152
155
|
end
|
|
153
156
|
|
|
154
|
-
queries.map { |query|
|
|
157
|
+
queries.map { |query| Commands.command_text(query) }.freeze
|
|
155
158
|
end
|
|
156
159
|
|
|
157
160
|
# 仅当存在唯一公共查询时允许推断;显式命令必须覆盖全部请求视图。
|
data/lib/ctx_parser/result.rb
CHANGED
|
@@ -4,18 +4,28 @@ require_relative "immutable"
|
|
|
4
4
|
|
|
5
5
|
module CtxParser
|
|
6
6
|
# 仅保存解析结果;采集命令、时间与证据由调用方管理。
|
|
7
|
-
Result = Data.define(:status, :records, :issues) do
|
|
7
|
+
Result = Data.define(:status, :records, :issues, :covered_scopes) do
|
|
8
8
|
# 冻结解析状态、归一化记录与诊断证据。
|
|
9
|
-
def initialize(status:, records: [], issues: [])
|
|
9
|
+
def initialize(status:, records: [], issues: [], covered_scopes: [])
|
|
10
10
|
raise ArgumentError, "Invalid parser status" unless %i[complete partial failed unsupported].include?(status)
|
|
11
11
|
raise ArgumentError, "Records must be hashes" unless records.is_a?(Array) && records.all? { |row| row.is_a?(Hash) }
|
|
12
12
|
raise ArgumentError, "Issues must be hashes" unless issues.is_a?(Array) && issues.all? { |issue| issue.is_a?(Hash) }
|
|
13
|
+
unless covered_scopes.is_a?(Array) && covered_scopes.all? { |scope| scope.is_a?(Hash) } && covered_scopes.uniq == covered_scopes
|
|
14
|
+
raise ArgumentError, "Covered scopes must be unique hashes"
|
|
15
|
+
end
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
if %i[failed unsupported].include?(status) && (records.any? || covered_scopes.any?)
|
|
18
|
+
raise ArgumentError, "Unavailable parser results cannot contain records or covered scopes"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
super(status: status, records: Immutable.copy(records), issues: Immutable.copy(issues), covered_scopes: Immutable.copy(covered_scopes))
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
# 判断解析结果是否具有完整证据。
|
|
18
25
|
def complete? = status == :complete
|
|
26
|
+
# 不完整事实仍可用于报告已观察到的问题。
|
|
27
|
+
def partial? = status == :partial
|
|
28
|
+
def available? = complete? || partial?
|
|
19
29
|
|
|
20
30
|
# 展示对象身份与状态,避免泄露原始回显或凭据。
|
|
21
31
|
def inspect = "#<#{self.class.name} status=#{status} records=#{records.size}>"
|
data/lib/ctx_parser/values.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "ipaddr"
|
|
4
|
+
require "time"
|
|
5
|
+
require "date"
|
|
4
6
|
|
|
5
7
|
module CtxParser
|
|
6
8
|
# 严格转换标量;单位及可选字段语法仍由命令解析器负责。
|
|
@@ -9,6 +11,19 @@ module CtxParser
|
|
|
9
11
|
|
|
10
12
|
module_function
|
|
11
13
|
|
|
14
|
+
# 严格时刻包含时区;拒绝 Time.iso8601 自动归一化的非法日期或时分秒。
|
|
15
|
+
def timestamp(raw)
|
|
16
|
+
unless raw.is_a?(String) && /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\z/.match?(raw)
|
|
17
|
+
raise ArgumentError, "Timestamp requires an explicit offset"
|
|
18
|
+
end
|
|
19
|
+
Date.iso8601(raw[0, 10])
|
|
20
|
+
if raw[11, 2].to_i >= 24 || raw[14, 2].to_i >= 60 || raw[17, 2].to_i >= 60 ||
|
|
21
|
+
(offset = /[+-](\d{2}):(\d{2})\z/.match(raw)) && (offset[1].to_i >= 24 || offset[2].to_i >= 60)
|
|
22
|
+
raise ArgumentError, "Invalid timestamp"
|
|
23
|
+
end
|
|
24
|
+
Time.iso8601(raw).getutc.freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
12
27
|
# 解析十进制整数,并校验声明的边界。
|
|
13
28
|
def integer(raw, min: 0, max: nil)
|
|
14
29
|
unless (min.nil? || min.is_a?(Integer)) && (max.nil? || max.is_a?(Integer)) && (min.nil? || max.nil? || min <= max)
|
data/lib/ctx_parser/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ctx_parser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gatework contributors
|
|
@@ -9,6 +9,34 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: date
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: time
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
12
40
|
- !ruby/object:Gem::Dependency
|
|
13
41
|
name: digest
|
|
14
42
|
requirement: !ruby/object:Gem::Requirement
|