rubylens 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 +67 -39
- data/assets/runtime/report.js +1607 -251
- data/assets/shells/report.html +9 -7
- data/assets/shells/showcase.html +1 -0
- data/assets/styles/report.css +51 -25
- data/assets/styles/showcase.css +3 -0
- data/lib/rubylens/art_model_builder.rb +62 -10
- data/lib/rubylens/artifact_marker.rb +17 -0
- data/lib/rubylens/atomic_output.rb +27 -0
- data/lib/rubylens/cli.rb +53 -36
- data/lib/rubylens/clip/chrome_page.rb +183 -0
- data/lib/rubylens/clip/deadline_io.rb +28 -0
- data/lib/rubylens/clip/renderer.rb +113 -0
- data/lib/rubylens/clip/toolchain.rb +82 -0
- data/lib/rubylens/clip/web_socket_channel.rb +116 -0
- data/lib/rubylens/clip_generator.rb +102 -0
- data/lib/rubylens/default_output.rb +24 -0
- data/lib/rubylens/generator.rb +9 -10
- data/lib/rubylens/git_repository.rb +13 -7
- data/lib/rubylens/index/manifest.rb +8 -7
- data/lib/rubylens/index/rspec_extractor.rb +12 -10
- data/lib/rubylens/index/rubydex_adapter.rb +290 -140
- data/lib/rubylens/index/source_path.rb +12 -11
- data/lib/rubylens/model/dependency_aggregation.rb +9 -7
- data/lib/rubylens/morphology_classifier.rb +52 -12
- data/lib/rubylens/report_writer.rb +5 -20
- data/lib/rubylens/showcase_generator.rb +5 -9
- data/lib/rubylens/showcase_model.rb +12 -14
- data/lib/rubylens/showcase_writer.rb +0 -6
- data/lib/rubylens/version.rb +1 -1
- data/lib/rubylens.rb +7 -0
- metadata +10 -1
|
@@ -8,25 +8,27 @@ module RubyLens
|
|
|
8
8
|
def initialize(package_count:)
|
|
9
9
|
raise ArgumentError, "package_count must be nonnegative" if package_count.negative?
|
|
10
10
|
|
|
11
|
-
@counts = Array.new(package_count, 0)
|
|
12
11
|
@ruby_counts = Array.new(package_count) { Array.new(4, 0) }
|
|
13
12
|
@signal_maxima = Array.new(6, 0)
|
|
14
13
|
@rows = Array.new(package_count) { [] }
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def add(package_index:, row:, construct_index:)
|
|
18
|
-
@
|
|
17
|
+
local_index = @rows[package_index].length
|
|
19
18
|
@ruby_counts[package_index][construct_index] += 1 if construct_index
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
maxima = @signal_maxima
|
|
20
|
+
SIGNAL_COLUMNS.each do |column|
|
|
21
|
+
value = row[column]
|
|
22
|
+
index = column - 1
|
|
23
|
+
maxima[index] = value if value > maxima[index]
|
|
22
24
|
end
|
|
23
|
-
@rows[package_index] << row.dup.freeze
|
|
25
|
+
@rows[package_index] << (row.frozen? ? row : row.dup.freeze)
|
|
26
|
+
local_index
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def packages
|
|
27
|
-
@
|
|
30
|
+
@rows.each_index.map do |index|
|
|
28
31
|
{
|
|
29
|
-
declaration_count: @counts[index],
|
|
30
32
|
ruby_counts: @ruby_counts[index].dup.freeze,
|
|
31
33
|
declarations: @rows[index].dup.freeze,
|
|
32
34
|
}.freeze
|
|
@@ -16,6 +16,10 @@ module RubyLens
|
|
|
16
16
|
BAR_CONCENTRATION = 0.50
|
|
17
17
|
SPIRAL_STAGE_WIDTH = 0.10
|
|
18
18
|
SPIRAL_KNOB_RANGE = 0.35
|
|
19
|
+
LARGE_PACKAGE_ENRICHMENT_THRESHOLD = 10_000
|
|
20
|
+
LARGE_PACKAGE_ENRICHMENT_FAMILIES = [SPIRAL, BARRED_SPIRAL].freeze
|
|
21
|
+
UINT32_MASK = 0xffff_ffff
|
|
22
|
+
UINT32_RANGE = 0x1_0000_0000
|
|
19
23
|
|
|
20
24
|
DEFAULT_KNOBS = [0, 240, 3, 105, 380, 0, 0, 0, 0].freeze
|
|
21
25
|
|
|
@@ -32,7 +36,10 @@ module RubyLens
|
|
|
32
36
|
inputs = package_classification? ? package_classification_inputs : project_classification_inputs
|
|
33
37
|
return fallback(phase_seed: fallback_phase_seed) unless inputs
|
|
34
38
|
|
|
35
|
-
classify(**inputs)
|
|
39
|
+
classified = classify(**inputs)
|
|
40
|
+
return classified unless package_classification?
|
|
41
|
+
|
|
42
|
+
enrich_large_smooth_package(classified, size: inputs.fetch(:size), phase_seed: inputs.fetch(:phase_seed))
|
|
36
43
|
rescue KeyError, TypeError, ArgumentError
|
|
37
44
|
fallback(phase_seed: fallback_phase_seed)
|
|
38
45
|
end
|
|
@@ -55,10 +62,10 @@ module RubyLens
|
|
|
55
62
|
return if rows.empty?
|
|
56
63
|
return unless rows.each_with_index.all? { |row, index| valid_namespace?(row, names[index]) }
|
|
57
64
|
|
|
58
|
-
core_indexes = rows.each_index.reject { |index| rows[index][
|
|
65
|
+
core_indexes = rows.each_index.reject { |index| rows[index][1] == 1 }
|
|
59
66
|
core_count = core_indexes.length
|
|
60
67
|
test_count = rows.length - core_count
|
|
61
|
-
module_count = core_indexes.count { |index| rows[index][
|
|
68
|
+
module_count = core_indexes.count { |index| rows[index][0] == 1 }
|
|
62
69
|
dependency_count = packages.sum { |package| dependency_declaration_count(package) }
|
|
63
70
|
|
|
64
71
|
module_fraction = ratio(module_count, core_count)
|
|
@@ -77,10 +84,12 @@ module RubyLens
|
|
|
77
84
|
def package_classification_inputs
|
|
78
85
|
return unless @package.is_a?(Hash) && valid_phase_seed?(@phase_seed)
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
declarations = @package.fetch("declarations")
|
|
81
88
|
counts = @package.fetch("ruby_counts")
|
|
82
|
-
return unless
|
|
89
|
+
return unless declarations.is_a?(Array)
|
|
83
90
|
return unless counts.is_a?(Array) && counts.length == 4
|
|
91
|
+
|
|
92
|
+
size = declarations.length
|
|
84
93
|
return unless counts.all? { |count| count.is_a?(Integer) && count >= 0 }
|
|
85
94
|
return if counts.sum.zero?
|
|
86
95
|
|
|
@@ -105,18 +114,18 @@ module RubyLens
|
|
|
105
114
|
end
|
|
106
115
|
|
|
107
116
|
def valid_namespace?(row, name)
|
|
108
|
-
row.is_a?(Array) && row.length >=
|
|
109
|
-
[0, 1].include?(row[
|
|
117
|
+
row.is_a?(Array) && row.length >= 2 &&
|
|
118
|
+
[0, 1].include?(row[0]) && [0, 1, 2].include?(row[1]) &&
|
|
110
119
|
name.is_a?(String) && !name.empty?
|
|
111
120
|
end
|
|
112
121
|
|
|
113
122
|
def dependency_declaration_count(package)
|
|
114
123
|
raise TypeError unless package.is_a?(Hash)
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
raise TypeError unless
|
|
125
|
+
declarations = package.fetch("declarations")
|
|
126
|
+
raise TypeError unless declarations.is_a?(Array)
|
|
118
127
|
|
|
119
|
-
|
|
128
|
+
declarations.length
|
|
120
129
|
end
|
|
121
130
|
|
|
122
131
|
def ratio(numerator, denominator)
|
|
@@ -171,8 +180,31 @@ module RubyLens
|
|
|
171
180
|
|
|
172
181
|
family = concentration >= BAR_CONCENTRATION ? BARRED_SPIRAL : SPIRAL
|
|
173
182
|
stage = [[((structure - LENTICULAR_MAX) / SPIRAL_STAGE_WIDTH).floor, 0].max, 2].min
|
|
174
|
-
designation = family == BARRED_SPIRAL ? "SB#{%w[a b c][stage]}" : "S#{%w[a b c][stage]}"
|
|
175
183
|
progress = [[(structure - LENTICULAR_MAX) / SPIRAL_KNOB_RANGE, 0.0].max, 1.0].min
|
|
184
|
+
|
|
185
|
+
spiral_morphology(family:, stage:, progress:, phase_seed:)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def enrich_large_smooth_package(classified, size:, phase_seed:)
|
|
189
|
+
return classified if size < LARGE_PACKAGE_ENRICHMENT_THRESHOLD
|
|
190
|
+
return classified unless [ELLIPTICAL, LENTICULAR].include?(classified.fetch("family"))
|
|
191
|
+
|
|
192
|
+
family_count = LARGE_PACKAGE_ENRICHMENT_FAMILIES.length
|
|
193
|
+
family = LARGE_PACKAGE_ENRICHMENT_FAMILIES.fetch(phase_seed % family_count)
|
|
194
|
+
progress = seeded_unit(phase_seed / family_count, 91)
|
|
195
|
+
stage = [(progress * 3).floor, 2].min
|
|
196
|
+
|
|
197
|
+
spiral_morphology(
|
|
198
|
+
family:,
|
|
199
|
+
stage:,
|
|
200
|
+
progress:,
|
|
201
|
+
phase_seed:,
|
|
202
|
+
bar_length: 0.45 + 0.15 * progress,
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def spiral_morphology(family:, stage:, progress:, phase_seed:, bar_length: nil)
|
|
207
|
+
designation = family == BARRED_SPIRAL ? "SB#{%w[a b c][stage]}" : "S#{%w[a b c][stage]}"
|
|
176
208
|
arm_limit = family == BARRED_SPIRAL ? 2 : 4
|
|
177
209
|
morphology(
|
|
178
210
|
family,
|
|
@@ -181,11 +213,19 @@ module RubyLens
|
|
|
181
213
|
arm_count: 2 + (progress * arm_limit).round,
|
|
182
214
|
winding: scaled(0.18 - 0.115 * progress),
|
|
183
215
|
arm_fraction: scaled(0.45 + 0.11 * progress),
|
|
184
|
-
bar_length: family == BARRED_SPIRAL ? scaled(0.
|
|
216
|
+
bar_length: family == BARRED_SPIRAL ? scaled(bar_length || (0.50 - 0.22 * progress)) : 0,
|
|
185
217
|
phase_seed:,
|
|
186
218
|
)
|
|
187
219
|
end
|
|
188
220
|
|
|
221
|
+
def seeded_unit(seed, channel)
|
|
222
|
+
value = (seed ^ (channel * 0x9e37_79b9)) & UINT32_MASK
|
|
223
|
+
value = ((value ^ (value >> 16)) * 0x21f0_aaad) & UINT32_MASK
|
|
224
|
+
value = ((value ^ (value >> 15)) * 0x735a_2d97) & UINT32_MASK
|
|
225
|
+
value = (value ^ (value >> 15)) & UINT32_MASK
|
|
226
|
+
value.fdiv(UINT32_RANGE)
|
|
227
|
+
end
|
|
228
|
+
|
|
189
229
|
def morphology(family, designation, ellipticity: 0, bulge_share: 0, arm_count: 0, winding: 0,
|
|
190
230
|
arm_fraction: 0, bar_length: 0, clump_count: 0, clump_spread: 0, phase_seed: 0)
|
|
191
231
|
{
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
require "base64"
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require "json"
|
|
6
|
-
|
|
6
|
+
require_relative "artifact_marker"
|
|
7
|
+
require_relative "atomic_output"
|
|
7
8
|
require_relative "report_asset_assembler"
|
|
8
9
|
|
|
9
10
|
module RubyLens
|
|
10
11
|
class ReportWriter
|
|
11
12
|
MODEL_PLACEHOLDER = "{{MODEL_BASE64}}"
|
|
13
|
+
MARKER = '<meta name="generator" content="RubyLens">'
|
|
12
14
|
|
|
13
15
|
def initialize(asset_assembler: ReportAssetAssembler.new)
|
|
14
16
|
@asset_assembler = asset_assembler
|
|
@@ -26,14 +28,7 @@ module RubyLens
|
|
|
26
28
|
|
|
27
29
|
payload = Base64.strict_encode64(JSON.generate(model))
|
|
28
30
|
html = template.sub(MODEL_PLACEHOLDER, payload)
|
|
29
|
-
|
|
30
|
-
output
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def rubylens_report?(path)
|
|
34
|
-
File.file?(path) && File.open(path, "rb") { |file| file.read(2048).include?('<meta name="generator" content="RubyLens">') }
|
|
35
|
-
rescue Errno::ENOENT, Errno::EACCES
|
|
36
|
-
false
|
|
31
|
+
AtomicOutput.replace(output) { |temporary| File.binwrite(temporary, html) }
|
|
37
32
|
end
|
|
38
33
|
|
|
39
34
|
private
|
|
@@ -42,17 +37,7 @@ module RubyLens
|
|
|
42
37
|
return unless File.basename(directory) == ".rubylens"
|
|
43
38
|
|
|
44
39
|
ignore = File.join(directory, ".gitignore")
|
|
45
|
-
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def atomic_write(path, contents)
|
|
49
|
-
temporary = File.join(File.dirname(path), ".#{File.basename(path)}.#{SecureRandom.hex(6)}.tmp")
|
|
50
|
-
File.open(temporary, File::WRONLY | File::CREAT | File::EXCL, 0o600) { |file| file.write(contents) }
|
|
51
|
-
File.chmod(0o600, temporary)
|
|
52
|
-
File.rename(temporary, path)
|
|
53
|
-
File.chmod(0o600, path)
|
|
54
|
-
ensure
|
|
55
|
-
FileUtils.rm_f(temporary) if temporary && File.exist?(temporary)
|
|
40
|
+
AtomicOutput.replace(ignore) { |temporary| File.binwrite(temporary, "*\n") } unless File.exist?(ignore)
|
|
56
41
|
end
|
|
57
42
|
end
|
|
58
43
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "artifact_marker"
|
|
4
|
+
require_relative "default_output"
|
|
3
5
|
require_relative "generator"
|
|
4
6
|
require_relative "showcase_model"
|
|
5
7
|
require_relative "showcase_writer"
|
|
@@ -17,17 +19,11 @@ module RubyLens
|
|
|
17
19
|
|
|
18
20
|
def call
|
|
19
21
|
root = File.realpath(@path)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if output.nil?
|
|
23
|
-
output = File.join(root, DEFAULT_SHOWCASE_NAME)
|
|
24
|
-
if File.exist?(output) && !showcase_writer.rubylens_showcase?(output)
|
|
25
|
-
raise Error, "default showcase path already exists and is not a RubyLens showcase"
|
|
26
|
-
end
|
|
27
|
-
GitRepository.new(root).exclude_local(output, description: "showcase")
|
|
22
|
+
output = @output || DefaultOutput.resolve(root: root, name: DEFAULT_SHOWCASE_NAME, description: "showcase") do |existing|
|
|
23
|
+
ArtifactMarker.present?(existing, ShowcaseWriter::MARKER)
|
|
28
24
|
end
|
|
29
25
|
model, warnings = GenerationPipeline.new(root:, lockfile: @lockfile).call
|
|
30
|
-
output_path =
|
|
26
|
+
output_path = ShowcaseWriter.new.write(ShowcaseModel.new(model, details: @details).call, output: output)
|
|
31
27
|
|
|
32
28
|
Result.new(output_path:, counts: model.fetch("totals").freeze, warnings:)
|
|
33
29
|
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => error
|
|
@@ -6,7 +6,7 @@ require_relative "errors"
|
|
|
6
6
|
module RubyLens
|
|
7
7
|
class ShowcaseModel
|
|
8
8
|
SIGNAL_FIELDS = ArtModelBuilder::SIGNAL_FIELDS
|
|
9
|
-
TOTAL_FIELDS = %w[namespaces packages dependencyStars
|
|
9
|
+
TOTAL_FIELDS = %w[namespaces packages dependencyStars].freeze
|
|
10
10
|
CATEGORY_FIELDS = %w[core tests].freeze
|
|
11
11
|
ANNOTATION_LIMIT = 200
|
|
12
12
|
ANNOTATION_CATEGORIES = %w[core dependencies tests].freeze
|
|
@@ -14,7 +14,7 @@ module RubyLens
|
|
|
14
14
|
RUBY_NAME_PATTERN = /\A\p{Lu}[\p{L}\p{N}_]*(?:::\p{Lu}[\p{L}\p{N}_]*)*\z/
|
|
15
15
|
MAX_ANNOTATION_NAME_LENGTH = 160
|
|
16
16
|
RSPEC_PROXY_PREFIX = "RSpec example group #"
|
|
17
|
-
|
|
17
|
+
FALLBACK_MORPHOLOGY_ROW = [MorphologyClassifier::SPIRAL, *MorphologyClassifier::DEFAULT_KNOBS].freeze
|
|
18
18
|
|
|
19
19
|
def initialize(model, details: false)
|
|
20
20
|
@model = model
|
|
@@ -27,12 +27,13 @@ module RubyLens
|
|
|
27
27
|
raise Error, "package morphology rows must align with packages" unless package_morphologies.length == packages.length
|
|
28
28
|
|
|
29
29
|
showcase = {
|
|
30
|
-
"schema" => "rubylens.showcase.
|
|
30
|
+
"schema" => "rubylens.showcase.v7",
|
|
31
31
|
"projectName" => @model.fetch("projectName"),
|
|
32
32
|
"details" => @details,
|
|
33
33
|
"domains" => project_hash(@model.fetch("domains"), SIGNAL_FIELDS),
|
|
34
34
|
"morphology" => morphology_row,
|
|
35
|
-
"namespaces" => @model.fetch("namespaces").map { |row| numeric_row(row,
|
|
35
|
+
"namespaces" => @model.fetch("namespaces").map { |row| numeric_row(row, 14) },
|
|
36
|
+
"constantReferenceLinks" => @model.fetch("constantReferenceLinks", []).map { |row| numeric_row(row, 2) },
|
|
36
37
|
"packages" => packages.map { |row| numeric_row(row, 9) },
|
|
37
38
|
"packageMorphologies" => package_morphologies.map { |row| numeric_row(row, 10) },
|
|
38
39
|
"dependencySystems" => @model.fetch("dependencySystems", []).map { |row| numeric_row(row, 2) },
|
|
@@ -54,14 +55,11 @@ module RubyLens
|
|
|
54
55
|
|
|
55
56
|
def morphology_row
|
|
56
57
|
morphology = @model["morphology"]
|
|
57
|
-
return
|
|
58
|
+
return FALLBACK_MORPHOLOGY_ROW.dup unless morphology.is_a?(Array)
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
numeric_row([morphology.fetch("family"), *knobs], 10)
|
|
63
|
-
rescue KeyError, Error
|
|
64
|
-
LEGACY_MORPHOLOGY_ROW.dup
|
|
60
|
+
numeric_row(morphology, 10)
|
|
61
|
+
rescue Error
|
|
62
|
+
FALLBACK_MORPHOLOGY_ROW.dup
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
def project_hash(source, fields)
|
|
@@ -112,14 +110,14 @@ module RubyLens
|
|
|
112
110
|
rows = @model.fetch("namespaces")
|
|
113
111
|
names.each_with_index.filter_map do |name, index|
|
|
114
112
|
row = rows.fetch(index)
|
|
115
|
-
next unless (row.fetch(
|
|
113
|
+
next unless (row.fetch(2) == 1) == test
|
|
116
114
|
next if OMITTED_ANNOTATION_NAMES.include?(name)
|
|
117
115
|
next unless safe_ruby_name?(name)
|
|
118
116
|
|
|
119
|
-
[row.slice(
|
|
117
|
+
[row.slice(3, 6).sum, {
|
|
120
118
|
"category" => test ? "tests" : "core",
|
|
121
119
|
"name" => name,
|
|
122
|
-
"kind" => row.fetch(
|
|
120
|
+
"kind" => row.fetch(1) == 0 ? "Class" : "Module",
|
|
123
121
|
"anchor" => index,
|
|
124
122
|
}]
|
|
125
123
|
end.sort_by { |rank, candidate| [-rank, candidate.fetch("name"), candidate.fetch("anchor")] }
|
data/lib/rubylens/version.rb
CHANGED
data/lib/rubylens.rb
CHANGED
|
@@ -8,6 +8,8 @@ end
|
|
|
8
8
|
require_relative "rubylens/version"
|
|
9
9
|
require_relative "rubylens/errors"
|
|
10
10
|
require_relative "rubylens/paths"
|
|
11
|
+
require_relative "rubylens/atomic_output"
|
|
12
|
+
require_relative "rubylens/default_output"
|
|
11
13
|
require_relative "rubylens/git_repository"
|
|
12
14
|
require_relative "rubylens/dependency_warning"
|
|
13
15
|
require_relative "rubylens/index/manifest"
|
|
@@ -20,6 +22,7 @@ require_relative "rubylens/generator"
|
|
|
20
22
|
require_relative "rubylens/showcase_model"
|
|
21
23
|
require_relative "rubylens/showcase_writer"
|
|
22
24
|
require_relative "rubylens/showcase_generator"
|
|
25
|
+
require_relative "rubylens/clip_generator"
|
|
23
26
|
|
|
24
27
|
module RubyLens
|
|
25
28
|
module_function
|
|
@@ -31,4 +34,8 @@ module RubyLens
|
|
|
31
34
|
def generate_showcase(path: Dir.pwd, output: nil, lockfile: nil, details: false)
|
|
32
35
|
ShowcaseGenerator.new(path: path, output: output, lockfile: lockfile, details: details).call
|
|
33
36
|
end
|
|
37
|
+
|
|
38
|
+
def generate_clip(path: Dir.pwd, output: nil, lockfile: nil, details: false, progress: nil)
|
|
39
|
+
ClipGenerator.new(path: path, output: output, lockfile: lockfile, details: details, progress: progress).call
|
|
40
|
+
end
|
|
34
41
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubylens
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RubyLens contributors
|
|
@@ -55,7 +55,16 @@ files:
|
|
|
55
55
|
- exe/rubylens
|
|
56
56
|
- lib/rubylens.rb
|
|
57
57
|
- lib/rubylens/art_model_builder.rb
|
|
58
|
+
- lib/rubylens/artifact_marker.rb
|
|
59
|
+
- lib/rubylens/atomic_output.rb
|
|
58
60
|
- lib/rubylens/cli.rb
|
|
61
|
+
- lib/rubylens/clip/chrome_page.rb
|
|
62
|
+
- lib/rubylens/clip/deadline_io.rb
|
|
63
|
+
- lib/rubylens/clip/renderer.rb
|
|
64
|
+
- lib/rubylens/clip/toolchain.rb
|
|
65
|
+
- lib/rubylens/clip/web_socket_channel.rb
|
|
66
|
+
- lib/rubylens/clip_generator.rb
|
|
67
|
+
- lib/rubylens/default_output.rb
|
|
59
68
|
- lib/rubylens/dependency_warning.rb
|
|
60
69
|
- lib/rubylens/errors.rb
|
|
61
70
|
- lib/rubylens/generator.rb
|