latticerb 0.1.0 → 0.2.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/README.md +2 -1
- data/lib/lattice/cli.rb +1 -1
- data/lib/lattice/formatters.rb +76 -9
- data/lib/lattice/version.rb +1 -1
- data/lib/lattice.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60031e0fe6baf8ed371a9e2705e905118008bd25c53f588754405399d7727429
|
|
4
|
+
data.tar.gz: 893b27940cca00121e203cf8ed0e685cdddfc8d7ee97758d0b1864d7eef1cb6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d13bb576b7255cb8cfb255de5cf725d5a344ae8264861fcb0207803999b246fdf5ebf5ba4f07ba511846fbc8301df26256ccda8f48a7c785d66022570ac5a7d
|
|
7
|
+
data.tar.gz: fb8d83f8f505e560cdb1277e419a53dea19d2a9350c9090b04c2b5a380b9e6dbbaccd040b3d617d6d77bb1fa1fb1987b499d94cb5cf3803d8d27c54b9c5cbf34
|
data/README.md
CHANGED
|
@@ -54,7 +54,7 @@ run = Lattice.generate(
|
|
|
54
54
|
|
|
55
55
|
run.scenarios # array of { name => value } hashes
|
|
56
56
|
run.coverage_curve # cumulative coverage per scenario
|
|
57
|
-
run.to_table # human-readable table (also: to_json_output, to_csv, to_summary)
|
|
57
|
+
run.to_table # human-readable table (also: to_json_output, to_yaml_output, to_csv, to_summary)
|
|
58
58
|
|
|
59
59
|
Lattice.scenarios(file: "...") # shortcut straight to the hashes
|
|
60
60
|
Lattice.validate!(schema) # raises Lattice::ValidationError / Lattice::ConstraintError
|
|
@@ -70,6 +70,7 @@ The gem installs a `lattice` executable mirroring the Python CLI:
|
|
|
70
70
|
cat schema.yaml | lattice validate
|
|
71
71
|
cat schema.yaml | lattice generate # JSON by default
|
|
72
72
|
lattice generate schema.yaml --format table
|
|
73
|
+
lattice generate schema.yaml --format yaml # same content as JSON, for hand-edited files
|
|
73
74
|
lattice generate schema.yaml --strength 3 --seed 7
|
|
74
75
|
lattice agent instructions # coding-agent integration guidance
|
|
75
76
|
```
|
data/lib/lattice/cli.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Lattice
|
|
|
65
65
|
}
|
|
66
66
|
parser = OptionParser.new do |opts|
|
|
67
67
|
opts.on("--strength STRENGTH", Integer)
|
|
68
|
-
opts.on("--format FORMAT", %w[table json csv summary])
|
|
68
|
+
opts.on("--format FORMAT", %w[table json csv summary yaml])
|
|
69
69
|
opts.on("--seed SEED", Integer)
|
|
70
70
|
opts.on("--pool-size SIZE", Integer) { |value| options[:pool_size] = value }
|
|
71
71
|
opts.on("--max-rows ROWS", Integer) { |value| options[:max_rows] = value }
|
data/lib/lattice/formatters.rb
CHANGED
|
@@ -7,8 +7,9 @@ require_relative "ipog"
|
|
|
7
7
|
require_relative "model"
|
|
8
8
|
|
|
9
9
|
module Lattice
|
|
10
|
-
# Output rendering. Table, JSON, CSV, and
|
|
11
|
-
# to the Python implementation's output for the same model
|
|
10
|
+
# Output rendering. Table, JSON, CSV, summary, and YAML formats are
|
|
11
|
+
# byte-identical to the Python implementation's output for the same model
|
|
12
|
+
# and result.
|
|
12
13
|
module Formatters
|
|
13
14
|
extend self
|
|
14
15
|
|
|
@@ -18,6 +19,7 @@ module Lattice
|
|
|
18
19
|
when "json" then format_json(model, result, seed:)
|
|
19
20
|
when "csv" then format_csv(model, result)
|
|
20
21
|
when "summary" then format_summary(model, result)
|
|
22
|
+
when "yaml" then format_yaml(model, result, seed:)
|
|
21
23
|
else
|
|
22
24
|
raise ArgumentError, "Unknown format `#{output_format}`."
|
|
23
25
|
end
|
|
@@ -77,16 +79,81 @@ module Lattice
|
|
|
77
79
|
}
|
|
78
80
|
end
|
|
79
81
|
}
|
|
80
|
-
|
|
82
|
+
# Python's json.dumps also escapes DEL, which Ruby's ascii_only leaves raw.
|
|
83
|
+
JSON.pretty_generate(payload, ascii_only: true).gsub("\u007f", "\\u007f")
|
|
81
84
|
end
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
# Same content as format_json, written by hand in one fixed style so Ruby
|
|
87
|
+
# and Python emit identical bytes (Psych and PyYAML each quote and wrap
|
|
88
|
+
# differently). Every string is double-quoted, so no value can be read
|
|
89
|
+
# back as a boolean, null, number or date.
|
|
90
|
+
def format_yaml(model, result, seed:)
|
|
91
|
+
last_point = result.coverage_curve.last
|
|
92
|
+
lines = [
|
|
93
|
+
"meta:",
|
|
94
|
+
" model_name: #{yaml_string(model.model_name)}",
|
|
95
|
+
" strength: #{model.strength}",
|
|
96
|
+
" test_count: #{result.rows.length}",
|
|
97
|
+
" exhaustive_count: #{model.exhaustive_count}",
|
|
98
|
+
" reduction: #{yaml_string(reduction_label(model.exhaustive_count, result.rows.length))}",
|
|
99
|
+
" coverage: #{yaml_string(last_point ? format('%.1f%%', last_point.cumulative_pct) : '0.0%')}",
|
|
100
|
+
" seed: #{seed.nil? ? 'null' : seed}"
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
if result.coverage_curve.empty?
|
|
104
|
+
lines << "coverage_curve: []"
|
|
105
|
+
else
|
|
106
|
+
lines << "coverage_curve:"
|
|
107
|
+
result.coverage_curve.each do |point|
|
|
108
|
+
lines << " - scenario: #{point.scenario}"
|
|
109
|
+
lines << " cumulative_pct: #{JSON.generate(point.cumulative_pct)}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
rows = named_rows(model, result)
|
|
114
|
+
if rows.empty?
|
|
115
|
+
lines << "scenarios: []"
|
|
116
|
+
else
|
|
117
|
+
lines << "scenarios:"
|
|
118
|
+
rows.each_with_index do |row, index|
|
|
119
|
+
lines << " - id: #{index + 1}"
|
|
120
|
+
lines << " values:"
|
|
121
|
+
model.parameter_names.each do |name|
|
|
122
|
+
lines << " #{yaml_string(name)}: #{yaml_string(row[name])}"
|
|
123
|
+
end
|
|
88
124
|
end
|
|
89
|
-
end
|
|
125
|
+
end
|
|
126
|
+
lines.join("\n")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
YAML_ESCAPES = {
|
|
130
|
+
"\"" => "\\\"", "\\" => "\\\\", "\b" => "\\b", "\t" => "\\t", "\n" => "\\n", "\f" => "\\f", "\r" => "\\r"
|
|
131
|
+
}.freeze
|
|
132
|
+
|
|
133
|
+
# Characters escaped inside a double-quoted YAML string: the quote and
|
|
134
|
+
# backslash, C0 controls, DEL, C1 controls (U+0085 would otherwise fold to
|
|
135
|
+
# a space), the Unicode line and paragraph separators, the byte-order mark,
|
|
136
|
+
# and the two noncharacters YAML cannot hold. Everything else is written as
|
|
137
|
+
# UTF-8, so accented letters and emoji stay readable.
|
|
138
|
+
YAML_ESCAPED_CHARS = /["\\\u0000-\u001f\u007f-\u009f\u2028\u2029\ufeff\ufffe\uffff]/
|
|
139
|
+
|
|
140
|
+
def yaml_string(value)
|
|
141
|
+
escaped = value.gsub(YAML_ESCAPED_CHARS) do |char|
|
|
142
|
+
YAML_ESCAPES.fetch(char) { format("\\u%04x", char.ord) }
|
|
143
|
+
end
|
|
144
|
+
"\"#{escaped}\""
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Matches Python's csv.writer: empty fields are written bare, except a row
|
|
148
|
+
# made of a single empty field, which is written as "" so it isn't a blank
|
|
149
|
+
# line.
|
|
150
|
+
def format_csv(model, result)
|
|
151
|
+
records = [model.parameter_names] + named_rows(model, result).map do |row|
|
|
152
|
+
model.parameter_names.map { |name| row[name] }
|
|
153
|
+
end
|
|
154
|
+
records.map do |record|
|
|
155
|
+
record == [""] ? "\"\"\r\n" : CSV.generate_line(record, row_sep: "\r\n", quote_empty: false)
|
|
156
|
+
end.join.strip
|
|
90
157
|
end
|
|
91
158
|
|
|
92
159
|
def headline(model, result)
|
data/lib/lattice/version.rb
CHANGED
data/lib/lattice.rb
CHANGED
|
@@ -32,6 +32,7 @@ module Lattice
|
|
|
32
32
|
def to_json_output = Formatters.render_output(model, result, output_format: "json", seed:)
|
|
33
33
|
def to_csv = Formatters.render_output(model, result, output_format: "csv", seed:)
|
|
34
34
|
def to_summary = Formatters.render_output(model, result, output_format: "summary", seed:)
|
|
35
|
+
def to_yaml_output = Formatters.render_output(model, result, output_format: "yaml", seed:)
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
class << self
|