ctx_parser 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +22 -0
- data/README-zh.md +102 -0
- data/README.md +102 -0
- data/docs/architecture-zh.md +273 -0
- data/docs/release.md +13 -0
- data/examples/offline.rb +30 -0
- data/lib/ctx_parser/banner.rb +23 -0
- data/lib/ctx_parser/block.rb +99 -0
- data/lib/ctx_parser/capture.rb +61 -0
- data/lib/ctx_parser/command_catalog.rb +54 -0
- data/lib/ctx_parser/configuration_parser.rb +73 -0
- data/lib/ctx_parser/cursor.rb +177 -0
- data/lib/ctx_parser/document.rb +41 -0
- data/lib/ctx_parser/immutable.rb +18 -0
- data/lib/ctx_parser/input.rb +31 -0
- data/lib/ctx_parser/line.rb +28 -0
- data/lib/ctx_parser/parser.rb +46 -0
- data/lib/ctx_parser/profile.rb +134 -0
- data/lib/ctx_parser/records.rb +36 -0
- data/lib/ctx_parser/registry.rb +39 -0
- data/lib/ctx_parser/result.rb +22 -0
- data/lib/ctx_parser/result_set.rb +36 -0
- data/lib/ctx_parser/values.rb +59 -0
- data/lib/ctx_parser/version.rb +6 -0
- data/lib/ctx_parser.rb +17 -0
- metadata +99 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "command_catalog"
|
|
4
|
+
|
|
5
|
+
module CtxParser
|
|
6
|
+
# 组合平台命令策略,仅覆盖已确认的型号差异。
|
|
7
|
+
class Profile
|
|
8
|
+
COMMANDS = {}.freeze
|
|
9
|
+
PARSERS = {}.freeze
|
|
10
|
+
MODEL_PARSERS = {}.freeze
|
|
11
|
+
|
|
12
|
+
attr_reader :model
|
|
13
|
+
|
|
14
|
+
# 根据传入设备型号选择命令与解析行为。
|
|
15
|
+
def initialize(model: nil)
|
|
16
|
+
raise ArgumentError, "Device model must be text" unless model.nil? || model.is_a?(String)
|
|
17
|
+
|
|
18
|
+
@model = model&.strip&.downcase&.freeze
|
|
19
|
+
@model_parsers = model_parsers
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# 返回当前平台策略声明的命令目录。
|
|
23
|
+
def commands = self.class::COMMANDS
|
|
24
|
+
|
|
25
|
+
# 动态目录可替换解析器索引,同时保留型号特定选择。
|
|
26
|
+
def parsers = self.class::PARSERS
|
|
27
|
+
|
|
28
|
+
# 保留选择顺序并归组共享视图,不混用不同命令的正文。
|
|
29
|
+
def queries(names)
|
|
30
|
+
unless names.is_a?(Array) && names.uniq == names && (names - commands.keys).empty?
|
|
31
|
+
raise ArgumentError, "Query dimensions must be unique supported names"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
plan = names.each_with_object({}) do |name, queries|
|
|
35
|
+
commands_for(name).each { |command| (queries[command] ||= []) << name }
|
|
36
|
+
end
|
|
37
|
+
plan.each_value(&:freeze)
|
|
38
|
+
plan.freeze
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# 单个维度可能需要多个独立回显;调用方必须分别保存正文。
|
|
42
|
+
def commands_for(name)
|
|
43
|
+
declaration = commands.fetch(name) { raise ArgumentError, "Unsupported parser dimension" }
|
|
44
|
+
queries = declaration.is_a?(Array) ? declaration : [ declaration ]
|
|
45
|
+
unless queries.any? && queries.uniq == queries && queries.all? { |query| query.is_a?(String) && !query.strip.empty? && !/[\x00-\x1f\x7f]/.match?(query) }
|
|
46
|
+
raise ArgumentError, "Parser dimension must declare one or more unique commands"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
queries.map { |query| query.frozen? ? query : query.dup.freeze }.freeze
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# 解析支持的观测维度对应的命令。
|
|
53
|
+
def command(name)
|
|
54
|
+
queries = commands_for(name)
|
|
55
|
+
raise ArgumentError, "Parser dimension has multiple commands; select one explicitly" unless queries.one?
|
|
56
|
+
|
|
57
|
+
queries.first
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# 确定维度使用的解析器,包括命令特定变体。
|
|
61
|
+
def parser_for(name, command: nil)
|
|
62
|
+
query = select_command([ name ], command)
|
|
63
|
+
@model_parsers.fetch(query) { parsers.fetch(query) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# 选择目标命令解析器,并投影单个请求维度。
|
|
67
|
+
def parse(name, text, complete: false, command: nil)
|
|
68
|
+
parse_many([ name ], text, complete: complete, command: command).fetch(name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# AP、Radio 等共享视图仅解析一次,确保状态与数量证据一致。
|
|
72
|
+
def parse_many(names, text, complete: false, command: nil)
|
|
73
|
+
unless names.is_a?(Array) && names.any? && names.uniq == names
|
|
74
|
+
raise ArgumentError, "Parser dimensions must be a nonempty unique array"
|
|
75
|
+
end
|
|
76
|
+
query = select_command(names, command)
|
|
77
|
+
|
|
78
|
+
result = parser_for(names.first, command: query).parse(text, complete: complete)
|
|
79
|
+
names.to_h { |name| [ name, project_independently(name, result) ] }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# 仅当存在唯一公共查询时允许推断;显式命令必须覆盖全部请求视图。
|
|
85
|
+
def select_command(names, selected)
|
|
86
|
+
candidates = names.map { |name| commands_for(name) }.reduce { |common, queries| common & queries }
|
|
87
|
+
unless selected.nil?
|
|
88
|
+
raise ArgumentError, "Selected command does not provide all parser dimensions" unless candidates.include?(selected)
|
|
89
|
+
|
|
90
|
+
selected
|
|
91
|
+
else
|
|
92
|
+
raise ArgumentError, "One CLI output must belong to one command" unless candidates.one?
|
|
93
|
+
|
|
94
|
+
candidates.first
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# 采集前校验所选型号,并隔离后续修改对型号查找的影响。
|
|
99
|
+
def model_parsers
|
|
100
|
+
declarations = self.class::MODEL_PARSERS
|
|
101
|
+
raise ArgumentError, "Model parsers must be a hash" unless declarations.is_a?(Hash)
|
|
102
|
+
|
|
103
|
+
overrides = declarations.fetch(model, {})
|
|
104
|
+
unless overrides.is_a?(Hash) && overrides.all? do |command, parser|
|
|
105
|
+
self.class::PARSERS.key?(command) && parser.is_a?(Class) && parser < Parser
|
|
106
|
+
end
|
|
107
|
+
raise ArgumentError, "Model parsers must override declared commands with Parser subclasses"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
overrides.dup.freeze
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# 投影失败仅影响单个视图;命令选择及解析错误仍正常抛出。
|
|
114
|
+
def project_independently(name, result)
|
|
115
|
+
return failed_projection(:invalid_parser_result) unless result.is_a?(Result) || result.is_a?(ResultSet)
|
|
116
|
+
|
|
117
|
+
projected = project(name, result)
|
|
118
|
+
projected.is_a?(Result) ? projected : failed_projection(:invalid_parser_result)
|
|
119
|
+
rescue KeyError
|
|
120
|
+
code = result.is_a?(ResultSet) && !result.results.key?(name) ? :missing_parser_view : :parser_projection_failed
|
|
121
|
+
failed_projection(code)
|
|
122
|
+
rescue StandardError
|
|
123
|
+
failed_projection(:parser_projection_failed)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# 诊断代码描述边界失败,不保留异常消息或原始值。
|
|
127
|
+
def failed_projection(code) = Result.new(status: :failed, issues: [ { code: code } ])
|
|
128
|
+
|
|
129
|
+
# 从共享命令结果中提取请求维度。
|
|
130
|
+
def project(name, result)
|
|
131
|
+
result.is_a?(ResultSet) ? result.fetch(name) : result
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CtxParser
|
|
4
|
+
# 检测身份冲突,不附加厂商特定的保留或完整性策略。
|
|
5
|
+
class Records
|
|
6
|
+
attr_reader :rows, :first_occurrences, :unambiguous, :duplicate_identity_count, :duplicate_row_count
|
|
7
|
+
|
|
8
|
+
# 冻结筛选结果数组,记录值的所有权仍属于调用方。
|
|
9
|
+
def initialize(rows, identity: nil, &key)
|
|
10
|
+
unless rows.is_a?(Array) && rows.all? { |row| row.is_a?(Hash) }
|
|
11
|
+
raise ArgumentError, "Records must be hashes"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
key ||= identity if identity.respond_to?(:call)
|
|
15
|
+
key ||= ->(row) { row.values_at(*identity) } if identity.is_a?(Array) && identity.any?
|
|
16
|
+
raise ArgumentError, "Identity must be a nonempty array or callable" unless key
|
|
17
|
+
|
|
18
|
+
@rows = rows.dup.freeze
|
|
19
|
+
groups = @rows.group_by { |row| key.call(row) }.values
|
|
20
|
+
@first_occurrences = groups.map(&:first).freeze
|
|
21
|
+
@unambiguous = groups.filter_map { |group| group.first if group.one? }.freeze
|
|
22
|
+
@duplicate_identity_count = groups.count { |group| group.size > 1 }
|
|
23
|
+
@duplicate_row_count = @rows.size - groups.size
|
|
24
|
+
freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 判断是否有多条记录声明相同逻辑身份。
|
|
28
|
+
def duplicates? = duplicate_identity_count.positive?
|
|
29
|
+
# 序列化诊断元数据,不包含原始来源文本。
|
|
30
|
+
def as_json(*) = { rows: rows.size, duplicate_identities: duplicate_identity_count }
|
|
31
|
+
|
|
32
|
+
# 提供身份统计,不记录可能包含敏感信息的记录正文。
|
|
33
|
+
def inspect = "#<#{self.class.name} size=#{rows.size} duplicate_identities=#{duplicate_identity_count}>"
|
|
34
|
+
alias to_s inspect
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "profile"
|
|
4
|
+
|
|
5
|
+
module CtxParser
|
|
6
|
+
# 各领域独立持有注册表;不支持的平台直接拒绝,不猜测其他厂商语法。
|
|
7
|
+
class Registry
|
|
8
|
+
# 注册平台策略及其声明的别名。
|
|
9
|
+
def initialize(profiles:, aliases: {})
|
|
10
|
+
@profiles = profiles.transform_keys { |key| platform_key(key) }.freeze
|
|
11
|
+
@aliases = aliases.transform_keys { |key| platform_key(key) }.transform_values { |key| platform_key(key) }.freeze
|
|
12
|
+
unless @profiles.size == profiles.size && @aliases.size == aliases.size &&
|
|
13
|
+
@profiles.values.all? { |profile| profile.is_a?(Class) && profile < Profile } &&
|
|
14
|
+
@aliases.values.all? { |key| @profiles.key?(key) } && (@profiles.keys & @aliases.keys).empty?
|
|
15
|
+
raise ArgumentError, "Invalid parser registry"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# 解析请求的解析结果或平台策略,拒绝未知名称。
|
|
20
|
+
def fetch(platform, model: nil)
|
|
21
|
+
key = platform_key(platform)
|
|
22
|
+
key = @aliases.fetch(key, key)
|
|
23
|
+
profile = @profiles.fetch(key) { raise ArgumentError, "Unsupported parser platform" }
|
|
24
|
+
profile.new(model: model)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# 选择已注册平台策略前,先归一化平台别名。
|
|
30
|
+
def platform_key(value)
|
|
31
|
+
raise ArgumentError, "Parser platform must be a name" unless value.is_a?(String) || value.is_a?(Symbol)
|
|
32
|
+
|
|
33
|
+
key = value.to_s.strip.downcase
|
|
34
|
+
raise ArgumentError, "Parser platform must be a name" if key.empty?
|
|
35
|
+
|
|
36
|
+
key.to_sym
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "immutable"
|
|
4
|
+
|
|
5
|
+
module CtxParser
|
|
6
|
+
# 仅保存解析结果;采集命令、时间与证据由调用方管理。
|
|
7
|
+
Result = Data.define(:status, :records, :issues) do
|
|
8
|
+
# 冻结解析状态、归一化记录与诊断证据。
|
|
9
|
+
def initialize(status:, records: [], issues: [])
|
|
10
|
+
raise ArgumentError, "Invalid parser status" unless %i[complete partial failed unsupported].include?(status)
|
|
11
|
+
raise ArgumentError, "Records must be hashes" unless records.is_a?(Array) && records.all? { |row| row.is_a?(Hash) }
|
|
12
|
+
|
|
13
|
+
super(status: status, records: Immutable.copy(records), issues: Immutable.copy(issues))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 判断解析结果是否具有完整证据。
|
|
17
|
+
def complete? = status == :complete
|
|
18
|
+
|
|
19
|
+
# 展示对象身份与状态,避免泄露原始回显或凭据。
|
|
20
|
+
def inspect = "#<#{self.class.name} status=#{status} records=#{records.size}>"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "result"
|
|
4
|
+
|
|
5
|
+
module CtxParser
|
|
6
|
+
# 同一回显可产生多份清单,各自保留完整性与诊断。
|
|
7
|
+
ResultSet = Data.define(:results) do
|
|
8
|
+
# 冻结具名章节结果,同时保留各章节的独立状态。
|
|
9
|
+
def initialize(results:)
|
|
10
|
+
unless results.is_a?(Hash) && results.any? && results.values.all? { |result| result.is_a?(Result) }
|
|
11
|
+
raise ArgumentError, "Result set must contain parser results"
|
|
12
|
+
end
|
|
13
|
+
super(results: results.dup.freeze)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 返回必需的具名章节结果。
|
|
17
|
+
def fetch(name) = results.fetch(name)
|
|
18
|
+
|
|
19
|
+
# 判断所有已选事实是否都有完整证据支持。
|
|
20
|
+
def complete? = results.values.all?(&:complete?)
|
|
21
|
+
|
|
22
|
+
# 推导汇总状态,避免将证据缺失当作成功。
|
|
23
|
+
def status
|
|
24
|
+
return :complete if complete?
|
|
25
|
+
return :partial if results.values.any? { |result| %i[complete partial].include?(result.status) }
|
|
26
|
+
|
|
27
|
+
results.values.all? { |result| result.status == :unsupported } ? :unsupported : :failed
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# 从全部章节结果汇总去重后的诊断。
|
|
31
|
+
def issues = results.values.flat_map(&:issues).uniq
|
|
32
|
+
|
|
33
|
+
# 展示对象身份与状态,避免泄露原始回显或凭据。
|
|
34
|
+
def inspect = "#<#{self.class.name} status=#{status} sections=#{results.keys.join(',')}>"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
|
|
5
|
+
module CtxParser
|
|
6
|
+
# 严格转换标量;单位及可选字段语法仍由命令解析器负责。
|
|
7
|
+
module Values
|
|
8
|
+
class OutOfRange < ArgumentError; end
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# 解析十进制整数,并校验声明的边界。
|
|
13
|
+
def integer(raw, min: 0, max: nil)
|
|
14
|
+
return if raw.nil?
|
|
15
|
+
raise ArgumentError, "Invalid decimal integer" unless /\A\d+\z/.match?(raw.to_s)
|
|
16
|
+
|
|
17
|
+
value = Integer(raw.to_s, 10)
|
|
18
|
+
raise OutOfRange, "Integer out of range" if (min && value < min) || (max && value > max)
|
|
19
|
+
|
|
20
|
+
value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# 归一化 MAC 地址,拒绝格式错误的输入。
|
|
24
|
+
def mac(raw)
|
|
25
|
+
return if raw.nil?
|
|
26
|
+
unless /\A(?:[0-9a-f]{12}|(?:[0-9a-f]{4}[-.]){2}[0-9a-f]{4}|(?:[0-9a-f]{2}:){5}[0-9a-f]{2})\z/i.match?(raw)
|
|
27
|
+
raise ArgumentError, "Invalid MAC address"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
raw.delete("-.:").downcase.scan(/../).join(":")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# 归一化主机地址,保留 IPv4 与 IPv6 语义。
|
|
34
|
+
def host(raw, allow_zone: false)
|
|
35
|
+
return if raw.nil?
|
|
36
|
+
raise ArgumentError, "Invalid host address" if raw.include?("/") || (!allow_zone && raw.include?("%"))
|
|
37
|
+
|
|
38
|
+
IPAddr.new(raw).to_s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# 校验主机地址有效性,不让格式错误值继续传播。
|
|
42
|
+
def valid_host?(raw, allow_zone: false)
|
|
43
|
+
!host(raw, allow_zone: allow_zone).nil?
|
|
44
|
+
rescue ArgumentError, IPAddr::Error
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# 统一校验网段与接口地址,仅接口投影保留主机位。
|
|
49
|
+
def ipv4_prefix(raw, host: false)
|
|
50
|
+
unless raw.is_a?(String) && raw.match?(%r{\A\d+(?:\.\d+){3}/\d+\z})
|
|
51
|
+
raise ArgumentError, "IPv4 prefix required"
|
|
52
|
+
end
|
|
53
|
+
network = IPAddr.new(raw)
|
|
54
|
+
raise ArgumentError, "IPv4 prefix required" unless network.ipv4?
|
|
55
|
+
|
|
56
|
+
host ? raw : "#{network}/#{network.prefix}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/ctx_parser.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ctx_parser/version"
|
|
4
|
+
|
|
5
|
+
# 加载可能被独立自动加载的依赖前,先定义命名空间。
|
|
6
|
+
module CtxParser
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "ctx_parser/parser"
|
|
10
|
+
if CtxParser.const_defined?(:Parser, false)
|
|
11
|
+
require_relative "ctx_parser/configuration_parser"
|
|
12
|
+
require_relative "ctx_parser/values"
|
|
13
|
+
require_relative "ctx_parser/records"
|
|
14
|
+
require_relative "ctx_parser/command_catalog"
|
|
15
|
+
require_relative "ctx_parser/profile"
|
|
16
|
+
require_relative "ctx_parser/registry"
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ctx_parser
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gatework contributors
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: digest
|
|
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: ipaddr
|
|
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'
|
|
40
|
+
description: Reusable configuration documents, command catalogs, platform profiles,
|
|
41
|
+
immutable results and parsing primitives for network automation.
|
|
42
|
+
executables: []
|
|
43
|
+
extensions: []
|
|
44
|
+
extra_rdoc_files: []
|
|
45
|
+
files:
|
|
46
|
+
- CHANGELOG.md
|
|
47
|
+
- LICENSE
|
|
48
|
+
- README-zh.md
|
|
49
|
+
- README.md
|
|
50
|
+
- docs/architecture-zh.md
|
|
51
|
+
- docs/release.md
|
|
52
|
+
- examples/offline.rb
|
|
53
|
+
- lib/ctx_parser.rb
|
|
54
|
+
- lib/ctx_parser/banner.rb
|
|
55
|
+
- lib/ctx_parser/block.rb
|
|
56
|
+
- lib/ctx_parser/capture.rb
|
|
57
|
+
- lib/ctx_parser/command_catalog.rb
|
|
58
|
+
- lib/ctx_parser/configuration_parser.rb
|
|
59
|
+
- lib/ctx_parser/cursor.rb
|
|
60
|
+
- lib/ctx_parser/document.rb
|
|
61
|
+
- lib/ctx_parser/immutable.rb
|
|
62
|
+
- lib/ctx_parser/input.rb
|
|
63
|
+
- lib/ctx_parser/line.rb
|
|
64
|
+
- lib/ctx_parser/parser.rb
|
|
65
|
+
- lib/ctx_parser/profile.rb
|
|
66
|
+
- lib/ctx_parser/records.rb
|
|
67
|
+
- lib/ctx_parser/registry.rb
|
|
68
|
+
- lib/ctx_parser/result.rb
|
|
69
|
+
- lib/ctx_parser/result_set.rb
|
|
70
|
+
- lib/ctx_parser/values.rb
|
|
71
|
+
- lib/ctx_parser/version.rb
|
|
72
|
+
homepage: https://github.com/gatework/ctx_parser
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata:
|
|
76
|
+
source_code_uri: https://github.com/gatework/ctx_parser
|
|
77
|
+
bug_tracker_uri: https://github.com/gatework/ctx_parser/issues
|
|
78
|
+
changelog_uri: https://github.com/gatework/ctx_parser/blob/main/CHANGELOG.md
|
|
79
|
+
documentation_uri: https://github.com/gatework/ctx_parser#readme
|
|
80
|
+
allowed_push_host: https://rubygems.org
|
|
81
|
+
rubygems_mfa_required: 'true'
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 4.0.6
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubygems_version: 4.0.16
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: Parse network CLI evidence into normalized facts.
|
|
99
|
+
test_files: []
|