rubylens 0.1.0.pre.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/LICENSE.txt +21 -0
- data/README.md +67 -0
- data/assets/runtime/report.js +1409 -0
- data/assets/shells/report.html +34 -0
- data/assets/shells/showcase.html +26 -0
- data/assets/styles/report.css +85 -0
- data/assets/styles/showcase.css +15 -0
- data/exe/rubylens +6 -0
- data/lib/rubylens/art_model_builder.rb +94 -0
- data/lib/rubylens/cli.rb +104 -0
- data/lib/rubylens/errors.rb +6 -0
- data/lib/rubylens/generator.rb +56 -0
- data/lib/rubylens/git_repository.rb +106 -0
- data/lib/rubylens/index/manifest.rb +231 -0
- data/lib/rubylens/index/rubydex_adapter.rb +322 -0
- data/lib/rubylens/model/dependency_aggregation.rb +105 -0
- data/lib/rubylens/paths.rb +13 -0
- data/lib/rubylens/report_asset_assembler.rb +38 -0
- data/lib/rubylens/report_writer.rb +61 -0
- data/lib/rubylens/showcase_generator.rb +41 -0
- data/lib/rubylens/showcase_model.rb +41 -0
- data/lib/rubylens/showcase_writer.rb +27 -0
- data/lib/rubylens/version.rb +5 -0
- data/lib/rubylens.rb +33 -0
- metadata +99 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
|
|
5
|
+
module RubyLens
|
|
6
|
+
class ReportAssetAssembler
|
|
7
|
+
STYLES_PLACEHOLDER = "{{REPORT_STYLES}}"
|
|
8
|
+
RUNTIME_PLACEHOLDER = "{{REPORT_RUNTIME}}"
|
|
9
|
+
|
|
10
|
+
def initialize(
|
|
11
|
+
shell_path: File.expand_path("../../assets/shells/report.html", __dir__),
|
|
12
|
+
stylesheet_path: File.expand_path("../../assets/styles/report.css", __dir__),
|
|
13
|
+
runtime_path: File.expand_path("../../assets/runtime/report.js", __dir__)
|
|
14
|
+
)
|
|
15
|
+
@shell_path = shell_path
|
|
16
|
+
@stylesheet_path = stylesheet_path
|
|
17
|
+
@runtime_path = runtime_path
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def assemble
|
|
21
|
+
shell = File.read(@shell_path)
|
|
22
|
+
validate_placeholder(shell, STYLES_PLACEHOLDER)
|
|
23
|
+
validate_placeholder(shell, RUNTIME_PLACEHOLDER)
|
|
24
|
+
|
|
25
|
+
shell
|
|
26
|
+
.sub(STYLES_PLACEHOLDER, File.read(@stylesheet_path))
|
|
27
|
+
.sub(RUNTIME_PLACEHOLDER, File.read(@runtime_path))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def validate_placeholder(shell, placeholder)
|
|
33
|
+
return if shell.scan(placeholder).length == 1
|
|
34
|
+
|
|
35
|
+
raise Error, "report shell must contain exactly one #{placeholder} placeholder"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "json"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require_relative "report_asset_assembler"
|
|
8
|
+
|
|
9
|
+
module RubyLens
|
|
10
|
+
class ReportWriter
|
|
11
|
+
MODEL_PLACEHOLDER = "{{MODEL_BASE64}}"
|
|
12
|
+
|
|
13
|
+
def initialize(template_path: nil, asset_assembler: nil)
|
|
14
|
+
raise ArgumentError, "provide template_path or asset_assembler, not both" if template_path && asset_assembler
|
|
15
|
+
|
|
16
|
+
@template_path = template_path
|
|
17
|
+
@asset_assembler = asset_assembler || ReportAssetAssembler.new unless template_path
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write(model, output:)
|
|
21
|
+
output = File.expand_path(output)
|
|
22
|
+
directory = File.dirname(output)
|
|
23
|
+
FileUtils.mkdir_p(directory, mode: 0o700)
|
|
24
|
+
protect_default_directory(directory)
|
|
25
|
+
template = @template_path ? File.read(@template_path) : @asset_assembler.assemble
|
|
26
|
+
unless template.scan(MODEL_PLACEHOLDER).length == 1
|
|
27
|
+
raise Error, "report template must contain exactly one #{MODEL_PLACEHOLDER} placeholder"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
payload = Base64.strict_encode64(JSON.generate(model))
|
|
31
|
+
html = template.sub(MODEL_PLACEHOLDER, payload)
|
|
32
|
+
atomic_write(output, html)
|
|
33
|
+
output
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def rubylens_report?(path)
|
|
37
|
+
File.file?(path) && File.open(path, "rb") { |file| file.read(2048).include?('<meta name="generator" content="RubyLens">') }
|
|
38
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def protect_default_directory(directory)
|
|
45
|
+
return unless File.basename(directory) == ".rubylens"
|
|
46
|
+
|
|
47
|
+
ignore = File.join(directory, ".gitignore")
|
|
48
|
+
atomic_write(ignore, "*\n") unless File.exist?(ignore)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def atomic_write(path, contents)
|
|
52
|
+
temporary = File.join(File.dirname(path), ".#{File.basename(path)}.#{SecureRandom.hex(6)}.tmp")
|
|
53
|
+
File.open(temporary, File::WRONLY | File::CREAT | File::EXCL, 0o600) { |file| file.write(contents) }
|
|
54
|
+
File.chmod(0o600, temporary)
|
|
55
|
+
File.rename(temporary, path)
|
|
56
|
+
File.chmod(0o600, path)
|
|
57
|
+
ensure
|
|
58
|
+
FileUtils.rm_f(temporary) if temporary && File.exist?(temporary)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "generator"
|
|
4
|
+
require_relative "showcase_model"
|
|
5
|
+
require_relative "showcase_writer"
|
|
6
|
+
|
|
7
|
+
module RubyLens
|
|
8
|
+
class ShowcaseGenerator
|
|
9
|
+
DEFAULT_SHOWCASE_NAME = "rubylens-showcase.html"
|
|
10
|
+
|
|
11
|
+
def initialize(
|
|
12
|
+
manifest_builder: Index::Manifest,
|
|
13
|
+
adapter: Index::RubydexAdapter.new,
|
|
14
|
+
model_builder: ArtModelBuilder.new,
|
|
15
|
+
showcase_model: ShowcaseModel.new,
|
|
16
|
+
showcase_writer: ShowcaseWriter.new,
|
|
17
|
+
pipeline: nil
|
|
18
|
+
)
|
|
19
|
+
@pipeline = pipeline || GenerationPipeline.new(manifest_builder:, adapter:, model_builder:)
|
|
20
|
+
@showcase_model = showcase_model
|
|
21
|
+
@showcase_writer = showcase_writer
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(path: Dir.pwd, output: nil, lockfile: nil)
|
|
25
|
+
root = File.realpath(path)
|
|
26
|
+
if output.nil?
|
|
27
|
+
output = File.join(root, DEFAULT_SHOWCASE_NAME)
|
|
28
|
+
if File.exist?(output) && !@showcase_writer.rubylens_showcase?(output)
|
|
29
|
+
raise Error, "default showcase path already exists and is not a RubyLens showcase"
|
|
30
|
+
end
|
|
31
|
+
GitRepository.new(root).exclude_local(output, description: "showcase")
|
|
32
|
+
end
|
|
33
|
+
model, warnings = @pipeline.call(root:, lockfile:)
|
|
34
|
+
output_path = @showcase_writer.write(@showcase_model.call(model), output: output)
|
|
35
|
+
|
|
36
|
+
Result.new(output_path:, counts: model.fetch("totals").freeze, warnings:)
|
|
37
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => error
|
|
38
|
+
raise Error, error.message
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "art_model_builder"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module RubyLens
|
|
7
|
+
class ShowcaseModel
|
|
8
|
+
SIGNAL_FIELDS = ArtModelBuilder::SIGNAL_FIELDS
|
|
9
|
+
TOTAL_FIELDS = %w[namespaces packages dependencyStars renderedDependencyStars].freeze
|
|
10
|
+
CATEGORY_FIELDS = %w[core tests].freeze
|
|
11
|
+
|
|
12
|
+
def call(model)
|
|
13
|
+
{
|
|
14
|
+
"schema" => "rubylens.showcase.v1",
|
|
15
|
+
"projectName" => model.fetch("projectName"),
|
|
16
|
+
"totals" => project_hash(model.fetch("totals"), TOTAL_FIELDS),
|
|
17
|
+
"domains" => project_hash(model.fetch("domains"), SIGNAL_FIELDS),
|
|
18
|
+
"categoryStats" => CATEGORY_FIELDS.to_h do |category|
|
|
19
|
+
[category, numeric_row(model.fetch("categoryStats").fetch(category), 4)]
|
|
20
|
+
end,
|
|
21
|
+
"namespaces" => model.fetch("namespaces").map { |row| numeric_row(row, 15) },
|
|
22
|
+
"packages" => model.fetch("packages").map { |row| numeric_row(row, 8) },
|
|
23
|
+
"dependencyStars" => model.fetch("dependencyStars").map { |row| numeric_row(row, 8) },
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def project_hash(source, fields)
|
|
30
|
+
fields.to_h { |field| [field, Integer(source.fetch(field))] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def numeric_row(row, length)
|
|
34
|
+
raise Error, "showcase model row has an unexpected shape" unless row.length >= length
|
|
35
|
+
|
|
36
|
+
row.first(length).map { |value| Integer(value) }
|
|
37
|
+
rescue ArgumentError, TypeError
|
|
38
|
+
raise Error, "showcase model rows must contain only numbers"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "report_writer"
|
|
4
|
+
|
|
5
|
+
module RubyLens
|
|
6
|
+
class ShowcaseWriter < ReportWriter
|
|
7
|
+
MARKER = '<meta name="rubylens-artifact" content="showcase">'
|
|
8
|
+
SHELL_PATH = File.expand_path("../../assets/shells/showcase.html", __dir__)
|
|
9
|
+
STYLESHEET_PATH = File.expand_path("../../assets/styles/showcase.css", __dir__)
|
|
10
|
+
RUNTIME_PATH = File.expand_path("../../assets/runtime/report.js", __dir__)
|
|
11
|
+
|
|
12
|
+
def initialize(template_path: nil, asset_assembler: nil)
|
|
13
|
+
asset_assembler ||= ReportAssetAssembler.new(
|
|
14
|
+
shell_path: SHELL_PATH,
|
|
15
|
+
stylesheet_path: STYLESHEET_PATH,
|
|
16
|
+
runtime_path: RUNTIME_PATH,
|
|
17
|
+
) unless template_path
|
|
18
|
+
super(template_path:, asset_assembler:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def rubylens_showcase?(path)
|
|
22
|
+
File.file?(path) && File.open(path, "rb") { |file| file.read(2048).include?(MARKER) }
|
|
23
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/rubylens.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rubylens/version"
|
|
4
|
+
require_relative "rubylens/errors"
|
|
5
|
+
require_relative "rubylens/paths"
|
|
6
|
+
require_relative "rubylens/git_repository"
|
|
7
|
+
require_relative "rubylens/index/manifest"
|
|
8
|
+
require_relative "rubylens/index/rubydex_adapter"
|
|
9
|
+
require_relative "rubylens/art_model_builder"
|
|
10
|
+
require_relative "rubylens/report_asset_assembler"
|
|
11
|
+
require_relative "rubylens/report_writer"
|
|
12
|
+
require_relative "rubylens/generator"
|
|
13
|
+
require_relative "rubylens/showcase_model"
|
|
14
|
+
require_relative "rubylens/showcase_writer"
|
|
15
|
+
require_relative "rubylens/showcase_generator"
|
|
16
|
+
|
|
17
|
+
module RubyLens
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def generate_report(path: Dir.pwd, output: nil, lockfile: nil)
|
|
21
|
+
Generator.new.call(path: path, output: output, lockfile: lockfile)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_showcase(path: Dir.pwd, output: nil, lockfile: nil)
|
|
25
|
+
ShowcaseGenerator.new.call(path: path, output: output, lockfile: lockfile)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def generate(path: Dir.pwd, output: nil, lockfile: nil)
|
|
29
|
+
generate_report(path: path, output: output, lockfile: lockfile)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require_relative "rubylens/cli"
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rubylens
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0.pre.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- RubyLens contributors
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rubydex
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.2.9
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.2.9
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: base64
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.2'
|
|
40
|
+
description: |-
|
|
41
|
+
RubyLens uses Rubydex to index Ruby semantics and generate private, local-first
|
|
42
|
+
data for local interactive reports and autonomous visual showcases.
|
|
43
|
+
executables:
|
|
44
|
+
- rubylens
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- LICENSE.txt
|
|
49
|
+
- README.md
|
|
50
|
+
- assets/runtime/report.js
|
|
51
|
+
- assets/shells/report.html
|
|
52
|
+
- assets/shells/showcase.html
|
|
53
|
+
- assets/styles/report.css
|
|
54
|
+
- assets/styles/showcase.css
|
|
55
|
+
- exe/rubylens
|
|
56
|
+
- lib/rubylens.rb
|
|
57
|
+
- lib/rubylens/art_model_builder.rb
|
|
58
|
+
- lib/rubylens/cli.rb
|
|
59
|
+
- lib/rubylens/errors.rb
|
|
60
|
+
- lib/rubylens/generator.rb
|
|
61
|
+
- lib/rubylens/git_repository.rb
|
|
62
|
+
- lib/rubylens/index/manifest.rb
|
|
63
|
+
- lib/rubylens/index/rubydex_adapter.rb
|
|
64
|
+
- lib/rubylens/model/dependency_aggregation.rb
|
|
65
|
+
- lib/rubylens/paths.rb
|
|
66
|
+
- lib/rubylens/report_asset_assembler.rb
|
|
67
|
+
- lib/rubylens/report_writer.rb
|
|
68
|
+
- lib/rubylens/showcase_generator.rb
|
|
69
|
+
- lib/rubylens/showcase_model.rb
|
|
70
|
+
- lib/rubylens/showcase_writer.rb
|
|
71
|
+
- lib/rubylens/version.rb
|
|
72
|
+
homepage: https://st0012.dev/rails-galaxy/
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata:
|
|
76
|
+
allowed_push_host: https://rubygems.org
|
|
77
|
+
homepage_uri: https://st0012.dev/rails-galaxy/
|
|
78
|
+
rubygems_mfa_required: 'true'
|
|
79
|
+
rdoc_options: []
|
|
80
|
+
require_paths:
|
|
81
|
+
- lib
|
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: 3.2.0
|
|
87
|
+
- - "<"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 4.1.dev
|
|
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.10
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: Turn a Ruby codebase into self-contained stellar HTML
|
|
99
|
+
test_files: []
|