expressir 2.1.15 → 2.1.16
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/.rubocop_todo.yml +15 -8
- data/README.adoc +200 -58
- data/docs/benchmarking.adoc +107 -0
- data/expressir.gemspec +3 -1
- data/lib/expressir/benchmark.rb +307 -0
- data/lib/expressir/cli.rb +165 -0
- data/lib/expressir/config.rb +40 -1
- data/lib/expressir/express/cache.rb +1 -1
- data/lib/expressir/express/error.rb +87 -0
- data/lib/expressir/express/hyperlink_formatter.rb +1 -1
- data/lib/expressir/express/parser.rb +53 -30
- data/lib/expressir/express/schema_head_formatter.rb +1 -1
- data/lib/expressir/express/visitor.rb +54 -48
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +1 -0
- metadata +34 -4
- data/docs/development.md +0 -90
@@ -0,0 +1,307 @@
|
|
1
|
+
module Expressir
|
2
|
+
module Benchmark
|
3
|
+
require "benchmark"
|
4
|
+
require "json"
|
5
|
+
require "csv"
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Measures the execution time of schema loading and optionally reports it
|
9
|
+
# @param file [String] File being processed
|
10
|
+
# @param options [Hash] Options for benchmarking
|
11
|
+
# @return The result of the block
|
12
|
+
def measure_file(file)
|
13
|
+
return yield unless Expressir.configuration.benchmark_enabled?
|
14
|
+
|
15
|
+
if Expressir.configuration.benchmark_ips?
|
16
|
+
require "benchmark/ips"
|
17
|
+
|
18
|
+
result = nil
|
19
|
+
::Benchmark.ips do |x|
|
20
|
+
x.config(time: Expressir.configuration.benchmark_iterations,
|
21
|
+
warmup: Expressir.configuration.benchmark_warmup)
|
22
|
+
|
23
|
+
x.report("Loading #{file}") do
|
24
|
+
result = yield
|
25
|
+
end
|
26
|
+
|
27
|
+
if Expressir.configuration.benchmark_save?
|
28
|
+
x.save! "expressir_benchmark_#{File.basename(file)}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Calculate objects per second
|
33
|
+
if result.respond_to?(:schemas)
|
34
|
+
objects_per_second = calculate_objects_per_second(result)
|
35
|
+
puts "Objects per second: #{objects_per_second}" if Expressir.configuration.benchmark_verbose?
|
36
|
+
end
|
37
|
+
|
38
|
+
else
|
39
|
+
# Standard benchmarking
|
40
|
+
result = nil
|
41
|
+
time = ::Benchmark.measure do
|
42
|
+
result = yield
|
43
|
+
end
|
44
|
+
|
45
|
+
if result.respond_to?(:schemas)
|
46
|
+
objects_per_second = calculate_objects_per_second(result, time.real)
|
47
|
+
output_benchmark_result(file, time.real, objects_per_second)
|
48
|
+
else
|
49
|
+
output_benchmark_result(file, time.real)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
# Benchmarks file collection processing
|
57
|
+
# @param files [Array<String>] List of files to process
|
58
|
+
# @return [Array] Results from processing files
|
59
|
+
def measure_collection(files)
|
60
|
+
unless Expressir.configuration.benchmark_enabled?
|
61
|
+
# Process each file individually even when benchmarking is disabled
|
62
|
+
results = []
|
63
|
+
files.each do |file|
|
64
|
+
file_results = yield(file)
|
65
|
+
results.concat(Array(file_results))
|
66
|
+
end
|
67
|
+
return results
|
68
|
+
end
|
69
|
+
|
70
|
+
results = []
|
71
|
+
total_time = ::Benchmark.measure do
|
72
|
+
files.each_with_index do |file, i|
|
73
|
+
if Expressir.configuration.benchmark_verbose?
|
74
|
+
puts "#{i + 1}/#{files.length} Processing #{file}"
|
75
|
+
end
|
76
|
+
|
77
|
+
file_results = measure_file(file) do
|
78
|
+
yield(file)
|
79
|
+
end
|
80
|
+
|
81
|
+
results.concat(Array(file_results))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
output_collection_result(files.length, total_time.real, results)
|
86
|
+
results
|
87
|
+
end
|
88
|
+
|
89
|
+
# Measures reference resolution
|
90
|
+
# @return The result of the block
|
91
|
+
def measure_references
|
92
|
+
return yield unless Expressir.configuration.benchmark_enabled?
|
93
|
+
|
94
|
+
if Expressir.configuration.benchmark_ips?
|
95
|
+
require "benchmark/ips"
|
96
|
+
|
97
|
+
result = nil
|
98
|
+
::Benchmark.ips do |x|
|
99
|
+
x.config(time: Expressir.configuration.benchmark_iterations,
|
100
|
+
warmup: Expressir.configuration.benchmark_warmup)
|
101
|
+
|
102
|
+
x.report("Reference resolution") do
|
103
|
+
result = yield
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
else
|
108
|
+
result = nil
|
109
|
+
time = ::Benchmark.measure do
|
110
|
+
result = yield
|
111
|
+
end
|
112
|
+
|
113
|
+
output_benchmark_result("Reference resolution", time.real)
|
114
|
+
end
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
118
|
+
# Benchmark with caching
|
119
|
+
# @param path [String] Path to the file
|
120
|
+
# @param cache_path [String] Path for the cache file
|
121
|
+
# @return [Hash] Results with timing information
|
122
|
+
def measure_with_cache(path, cache_path = nil)
|
123
|
+
return yield(path) unless Expressir.configuration.benchmark_enabled?
|
124
|
+
|
125
|
+
require "tempfile"
|
126
|
+
temp_file = nil
|
127
|
+
|
128
|
+
unless cache_path
|
129
|
+
temp_file = Tempfile.new(["expressir_cache", ".bin"])
|
130
|
+
cache_path = temp_file.path
|
131
|
+
end
|
132
|
+
|
133
|
+
results = {}
|
134
|
+
|
135
|
+
# Measure parsing time
|
136
|
+
parsing_time = ::Benchmark.measure do
|
137
|
+
results[:repository] = yield(path)
|
138
|
+
end
|
139
|
+
results[:parsing_time] = parsing_time.real
|
140
|
+
|
141
|
+
# Measure cache writing time
|
142
|
+
cache_write_time = ::Benchmark.measure do
|
143
|
+
Expressir::Express::Cache.to_file(cache_path, results[:repository])
|
144
|
+
end
|
145
|
+
results[:cache_write_time] = cache_write_time.real
|
146
|
+
|
147
|
+
# Measure cache reading time
|
148
|
+
cache_read_time = ::Benchmark.measure do
|
149
|
+
results[:cached_repository] = Expressir::Express::Cache.from_file(cache_path)
|
150
|
+
end
|
151
|
+
results[:cache_read_time] = cache_read_time.real
|
152
|
+
|
153
|
+
# Output results
|
154
|
+
if Expressir.configuration.benchmark_verbose?
|
155
|
+
puts "Parsing time: #{results[:parsing_time].round(4)}s"
|
156
|
+
puts "Cache write time: #{results[:cache_write_time].round(4)}s"
|
157
|
+
puts "Cache read time: #{results[:cache_read_time].round(4)}s"
|
158
|
+
|
159
|
+
if results[:repository].respond_to?(:schemas)
|
160
|
+
objects = count_objects(results[:repository])
|
161
|
+
puts "Total objects: #{objects}"
|
162
|
+
puts "Objects per second (parsing): #{(objects / results[:parsing_time]).round(2)}"
|
163
|
+
puts "Objects per second (reading): #{(objects / results[:cache_read_time]).round(2)}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Clean up temp file if we created one
|
168
|
+
if temp_file
|
169
|
+
temp_file.close
|
170
|
+
temp_file.unlink
|
171
|
+
end
|
172
|
+
|
173
|
+
results
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
# Calculate objects per second
|
179
|
+
# @param repository [Object] The repository object
|
180
|
+
# @param time [Float] The time taken in seconds
|
181
|
+
# @return [Float] Objects per second
|
182
|
+
def calculate_objects_per_second(repository, time = 1.0)
|
183
|
+
objects = count_objects(repository)
|
184
|
+
(objects / time).round(2)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Count objects in a repository
|
188
|
+
# @param repository [Object] The repository object
|
189
|
+
# @return [Integer] Number of objects
|
190
|
+
def count_objects(repository)
|
191
|
+
return 0 unless repository.respond_to?(:schemas)
|
192
|
+
|
193
|
+
count = repository.schemas.size
|
194
|
+
|
195
|
+
repository.schemas.each do |schema|
|
196
|
+
count += schema.entities.size if schema.entities
|
197
|
+
count += schema.types.size if schema.types
|
198
|
+
count += schema.functions.size if schema.functions
|
199
|
+
count += schema.rules.size if schema.rules
|
200
|
+
count += schema.procedures.size if schema.procedures
|
201
|
+
count += schema.constants.size if schema.constants
|
202
|
+
|
203
|
+
# Count attributes in entities
|
204
|
+
schema.entities&.each do |entity|
|
205
|
+
# Count explicit attributes
|
206
|
+
if entity.attributes
|
207
|
+
count += entity.attributes.size
|
208
|
+
end
|
209
|
+
|
210
|
+
# Count inheritance relationships
|
211
|
+
count += 1 if entity.subtype_of
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
count
|
216
|
+
end
|
217
|
+
|
218
|
+
# Output benchmark result in the appropriate format
|
219
|
+
# @param label [String] Label for the benchmark
|
220
|
+
# @param time [Float] Time in seconds
|
221
|
+
# @param objects_per_second [Float, nil] Objects per second metric (optional)
|
222
|
+
def output_benchmark_result(label, time, objects_per_second = nil)
|
223
|
+
time_rounded = time.round(4)
|
224
|
+
|
225
|
+
case Expressir.configuration.benchmark_format
|
226
|
+
when "json"
|
227
|
+
result = {
|
228
|
+
label: label,
|
229
|
+
time_seconds: time_rounded,
|
230
|
+
}
|
231
|
+
result[:objects_per_second] = objects_per_second if objects_per_second
|
232
|
+
puts JSON.generate(result)
|
233
|
+
when "csv"
|
234
|
+
headers = ["Label", "Time (s)"]
|
235
|
+
headers << "Objects/s" if objects_per_second
|
236
|
+
|
237
|
+
values = [label, time_rounded]
|
238
|
+
values << objects_per_second if objects_per_second
|
239
|
+
|
240
|
+
puts headers.join(",")
|
241
|
+
puts values.join(",")
|
242
|
+
else
|
243
|
+
# Default format
|
244
|
+
message = "#{label}: #{time_rounded}s"
|
245
|
+
message += " (#{objects_per_second} objects/s)" if objects_per_second
|
246
|
+
puts message if Expressir.configuration.benchmark_verbose?
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Output collection benchmark result
|
251
|
+
# @param count [Integer] Number of files processed
|
252
|
+
# @param total_time [Float] Total time in seconds
|
253
|
+
# @param results [Array] Array of results
|
254
|
+
def output_collection_result(count, total_time, results)
|
255
|
+
avg_time = total_time / count
|
256
|
+
|
257
|
+
# Get total object count if possible
|
258
|
+
total_objects = 0
|
259
|
+
schema_count = 0
|
260
|
+
|
261
|
+
# Try to count total objects and schemas
|
262
|
+
results.each do |result|
|
263
|
+
if result.respond_to?(:schemas)
|
264
|
+
schema_count += result.schemas.size
|
265
|
+
total_objects += count_objects(result)
|
266
|
+
elsif result.is_a?(Array)
|
267
|
+
result.each do |r|
|
268
|
+
if r.respond_to?(:id) # Likely a schema
|
269
|
+
schema_count += 1
|
270
|
+
total_objects += 1 # Count the schema itself
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
objects_per_second = (total_objects / total_time).round(2)
|
277
|
+
|
278
|
+
case Expressir.configuration.benchmark_format
|
279
|
+
when "json"
|
280
|
+
result = {
|
281
|
+
files_processed: count,
|
282
|
+
schemas_loaded: schema_count,
|
283
|
+
total_objects: total_objects,
|
284
|
+
total_time_seconds: total_time.round(4),
|
285
|
+
average_time_seconds: avg_time.round(4),
|
286
|
+
objects_per_second: objects_per_second,
|
287
|
+
}
|
288
|
+
puts JSON.generate(result)
|
289
|
+
when "csv"
|
290
|
+
headers = ["Files", "Schemas", "Objects", "Total Time (s)", "Avg Time (s)", "Objects/s"]
|
291
|
+
values = [count, schema_count, total_objects, total_time.round(4), avg_time.round(4), objects_per_second]
|
292
|
+
|
293
|
+
puts headers.join(",")
|
294
|
+
puts values.join(",")
|
295
|
+
else
|
296
|
+
# Default format
|
297
|
+
if Expressir.configuration.benchmark_verbose?
|
298
|
+
puts "\nProcessed #{count} files in #{total_time.round(4)}s"
|
299
|
+
puts "Loaded #{schema_count} schemas containing #{total_objects} objects"
|
300
|
+
puts "Average time per file: #{avg_time.round(4)}s"
|
301
|
+
puts "Performance: #{objects_per_second} objects/s"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
data/lib/expressir/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "thor"
|
2
|
+
require "yaml"
|
2
3
|
|
3
4
|
module Expressir
|
4
5
|
class Cli < Thor
|
@@ -11,6 +12,67 @@ module Expressir
|
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
desc "clean PATH", "Strip remarks and prettify EXPRESS schema at PATH"
|
16
|
+
method_option :output, type: :string, desc: "Output file path (defaults to stdout)"
|
17
|
+
def clean(path)
|
18
|
+
repository = Expressir::Express::Parser.from_file(path)
|
19
|
+
formatted_schemas = repository.schemas.map do |schema|
|
20
|
+
# Format schema without remarks
|
21
|
+
schema.to_s(no_remarks: true)
|
22
|
+
end.join("\n\n")
|
23
|
+
|
24
|
+
if options[:output]
|
25
|
+
File.write(options[:output], formatted_schemas)
|
26
|
+
puts "Cleaned schema written to #{options[:output]}"
|
27
|
+
else
|
28
|
+
puts formatted_schemas
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "benchmark FILE_OR_YAML", "Benchmark schema loading performance for a file or list of files from YAML"
|
33
|
+
method_option :ips, type: :boolean, desc: "Use benchmark-ips for detailed statistics"
|
34
|
+
method_option :verbose, type: :boolean, desc: "Show verbose output"
|
35
|
+
method_option :save, type: :boolean, desc: "Save benchmark results to file"
|
36
|
+
method_option :format, type: :string, desc: "Output format (json, csv, default)"
|
37
|
+
def benchmark(path)
|
38
|
+
# Enable benchmarking via configuration
|
39
|
+
Expressir.configuration.benchmark_enabled = true
|
40
|
+
Expressir.configuration.benchmark_ips = options[:ips]
|
41
|
+
Expressir.configuration.benchmark_verbose = options[:verbose]
|
42
|
+
Expressir.configuration.benchmark_save = options[:save]
|
43
|
+
Expressir.configuration.benchmark_format = options[:format] if options[:format]
|
44
|
+
|
45
|
+
if [".yml", ".yaml"].include?(File.extname(path).downcase)
|
46
|
+
benchmark_from_yaml(path)
|
47
|
+
else
|
48
|
+
benchmark_file(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "benchmark-cache FILE_OR_YAML", "Benchmark schema loading with caching"
|
53
|
+
method_option :ips, type: :boolean, desc: "Use benchmark-ips for detailed statistics"
|
54
|
+
method_option :verbose, type: :boolean, desc: "Show verbose output"
|
55
|
+
method_option :save, type: :boolean, desc: "Save benchmark results to file"
|
56
|
+
method_option :format, type: :string, desc: "Output format (json, csv, default)"
|
57
|
+
method_option :cache_path, type: :string, desc: "Path to store the cache file"
|
58
|
+
def benchmark_cache(path)
|
59
|
+
# Same options as benchmark + cache_path
|
60
|
+
Expressir.configuration.benchmark_enabled = true
|
61
|
+
Expressir.configuration.benchmark_ips = options[:ips]
|
62
|
+
Expressir.configuration.benchmark_verbose = options[:verbose]
|
63
|
+
Expressir.configuration.benchmark_save = options[:save]
|
64
|
+
Expressir.configuration.benchmark_format = options[:format] if options[:format]
|
65
|
+
|
66
|
+
# Run benchmarks with cache
|
67
|
+
cache_path = options[:cache_path] || generate_temp_cache_path
|
68
|
+
|
69
|
+
if [".yml", ".yaml"].include?(File.extname(path).downcase)
|
70
|
+
benchmark_cache_from_yaml(path, cache_path)
|
71
|
+
else
|
72
|
+
benchmark_cache_file(path, cache_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
14
76
|
no_commands do
|
15
77
|
def _validate_schema(path)
|
16
78
|
repository = Expressir::Express::Parser.from_file(path)
|
@@ -30,6 +92,109 @@ module Expressir
|
|
30
92
|
puts msg
|
31
93
|
end
|
32
94
|
end
|
95
|
+
|
96
|
+
def benchmark_file(path)
|
97
|
+
puts "Express Schema Loading Benchmark"
|
98
|
+
puts "--------------------------------"
|
99
|
+
|
100
|
+
repository = Expressir::Benchmark.measure_file(path) do
|
101
|
+
Expressir::Express::Parser.from_file(path)
|
102
|
+
end
|
103
|
+
|
104
|
+
if repository
|
105
|
+
puts "Loaded #{repository.schemas.size} schemas"
|
106
|
+
else
|
107
|
+
puts "Failed to load schema"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def benchmark_from_yaml(yaml_path)
|
112
|
+
puts "Express Schema Loading Benchmark from YAML"
|
113
|
+
puts "--------------------------------"
|
114
|
+
|
115
|
+
# Load schema list from YAML
|
116
|
+
schema_list = YAML.load_file(yaml_path)
|
117
|
+
if schema_list.is_a?(Hash) && schema_list["schemas"]
|
118
|
+
# Handle format: { "schemas": ["path1", "path2", ...] }
|
119
|
+
schema_files = schema_list["schemas"]
|
120
|
+
elsif schema_list.is_a?(Array)
|
121
|
+
# Handle format: ["path1", "path2", ...]
|
122
|
+
schema_files = schema_list
|
123
|
+
else
|
124
|
+
puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
128
|
+
puts "YAML File: #{yaml_path}"
|
129
|
+
puts "Number of schemas in list: #{schema_files.size}"
|
130
|
+
puts "--------------------------------"
|
131
|
+
|
132
|
+
# Load the schemas
|
133
|
+
Expressir::Benchmark.measure_collection(schema_files) do |file|
|
134
|
+
repository = Expressir::Express::Parser.from_file(file)
|
135
|
+
repository.schemas
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def benchmark_cache_file(path, cache_path)
|
140
|
+
puts "Express Schema Loading Benchmark with Caching"
|
141
|
+
puts "--------------------------------"
|
142
|
+
puts "Schema: #{path}"
|
143
|
+
puts "Cache: #{cache_path}"
|
144
|
+
puts "--------------------------------"
|
145
|
+
|
146
|
+
# Benchmark with caching
|
147
|
+
results = Expressir::Benchmark.measure_with_cache(path, cache_path) do |file|
|
148
|
+
Expressir::Express::Parser.from_file(file)
|
149
|
+
end
|
150
|
+
|
151
|
+
if results[:repository]
|
152
|
+
schema_count = results[:repository].schemas.size
|
153
|
+
puts "Loaded #{schema_count} schemas"
|
154
|
+
else
|
155
|
+
puts "Failed to load schema"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def benchmark_cache_from_yaml(yaml_path, cache_path)
|
160
|
+
puts "Express Schema Loading Benchmark with Caching from YAML"
|
161
|
+
puts "--------------------------------"
|
162
|
+
|
163
|
+
# Load schema list from YAML
|
164
|
+
schema_list = YAML.load_file(yaml_path)
|
165
|
+
if schema_list.is_a?(Hash) && schema_list["schemas"]
|
166
|
+
# Handle format: { "schemas": ["path1", "path2", ...] }
|
167
|
+
schema_files = schema_list["schemas"]
|
168
|
+
elsif schema_list.is_a?(Array)
|
169
|
+
# Handle format: ["path1", "path2", ...]
|
170
|
+
schema_files = schema_list
|
171
|
+
else
|
172
|
+
puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
|
173
|
+
return
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "YAML File: #{yaml_path}"
|
177
|
+
puts "Number of schemas in list: #{schema_files.size}"
|
178
|
+
puts "Cache: #{cache_path}"
|
179
|
+
puts "--------------------------------"
|
180
|
+
|
181
|
+
# Process each file with caching
|
182
|
+
schema_files.each_with_index do |file, index|
|
183
|
+
puts "Processing file #{index + 1}/#{schema_files.size}: #{file}"
|
184
|
+
|
185
|
+
# Benchmark with caching
|
186
|
+
Expressir::Benchmark.measure_with_cache(file, cache_path) do |path|
|
187
|
+
Expressir::Express::Parser.from_file(path)
|
188
|
+
end
|
189
|
+
|
190
|
+
puts "--------------------------------"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def generate_temp_cache_path
|
195
|
+
require "tempfile"
|
196
|
+
Tempfile.new(["expressir_cache", ".bin"]).path
|
197
|
+
end
|
33
198
|
end
|
34
199
|
|
35
200
|
desc "validate *PATH", "validate EXPRESS schema located at PATH"
|
data/lib/expressir/config.rb
CHANGED
@@ -12,10 +12,49 @@ module Expressir
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class Configuration
|
15
|
-
attr_accessor :logs
|
15
|
+
attr_accessor :logs, :benchmark_enabled, :benchmark_ips, :benchmark_verbose,
|
16
|
+
:benchmark_save, :benchmark_iterations, :benchmark_warmup, :benchmark_parallel,
|
17
|
+
:benchmark_format
|
16
18
|
|
17
19
|
def initialize
|
18
20
|
@logs = %i(error)
|
21
|
+
# Enable benchmarking via environment variable
|
22
|
+
@benchmark_enabled = ENV["EXPRESSIR_BENCHMARK"] == "true"
|
23
|
+
# Use benchmark-ips for detailed statistics
|
24
|
+
@benchmark_ips = ENV["EXPRESSIR_BENCHMARK_IPS"] == "true"
|
25
|
+
# Show verbose output for benchmarking
|
26
|
+
@benchmark_verbose = ENV["EXPRESSIR_BENCHMARK_VERBOSE"] == "true"
|
27
|
+
# Save benchmark results to file
|
28
|
+
@benchmark_save = ENV["EXPRESSIR_BENCHMARK_SAVE"] == "true"
|
29
|
+
# Number of iterations for benchmark-ips
|
30
|
+
@benchmark_iterations = (ENV["EXPRESSIR_BENCHMARK_ITERATIONS"] || "3").to_i
|
31
|
+
# Warmup time for benchmark-ips in seconds
|
32
|
+
@benchmark_warmup = (ENV["EXPRESSIR_BENCHMARK_WARMUP"] || "1").to_i
|
33
|
+
# Enable parallel processing (for future implementation)
|
34
|
+
@benchmark_parallel = ENV["EXPRESSIR_BENCHMARK_PARALLEL"] == "true"
|
35
|
+
# Output format for benchmark results (default, json, csv)
|
36
|
+
@benchmark_format = ENV["EXPRESSIR_BENCHMARK_FORMAT"] || "default"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Helper methods for checking benchmark settings
|
40
|
+
def benchmark_enabled?
|
41
|
+
@benchmark_enabled
|
42
|
+
end
|
43
|
+
|
44
|
+
def benchmark_ips?
|
45
|
+
@benchmark_enabled && @benchmark_ips
|
46
|
+
end
|
47
|
+
|
48
|
+
def benchmark_verbose?
|
49
|
+
@benchmark_enabled && @benchmark_verbose
|
50
|
+
end
|
51
|
+
|
52
|
+
def benchmark_save?
|
53
|
+
@benchmark_enabled && @benchmark_save
|
54
|
+
end
|
55
|
+
|
56
|
+
def benchmark_parallel?
|
57
|
+
@benchmark_enabled && @benchmark_parallel
|
19
58
|
end
|
20
59
|
end
|
21
60
|
|
@@ -38,7 +38,7 @@ module Expressir
|
|
38
38
|
cache = Model::Cache.from_yaml(yaml)
|
39
39
|
|
40
40
|
if cache.version != version
|
41
|
-
raise Error.new(
|
41
|
+
raise Error::CacheVersionMismatchError.new(cache.version, version)
|
42
42
|
end
|
43
43
|
|
44
44
|
cache
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Expressir
|
2
|
+
module Express
|
3
|
+
# Module containing all Express-related errors
|
4
|
+
module Error
|
5
|
+
# Base error class for Express errors
|
6
|
+
class ExpressError < StandardError; end
|
7
|
+
|
8
|
+
# Error raised when a schema file fails to parse
|
9
|
+
class SchemaParseFailure < ExpressError
|
10
|
+
attr_reader :filename, :original_error, :parse_failure_cause
|
11
|
+
|
12
|
+
# Initialize a new SchemaParseFailure error
|
13
|
+
# @param filename [String] The name of the file that failed to parse
|
14
|
+
# @param original_error [Parslet::ParseFailed] The original parsing error
|
15
|
+
def initialize(filename, original_error)
|
16
|
+
@filename = filename
|
17
|
+
@original_error = original_error
|
18
|
+
@parse_failure_cause = original_error.parse_failure_cause
|
19
|
+
super("Failed to parse schema in file '#{filename}': #{original_error.message}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Error raised when there's a version mismatch in the cache
|
24
|
+
class CacheVersionMismatchError < ExpressError
|
25
|
+
attr_reader :cache_version, :expressir_version
|
26
|
+
|
27
|
+
# Initialize a new CacheVersionMismatchError
|
28
|
+
# @param cache_version [String] The version of the cache file
|
29
|
+
# @param expressir_version [String] The current Expressir version
|
30
|
+
def initialize(cache_version, expressir_version)
|
31
|
+
@cache_version = cache_version
|
32
|
+
@expressir_version = expressir_version
|
33
|
+
super("Cache version mismatch, cache version is #{cache_version}, Expressir version is #{expressir_version}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Base class for visitor-related errors
|
38
|
+
class VisitorError < ExpressError; end
|
39
|
+
|
40
|
+
# Error raised when visitor encounters invalid state
|
41
|
+
class VisitorInvalidStateError < VisitorError
|
42
|
+
# Initialize a new VisitorInvalidStateError
|
43
|
+
# @param message [String] Additional error information
|
44
|
+
def initialize(message = "Invalid state")
|
45
|
+
super("Visitor error: #{message}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Error raised when visitor encounters invalid input
|
50
|
+
class VisitorInvalidInputError < VisitorError
|
51
|
+
# Initialize a new VisitorInvalidInputError
|
52
|
+
# @param message [String] Additional error information
|
53
|
+
def initialize(message)
|
54
|
+
super("Invalid input error: #{message}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Error raised when a required method is missing
|
59
|
+
class FormatterMethodMissingError < ExpressError
|
60
|
+
# Initialize a new FormatterMethodMissingError
|
61
|
+
# @param formatter [String] Name of the formatter
|
62
|
+
# @param method [String] Name of the missing method
|
63
|
+
def initialize(formatter, method = nil)
|
64
|
+
method_msg = method ? " Missing method: #{method}" : " Missing required method."
|
65
|
+
super("Formatter error in #{formatter}.#{method_msg}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Error raised when reference resolution fails
|
70
|
+
class VisitorReferenceResolutionError < VisitorError
|
71
|
+
attr_reader :source, :reference
|
72
|
+
|
73
|
+
# Initialize a new VisitorReferenceResolutionError
|
74
|
+
# @param source [String] Source of the reference
|
75
|
+
# @param reference [String] The reference that failed to resolve
|
76
|
+
def initialize(source, reference)
|
77
|
+
@source = source
|
78
|
+
@reference = reference
|
79
|
+
super("Failed to resolve reference '#{reference}' from '#{source}'")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# For backward compatibility
|
85
|
+
CacheVersionMismatchError = Error::CacheVersionMismatchError
|
86
|
+
end
|
87
|
+
end
|
@@ -9,7 +9,7 @@ module Expressir
|
|
9
9
|
# @!visibility private
|
10
10
|
def self.included(mod)
|
11
11
|
if !mod.superclass.private_method_defined? :format_references_simple_reference
|
12
|
-
raise "
|
12
|
+
raise Error::FormatterMethodMissingError.new("HyperlinkFormatter", "format_references_simple_reference")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|