ruby_llm-contract 0.10.5 → 1.0.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 +124 -5
- data/README.md +12 -2
- data/docs/guide/llm_judge.md +3 -3
- data/docs/guide/multimodal_input.md +4 -3
- data/docs/guide/output_schema.md +2 -2
- data/docs/guide/relation_to_tribunal.md +28 -0
- data/examples/README.md +1 -1
- data/lib/ruby_llm/contract/adapters/ruby_llm.rb +13 -9
- data/lib/ruby_llm/contract/concerns/context_helpers.rb +0 -2
- data/lib/ruby_llm/contract/concerns/deep_symbolize.rb +0 -1
- data/lib/ruby_llm/contract/contract/parser.rb +0 -3
- data/lib/ruby_llm/contract/contract/schema_validator/bound_rule.rb +0 -1
- data/lib/ruby_llm/contract/contract/schema_validator/enum_rule.rb +0 -1
- data/lib/ruby_llm/contract/contract/schema_validator/node.rb +0 -4
- data/lib/ruby_llm/contract/contract/schema_validator/scalar_rules.rb +0 -1
- data/lib/ruby_llm/contract/contract/schema_validator/type_rule.rb +0 -1
- data/lib/ruby_llm/contract/cost_calculator.rb +27 -2
- data/lib/ruby_llm/contract/eval/baseline_diff.rb +4 -2
- data/lib/ruby_llm/contract/eval/candidate_label.rb +31 -0
- data/lib/ruby_llm/contract/eval/case_executor.rb +2 -2
- data/lib/ruby_llm/contract/eval/case_result.rb +8 -0
- data/lib/ruby_llm/contract/eval/contract_detail_builder.rb +0 -2
- data/lib/ruby_llm/contract/eval/model_comparison.rb +3 -2
- data/lib/ruby_llm/contract/eval/pipeline_result_adapter.rb +1 -2
- data/lib/ruby_llm/contract/eval/prompt_diff_comparator.rb +0 -1
- data/lib/ruby_llm/contract/eval/prompt_diff_presenter.rb +0 -1
- data/lib/ruby_llm/contract/eval/prompt_diff_serializer.rb +0 -1
- data/lib/ruby_llm/contract/eval/report_presenter.rb +0 -1
- data/lib/ruby_llm/contract/eval/report_stats.rb +0 -1
- data/lib/ruby_llm/contract/eval/report_storage.rb +0 -1
- data/lib/ruby_llm/contract/eval/retry_optimizer.rb +10 -20
- data/lib/ruby_llm/contract/eval/trait_evaluator.rb +0 -2
- data/lib/ruby_llm/contract/eval.rb +1 -0
- data/lib/ruby_llm/contract/pipeline/runner.rb +0 -1
- data/lib/ruby_llm/contract/rake_task/suite_gate.rb +0 -12
- data/lib/ruby_llm/contract/rake_task.rb +1 -2
- data/lib/ruby_llm/contract/step/base.rb +12 -3
- data/lib/ruby_llm/contract/step/dsl.rb +2 -4
- data/lib/ruby_llm/contract/step/limit_checker.rb +0 -2
- data/lib/ruby_llm/contract/step/retry_executor.rb +0 -2
- data/lib/ruby_llm/contract/token_estimator.rb +0 -2
- data/lib/ruby_llm/contract/version.rb +1 -1
- data/lib/ruby_llm/contract.rb +0 -4
- data/ruby_llm-contract.gemspec +6 -5
- metadata +7 -11
- data/.rubocop.yml +0 -58
- data/Gemfile +0 -13
- data/Gemfile.lock +0 -278
- data/Rakefile +0 -8
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Contract
|
|
5
5
|
module Eval
|
|
6
|
-
#
|
|
7
|
-
# Replaces OpenStruct usage in Runner#normalize_pipeline_result.
|
|
6
|
+
# Replaces OpenStruct usage in StepResultNormalizer#normalize_pipeline_result.
|
|
8
7
|
PipelineResultAdapter = Struct.new(:status, :ok_flag, :parsed_output, :validation_errors, :trace) do
|
|
9
8
|
def ok?
|
|
10
9
|
ok_flag
|
|
@@ -13,6 +13,10 @@ module RubyLLM
|
|
|
13
13
|
# result.print_summary
|
|
14
14
|
# result.to_dsl # => copy-paste retry_policy
|
|
15
15
|
class RetryOptimizer
|
|
16
|
+
# Documented CANDIDATES= shorthand, parsed by
|
|
17
|
+
# OptimizeRakeTask#parse_candidates and rendered in this table's headers.
|
|
18
|
+
EFFORT_SEPARATOR = "@"
|
|
19
|
+
|
|
16
20
|
Result = Struct.new(:step_name, :eval_names, :candidate_labels, :score_matrix,
|
|
17
21
|
:constraining_eval, :chain, :chain_details, keyword_init: true) do
|
|
18
22
|
# Terminology alias — `hardest_eval` is the narrative name used in docs;
|
|
@@ -80,11 +84,10 @@ module RubyLLM
|
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
def short_candidate_label(label)
|
|
83
|
-
label
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
.sub(")", "")
|
|
87
|
+
config = CandidateLabel.parse(label)
|
|
88
|
+
model = config[:model].sub("gpt-5-", "").sub("gpt-4.1", "4.1")
|
|
89
|
+
effort = config[:reasoning_effort]
|
|
90
|
+
effort ? "#{model}#{EFFORT_SEPARATOR}#{effort}" : model
|
|
88
91
|
end
|
|
89
92
|
|
|
90
93
|
def print_dsl(io)
|
|
@@ -176,11 +179,9 @@ module RubyLLM
|
|
|
176
179
|
def build_chain(matrix, labels, evals)
|
|
177
180
|
total = evals.size
|
|
178
181
|
|
|
179
|
-
# Find cheapest model that passes every eval — the safe fallback.
|
|
180
182
|
safe_fallback = labels.find { |l| evals.all? { |e| (matrix.dig(e, l) || 0) >= @min_score } }
|
|
181
183
|
return [[], []] unless safe_fallback
|
|
182
184
|
|
|
183
|
-
# Prepend cheaper models that pass a strict subset.
|
|
184
185
|
chain = []
|
|
185
186
|
details = []
|
|
186
187
|
covered_evals = Set.new
|
|
@@ -193,27 +194,16 @@ module RubyLLM
|
|
|
193
194
|
next if new_additions.empty?
|
|
194
195
|
|
|
195
196
|
covered_evals.merge(new_additions)
|
|
196
|
-
chain <<
|
|
197
|
+
chain << CandidateLabel.parse(label)
|
|
197
198
|
details << { label: label, passes: new_additions.size, cost: label }
|
|
198
199
|
end
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
chain << parse_label_to_config(safe_fallback)
|
|
201
|
+
chain << CandidateLabel.parse(safe_fallback)
|
|
202
202
|
details << { label: safe_fallback, passes: total, cost: safe_fallback }
|
|
203
203
|
|
|
204
204
|
[chain, details]
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
-
def parse_label_to_config(label)
|
|
208
|
-
if label.match?(/\(effort: (\w+)\)/)
|
|
209
|
-
model = label.sub(/\s*\(effort:.*/, "").strip
|
|
210
|
-
effort = label.match(/\(effort: (\w+)\)/)[1]
|
|
211
|
-
{ model: model, reasoning_effort: effort }
|
|
212
|
-
else
|
|
213
|
-
{ model: label }
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
207
|
def empty_result(evals)
|
|
218
208
|
Result.new(
|
|
219
209
|
step_name: @step.name || @step.to_s,
|
|
@@ -23,6 +23,7 @@ require_relative "eval/report_storage"
|
|
|
23
23
|
require_relative "eval/report"
|
|
24
24
|
require_relative "eval/aggregated_report"
|
|
25
25
|
require_relative "eval/eval_definition"
|
|
26
|
+
require_relative "eval/candidate_label"
|
|
26
27
|
require_relative "eval/model_comparison"
|
|
27
28
|
require_relative "eval/baseline_diff"
|
|
28
29
|
require_relative "eval/prompt_diff_serializer"
|
|
@@ -95,7 +95,6 @@ module RubyLLM
|
|
|
95
95
|
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
# Encapsulates mutable state during pipeline execution
|
|
99
98
|
class ExecutionState
|
|
100
99
|
attr_reader :trace_id, :step_results, :step_traces, :outputs_by_step,
|
|
101
100
|
:current_input, :status, :failed_step
|
|
@@ -3,18 +3,6 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Contract
|
|
5
5
|
class RakeTask < ::Rake::TaskLib
|
|
6
|
-
# Encapsulates the pass/fail gate that runs after `RakeTask#define_task`
|
|
7
|
-
# has collected eval reports. Extracted from the prior `define_task`
|
|
8
|
-
# god-method so each gating dimension (cost, score, regression) is
|
|
9
|
-
# testable in isolation.
|
|
10
|
-
#
|
|
11
|
-
# Returns a `Verdict` value object with:
|
|
12
|
-
# - `passed?` — overall gate verdict
|
|
13
|
-
# - `abort_reason` — String for `abort` when `passed? == false`, nil otherwise
|
|
14
|
-
# - `passed_reports` — [[host, report], ...] of reports that individually passed
|
|
15
|
-
# (used to decide which baselines to save)
|
|
16
|
-
# - `suite_cost` — total cost across all reports
|
|
17
|
-
#
|
|
18
6
|
# Gate ordering (preserved from pre-refactor behaviour):
|
|
19
7
|
# 1. cost gate runs FIRST — if `maximum_cost` set and exceeded, the
|
|
20
8
|
# suite aborts before any score check; passed_reports is empty.
|
|
@@ -164,7 +164,7 @@ module RubyLLM
|
|
|
164
164
|
Array(JSON.parse(raw))
|
|
165
165
|
else
|
|
166
166
|
raw.split(",").map(&:strip).reject(&:empty?).map do |entry|
|
|
167
|
-
model, effort = entry.split(
|
|
167
|
+
model, effort = entry.split(Eval::RetryOptimizer::EFFORT_SEPARATOR, 2)
|
|
168
168
|
config = { model: model.strip }
|
|
169
169
|
config[:reasoning_effort] = effort.strip if effort && !effort.empty?
|
|
170
170
|
config
|
|
@@ -191,7 +191,6 @@ module RubyLLM
|
|
|
191
191
|
end
|
|
192
192
|
end
|
|
193
193
|
|
|
194
|
-
# Auto-register the optimize task when this file is loaded
|
|
195
194
|
OptimizeRakeTask.new
|
|
196
195
|
end
|
|
197
196
|
end
|
|
@@ -6,6 +6,11 @@ module RubyLLM
|
|
|
6
6
|
class Base
|
|
7
7
|
DEFAULT_OUTPUT_TOKENS = 256
|
|
8
8
|
|
|
9
|
+
# Eval::CaseExecutor substring-matches this to choose skip over raise.
|
|
10
|
+
# Reworded without the consumer, every skipped eval case becomes a hard
|
|
11
|
+
# failure of the whole run, so both ends read it from here.
|
|
12
|
+
NO_ADAPTER_MESSAGE = "No adapter configured"
|
|
13
|
+
|
|
9
14
|
def self.inherited(subclass)
|
|
10
15
|
super
|
|
11
16
|
Contract.register_eval_host(subclass) if respond_to?(:eval_defined?) && eval_defined?
|
|
@@ -141,7 +146,10 @@ module RubyLLM
|
|
|
141
146
|
def estimate_eval_cost_for_model(cases, model_name)
|
|
142
147
|
cases.sum do |test_case|
|
|
143
148
|
estimate = estimate_cost(input: test_case.input, model: model_name)
|
|
144
|
-
|
|
149
|
+
# Two misses floor to 0.0, not one: model absent from the registry
|
|
150
|
+
# (nil estimate), or present with unreadable pricing (hash whose
|
|
151
|
+
# estimated_cost is nil). Documented as a floor, not a fail-closed.
|
|
152
|
+
(estimate && estimate[:estimated_cost]) || 0.0
|
|
145
153
|
end.round(6)
|
|
146
154
|
end
|
|
147
155
|
|
|
@@ -225,8 +233,9 @@ module RubyLLM
|
|
|
225
233
|
adapter = context[:adapter] || RubyLLM::Contract.configuration.default_adapter
|
|
226
234
|
return adapter if adapter
|
|
227
235
|
|
|
228
|
-
raise RubyLLM::Contract::Error,
|
|
229
|
-
|
|
236
|
+
raise RubyLLM::Contract::Error,
|
|
237
|
+
"#{NO_ADAPTER_MESSAGE}. Set one with RubyLLM::Contract.configure " \
|
|
238
|
+
"{ |c| c.default_adapter = ... } or pass context: { adapter: ... }"
|
|
230
239
|
end
|
|
231
240
|
|
|
232
241
|
# ADR-0021 deliverable 2: narrow ArgumentError rescue to DSL-setup phase only.
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Contract
|
|
5
5
|
module Step
|
|
6
|
-
# Extracted from Base to reduce class length.
|
|
7
|
-
# DSL accessor methods for step definition (input_type, output_type, prompt, etc.).
|
|
8
6
|
module Dsl # rubocop:disable Metrics/ModuleLength
|
|
9
7
|
# Sentinel signalling "explicitly reset" (`some_attr(:default)`).
|
|
10
8
|
# Distinguishes reset (lookup stops at this class, returns nil) from
|
|
@@ -65,8 +63,8 @@ module RubyLLM
|
|
|
65
63
|
|
|
66
64
|
def output_schema(&block)
|
|
67
65
|
if block
|
|
68
|
-
require "
|
|
69
|
-
@output_schema = ::
|
|
66
|
+
require "schematist"
|
|
67
|
+
@output_schema = ::Schematist::Schema.create(&block)
|
|
70
68
|
elsif defined?(@output_schema)
|
|
71
69
|
@output_schema
|
|
72
70
|
elsif superclass.respond_to?(:output_schema)
|
data/lib/ruby_llm/contract.rb
CHANGED
|
@@ -21,8 +21,6 @@ module RubyLLM
|
|
|
21
21
|
step_adapter_overrides.clear
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# --- Eval host registry ---
|
|
25
|
-
|
|
26
24
|
def register_eval_host(klass)
|
|
27
25
|
eval_hosts << klass unless eval_hosts.include?(klass)
|
|
28
26
|
end
|
|
@@ -101,9 +99,7 @@ module RubyLLM
|
|
|
101
99
|
|
|
102
100
|
private
|
|
103
101
|
|
|
104
|
-
# Filter stale hosts, deduplicate by name (last wins), prune registry in-place
|
|
105
102
|
def live_eval_hosts
|
|
106
|
-
# Remove hosts without evals
|
|
107
103
|
@eval_hosts&.reject! { |h| !h.respond_to?(:eval_defined?) || !h.eval_defined? }
|
|
108
104
|
|
|
109
105
|
# Deduplicate: if two classes share a name (reload), keep the latest
|
data/ruby_llm-contract.gemspec
CHANGED
|
@@ -16,7 +16,6 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.license = "MIT"
|
|
17
17
|
spec.required_ruby_version = ">= 3.2.0"
|
|
18
18
|
|
|
19
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
19
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
20
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
22
21
|
spec.metadata["documentation_uri"] = "#{spec.homepage}#readme"
|
|
@@ -25,16 +24,18 @@ Gem::Specification.new do |spec|
|
|
|
25
24
|
spec.files = Dir.chdir(__dir__) do
|
|
26
25
|
# Internal trackers + dev configs excluded so the published gem
|
|
27
26
|
# contains only what adopters actually need at runtime.
|
|
28
|
-
excluded_files = %w[
|
|
27
|
+
excluded_files = %w[.rspec .simplecov .rubocop.yml .rubocop_todo.yml Gemfile Rakefile]
|
|
29
28
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
30
29
|
(File.expand_path(f) == __FILE__) ||
|
|
31
|
-
f.start_with?("spec/", "docs/ideas/", "doc/", ".ai/", ".claude/", ".git",
|
|
30
|
+
f.start_with?("spec/", "docs/ideas/", "doc/", ".ai/", ".claude/", ".git",
|
|
31
|
+
".revive/", "gemfiles/") ||
|
|
32
32
|
excluded_files.include?(f)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
spec.require_paths = ["lib"]
|
|
36
36
|
|
|
37
37
|
spec.add_dependency "dry-types", "~> 1.7"
|
|
38
|
-
spec.add_dependency "ruby_llm", "~>
|
|
39
|
-
|
|
38
|
+
spec.add_dependency "ruby_llm", "~> 2.0"
|
|
39
|
+
# schematist is what ruby_llm-schema became; ruby_llm 2.x depends on it too.
|
|
40
|
+
spec.add_dependency "schematist", "~> 1.1"
|
|
40
41
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-contract
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justyna
|
|
@@ -29,28 +29,28 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '2.0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '2.0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: schematist
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '1.1'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '1.1'
|
|
54
54
|
description: Wraps RubyLLM::Chat with input/output contracts, business-rule validation,
|
|
55
55
|
retry with model escalation on validation failure, pre-flight cost ceilings, and
|
|
56
56
|
an evaluation framework. Sibling abstraction to RubyLLM::Agent — same niche (reusable
|
|
@@ -59,13 +59,9 @@ executables: []
|
|
|
59
59
|
extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
|
61
61
|
files:
|
|
62
|
-
- ".rubocop.yml"
|
|
63
62
|
- CHANGELOG.md
|
|
64
|
-
- Gemfile
|
|
65
|
-
- Gemfile.lock
|
|
66
63
|
- LICENSE
|
|
67
64
|
- README.md
|
|
68
|
-
- Rakefile
|
|
69
65
|
- docs/architecture.md
|
|
70
66
|
- docs/guide/best_practices.md
|
|
71
67
|
- docs/guide/eval_first.md
|
|
@@ -124,6 +120,7 @@ files:
|
|
|
124
120
|
- lib/ruby_llm/contract/eval.rb
|
|
125
121
|
- lib/ruby_llm/contract/eval/aggregated_report.rb
|
|
126
122
|
- lib/ruby_llm/contract/eval/baseline_diff.rb
|
|
123
|
+
- lib/ruby_llm/contract/eval/candidate_label.rb
|
|
127
124
|
- lib/ruby_llm/contract/eval/case_executor.rb
|
|
128
125
|
- lib/ruby_llm/contract/eval/case_result.rb
|
|
129
126
|
- lib/ruby_llm/contract/eval/case_result_builder.rb
|
|
@@ -200,7 +197,6 @@ homepage: https://github.com/justi/ruby_llm-contract
|
|
|
200
197
|
licenses:
|
|
201
198
|
- MIT
|
|
202
199
|
metadata:
|
|
203
|
-
homepage_uri: https://github.com/justi/ruby_llm-contract
|
|
204
200
|
source_code_uri: https://github.com/justi/ruby_llm-contract
|
|
205
201
|
changelog_uri: https://github.com/justi/ruby_llm-contract/blob/main/CHANGELOG.md
|
|
206
202
|
documentation_uri: https://github.com/justi/ruby_llm-contract#readme
|
data/.rubocop.yml
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 3.2
|
|
3
|
-
NewCops: enable
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
|
|
6
|
-
Style/Documentation:
|
|
7
|
-
Enabled: false
|
|
8
|
-
|
|
9
|
-
Style/StringLiterals:
|
|
10
|
-
EnforcedStyle: double_quotes
|
|
11
|
-
|
|
12
|
-
Style/StringLiteralsInInterpolation:
|
|
13
|
-
EnforcedStyle: double_quotes
|
|
14
|
-
|
|
15
|
-
Metrics/BlockLength:
|
|
16
|
-
Exclude:
|
|
17
|
-
- 'spec/**/*'
|
|
18
|
-
- '*.gemspec'
|
|
19
|
-
|
|
20
|
-
Metrics/MethodLength:
|
|
21
|
-
Max: 25
|
|
22
|
-
|
|
23
|
-
Layout/LineLength:
|
|
24
|
-
Max: 120
|
|
25
|
-
|
|
26
|
-
Style/OneClassPerFile:
|
|
27
|
-
Exclude:
|
|
28
|
-
- 'spec/**/*'
|
|
29
|
-
- 'examples/**/*'
|
|
30
|
-
|
|
31
|
-
Lint/UnusedBlockArgument:
|
|
32
|
-
Exclude:
|
|
33
|
-
- 'spec/**/*'
|
|
34
|
-
|
|
35
|
-
Naming/VariableNumber:
|
|
36
|
-
Exclude:
|
|
37
|
-
- 'spec/**/*'
|
|
38
|
-
- 'examples/**/*'
|
|
39
|
-
|
|
40
|
-
AllCops:
|
|
41
|
-
Exclude:
|
|
42
|
-
- 'internal/**/*'
|
|
43
|
-
|
|
44
|
-
Metrics/ClassLength:
|
|
45
|
-
Max: 140
|
|
46
|
-
|
|
47
|
-
Metrics/ModuleLength:
|
|
48
|
-
Max: 150
|
|
49
|
-
|
|
50
|
-
Metrics/AbcSize:
|
|
51
|
-
Max: 30
|
|
52
|
-
|
|
53
|
-
Metrics/ParameterLists:
|
|
54
|
-
Max: 12
|
|
55
|
-
MaxOptionalParameters: 10
|
|
56
|
-
|
|
57
|
-
Style/FormatStringToken:
|
|
58
|
-
Enabled: false
|
data/Gemfile
DELETED