rubylens 0.1.0 → 0.2.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 +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 +214 -72
- 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/constant_reference_collector.rb +150 -0
- data/lib/rubylens/index/declaration_collector.rb +288 -0
- data/lib/rubylens/index/git_package_source.rb +202 -0
- data/lib/rubylens/index/location_index.rb +103 -0
- data/lib/rubylens/index/manifest.rb +62 -157
- data/lib/rubylens/index/rspec_extractor.rb +12 -10
- data/lib/rubylens/index/rubydex_adapter.rb +111 -301
- data/lib/rubylens/index/source_path.rb +12 -11
- data/lib/rubylens/model/dependency_aggregation.rb +11 -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 +16 -3
|
@@ -2,60 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
require "rubydex"
|
|
4
4
|
require "set"
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "constant_reference_collector"
|
|
6
|
+
require_relative "declaration_collector"
|
|
7
|
+
require_relative "location_index"
|
|
6
8
|
require_relative "rspec_extractor"
|
|
7
|
-
require_relative "source_path"
|
|
8
9
|
|
|
9
10
|
module RubyLens
|
|
10
11
|
module Index
|
|
12
|
+
# Drives one Rubydex indexing run over the manifest and assembles the
|
|
13
|
+
# snapshot payload from what the collectors return.
|
|
14
|
+
#
|
|
15
|
+
# The snapshot is the privacy boundary: every namespace and dependency star
|
|
16
|
+
# leaves here as a row of integers, and the only strings that survive are
|
|
17
|
+
# namespace names, package names, and the project name.
|
|
11
18
|
class RubydexAdapter
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Everything the collectors need from one declaration's definitions, gathered in a
|
|
15
|
-
# single pass so each definition's location crosses the Rubydex boundary once.
|
|
16
|
-
DefinitionSummary = Data.define(
|
|
17
|
-
:workspace_count,
|
|
18
|
-
:workspace_relatives,
|
|
19
|
-
:canonical_relatives,
|
|
20
|
-
:canonical_site_keys,
|
|
21
|
-
:package_site_keys,
|
|
22
|
-
)
|
|
19
|
+
SCHEMA = "rubylens.snapshot.v9"
|
|
20
|
+
RSPEC_ROW = [0, 1, *Array.new(11, 0)].freeze
|
|
23
21
|
|
|
22
|
+
# The manifest is duck-typed: production passes a Manifest, tests pass
|
|
23
|
+
# stubs, and `index` probes it with respond_to? for the optional parts.
|
|
24
|
+
#
|
|
25
|
+
#: (untyped manifest) -> void
|
|
24
26
|
def initialize(manifest)
|
|
25
27
|
@manifest = manifest
|
|
26
|
-
@
|
|
27
|
-
@workspace_location_cache = {}
|
|
28
|
-
@indexed_package_document_paths = Set.new
|
|
28
|
+
@locations = LocationIndex.new(manifest)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
#: () -> Hash[String, untyped]
|
|
31
32
|
def index
|
|
32
33
|
graph = Rubydex::Graph.new(workspace_path: @manifest.root.to_s)
|
|
33
34
|
index_errors = graph.index_all(@manifest.files)
|
|
34
|
-
|
|
35
|
+
# Documents resolve before `resolve` so package attribution sees exactly
|
|
36
|
+
# the documents Rubydex accepted, not the paths the manifest offered.
|
|
37
|
+
documents_with_paths = @locations.resolve_documents(graph)
|
|
35
38
|
graph.resolve
|
|
36
39
|
integrity_failures = graph.check_integrity
|
|
37
|
-
|
|
40
|
+
|
|
41
|
+
collected = DeclarationCollector.new(manifest: @manifest, locations: @locations).call(graph.declarations)
|
|
38
42
|
rspec = RSpecExtractor.new(
|
|
39
43
|
graph: graph,
|
|
40
44
|
manifest: @manifest,
|
|
41
|
-
package_document_paths: @
|
|
45
|
+
package_document_paths: @locations.package_document_paths,
|
|
46
|
+
documents_with_paths: documents_with_paths,
|
|
42
47
|
).call
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
|
|
49
|
+
namespaces = collected.namespaces
|
|
50
|
+
namespace_names = namespaces.map(&:name) + rspec.groups
|
|
51
|
+
references = ConstantReferenceCollector.new(locations: @locations).call(
|
|
52
|
+
graph: graph,
|
|
53
|
+
namespaces: namespaces,
|
|
54
|
+
definition_ranges: collected.definition_ranges,
|
|
55
|
+
ordinal_by_name: ordinal_by_name(namespaces),
|
|
56
|
+
dependency_ordinal_by_name: collected.dependency_ordinal_by_name,
|
|
57
|
+
namespace_count: namespace_names.length,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
category_stats = collected.category_stats
|
|
46
61
|
category_stats.fetch("tests")[0] += rspec.groups.length
|
|
47
62
|
category_stats.fetch("tests")[2] += rspec.method_count
|
|
48
63
|
|
|
49
64
|
{
|
|
50
|
-
"schema" =>
|
|
65
|
+
"schema" => SCHEMA,
|
|
51
66
|
"project_name" => project_name,
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
67
|
+
"namespace_names" => namespace_names,
|
|
68
|
+
"namespaces" => namespace_rows(namespaces, references.inbound_counts, rspec.groups.length),
|
|
69
|
+
"constant_reference_links" => references.links,
|
|
55
70
|
"category_stats" => category_stats,
|
|
56
|
-
"dependency_signal_maxima" => collected.
|
|
57
|
-
"packages" =>
|
|
58
|
-
"dependency_systems" =>
|
|
71
|
+
"dependency_signal_maxima" => collected.dependency_signal_maxima,
|
|
72
|
+
"packages" => package_rows(collected.dependency_packages),
|
|
73
|
+
"dependency_systems" => dependency_system_rows,
|
|
59
74
|
"dependency_warnings" => @manifest.respond_to?(:dependency_warnings) ? @manifest.dependency_warnings : [],
|
|
60
75
|
"warning_counts" => {
|
|
61
76
|
"manifest" => @manifest.warnings.length,
|
|
@@ -67,130 +82,100 @@ module RubyLens
|
|
|
67
82
|
|
|
68
83
|
private
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
end
|
|
87
|
-
collect_category_stat(category_stats, declaration, summary)
|
|
88
|
-
collect_dependency_declaration(aggregation, declaration, summary)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
{ workspace_records: records, category_stats:, dependency_aggregation: aggregation }
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def summarize_definitions(declaration)
|
|
95
|
-
namespace = namespace?(declaration)
|
|
96
|
-
workspace_count = 0
|
|
97
|
-
workspace_relatives = []
|
|
98
|
-
canonical_relatives = []
|
|
99
|
-
canonical_site_keys = []
|
|
100
|
-
package_site_keys = {}
|
|
101
|
-
|
|
102
|
-
declaration.definitions.each do |definition|
|
|
103
|
-
location = definition.location
|
|
104
|
-
if workspace_location?(location)
|
|
105
|
-
workspace_count += 1
|
|
106
|
-
relative = @manifest.relative_workspace_path(source_path(location.uri))
|
|
107
|
-
workspace_relatives << relative if relative
|
|
108
|
-
if namespace && canonical_namespace_definition?(declaration, definition)
|
|
109
|
-
canonical_site_keys << site_key(location)
|
|
110
|
-
canonical_relatives << relative if relative
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
package_index = package_index_for_location(location)
|
|
114
|
-
(package_site_keys[package_index] ||= []) << site_key(location) if package_index
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
DefinitionSummary.new(
|
|
118
|
-
workspace_count:,
|
|
119
|
-
workspace_relatives:,
|
|
120
|
-
canonical_relatives:,
|
|
121
|
-
canonical_site_keys:,
|
|
122
|
-
package_site_keys:,
|
|
123
|
-
)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def workspace_namespaces(records, rspec_groups)
|
|
127
|
-
ordinal_by_name = records.each_with_index.to_h { |(declaration, *), index| [declaration.name, index] }
|
|
128
|
-
components = records.map { |_declaration, _sites, _scope, component| component }
|
|
129
|
-
all_components = components + rspec_groups.map(&:component)
|
|
130
|
-
component_ids = all_components.uniq.sort.each_with_index.to_h
|
|
131
|
-
|
|
132
|
-
{
|
|
133
|
-
records: records,
|
|
134
|
-
rspec_groups: rspec_groups,
|
|
135
|
-
namespace_names: records.map { |declaration, *| declaration.name } + rspec_groups.map(&:name),
|
|
136
|
-
ordinal_by_name: ordinal_by_name,
|
|
137
|
-
component_ids: component_ids,
|
|
138
|
-
components: components,
|
|
139
|
-
component_counts: all_components.tally.sort_by { |name, _count| component_ids.fetch(name) }.map(&:last),
|
|
140
|
-
}
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def build_workspace_rows(workspace, inbound_references)
|
|
144
|
-
rows = workspace.fetch(:records).each_with_index.map do |(declaration, sites, scope, component), index|
|
|
85
|
+
#: (Array[DeclarationCollector::Namespace]) -> Hash[String, Integer]
|
|
86
|
+
def ordinal_by_name(namespaces)
|
|
87
|
+
ordinals = {}
|
|
88
|
+
namespaces.each_with_index { |namespace, index| ordinals[namespace.name] = index }
|
|
89
|
+
ordinals
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#: (Array[DeclarationCollector::Namespace], Hash[Integer, Integer], Integer) -> Array[Array[Integer]]
|
|
93
|
+
def namespace_rows(namespaces, inbound_counts, rspec_group_count)
|
|
94
|
+
drawn_names = ordinal_by_name(namespaces)
|
|
95
|
+
rows = namespaces.each_with_index.map do |namespace, index|
|
|
96
|
+
declaration = namespace.declaration
|
|
97
|
+
sites = namespace.definition_sites
|
|
98
|
+
# A namespace only earns a row through a class or module definition,
|
|
99
|
+
# so its own construct is exactly this kind.
|
|
100
|
+
kind = declaration.is_a?(Rubydex::Class) ? DeclarationCollector::CLASS : DeclarationCollector::MODULE
|
|
145
101
|
descendants = declaration.descendants.count do |descendant|
|
|
146
|
-
|
|
102
|
+
name = descendant.name
|
|
103
|
+
name != namespace.name && drawn_names.key?(name)
|
|
147
104
|
end
|
|
148
|
-
|
|
105
|
+
members, ruby_counts, instance_variables = member_statistics(
|
|
149
106
|
declaration,
|
|
150
|
-
|
|
107
|
+
kind: kind,
|
|
108
|
+
# Instance variables read as state on a class; test classes carry
|
|
109
|
+
# fixture state that would misrepresent them next to core classes.
|
|
110
|
+
count_instance_variables: kind == DeclarationCollector::CLASS && namespace.scope != LocationIndex::TEST,
|
|
151
111
|
)
|
|
152
112
|
[
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
scope,
|
|
113
|
+
kind,
|
|
114
|
+
namespace.scope,
|
|
156
115
|
[declaration.ancestors.count - 1, 0].max,
|
|
157
116
|
sites,
|
|
158
117
|
[sites - 1, 0].max,
|
|
159
118
|
descendants,
|
|
160
|
-
|
|
161
|
-
|
|
119
|
+
inbound_counts.fetch(index, 0),
|
|
120
|
+
members,
|
|
162
121
|
*ruby_counts,
|
|
163
|
-
|
|
122
|
+
instance_variables,
|
|
164
123
|
]
|
|
165
124
|
end
|
|
166
|
-
rows.concat(
|
|
125
|
+
rows.concat(Array.new(rspec_group_count) { RSPEC_ROW.dup })
|
|
167
126
|
end
|
|
168
127
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
128
|
+
# One sweep over a namespace's direct and singleton members yields the
|
|
129
|
+
# workspace member count, the method and constant construct counts, and
|
|
130
|
+
# the instance-variable count together, since each of the three otherwise
|
|
131
|
+
# re-reads every member's definitions.
|
|
132
|
+
#
|
|
133
|
+
#: (Rubydex::Namespace, kind: Integer, count_instance_variables: bool) -> [Integer, Array[Integer], Integer]
|
|
134
|
+
def member_statistics(declaration, kind:, count_instance_variables:)
|
|
135
|
+
members = 0
|
|
136
|
+
instance_variables = 0
|
|
137
|
+
ruby_counts = Array.new(4, 0)
|
|
138
|
+
ruby_counts[kind] += 1
|
|
139
|
+
|
|
140
|
+
seen = Set.new
|
|
141
|
+
groups = [[declaration.members, true]]
|
|
142
|
+
singleton = declaration.singleton_class
|
|
143
|
+
groups << [singleton.members, false] if singleton
|
|
144
|
+
groups.each do |group, direct|
|
|
145
|
+
group.each do |member|
|
|
146
|
+
next unless seen.add?(member.name)
|
|
147
|
+
next unless member.definitions.any? { |definition| @locations.workspace?(definition.location) }
|
|
148
|
+
|
|
149
|
+
members += 1
|
|
150
|
+
construct = case member
|
|
151
|
+
when Rubydex::Method then DeclarationCollector::METHOD
|
|
152
|
+
when Rubydex::Constant, Rubydex::ConstantAlias then DeclarationCollector::CONSTANT
|
|
153
|
+
end
|
|
154
|
+
ruby_counts[construct] += 1 if construct
|
|
155
|
+
instance_variables += 1 if direct && count_instance_variables && member.is_a?(Rubydex::InstanceVariable)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
[members, ruby_counts, instance_variables]
|
|
159
|
+
rescue StandardError
|
|
160
|
+
[members, ruby_counts, instance_variables]
|
|
176
161
|
end
|
|
177
162
|
|
|
178
|
-
|
|
179
|
-
|
|
163
|
+
#: (Array[DeclarationCollector::dependency_package]) -> Array[Hash[String, untyped]]
|
|
164
|
+
def package_rows(aggregates)
|
|
180
165
|
@manifest.packages.each_with_index.map do |package, index|
|
|
181
166
|
aggregate = aggregates.fetch(index)
|
|
182
167
|
{
|
|
183
168
|
"name" => package.name,
|
|
184
169
|
"role" => package.role == "direct" ? 0 : 1,
|
|
185
170
|
"location" => package.location == "workspace" ? 0 : 1,
|
|
186
|
-
"declaration_count" => aggregate.fetch(:declaration_count),
|
|
187
171
|
"ruby_counts" => aggregate.fetch(:ruby_counts),
|
|
188
172
|
"declarations" => aggregate.fetch(:declarations),
|
|
189
173
|
}
|
|
190
174
|
end
|
|
191
175
|
end
|
|
192
176
|
|
|
193
|
-
|
|
177
|
+
#: () -> Array[Hash[String, untyped]]
|
|
178
|
+
def dependency_system_rows
|
|
194
179
|
return [] unless @manifest.respond_to?(:dependency_systems)
|
|
195
180
|
|
|
196
181
|
@manifest.dependency_systems.map do |system|
|
|
@@ -202,182 +187,7 @@ module RubyLens
|
|
|
202
187
|
end
|
|
203
188
|
end
|
|
204
189
|
|
|
205
|
-
|
|
206
|
-
package_index, site_keys = summary.package_site_keys.max_by do |index, keys|
|
|
207
|
-
[keys.length, -index]
|
|
208
|
-
end
|
|
209
|
-
return unless package_index
|
|
210
|
-
|
|
211
|
-
sites = site_keys.uniq.length
|
|
212
|
-
row = [
|
|
213
|
-
namespace_kind(declaration),
|
|
214
|
-
namespace?(declaration) ? [declaration.ancestors.count - 1, 0].max : 0,
|
|
215
|
-
sites,
|
|
216
|
-
[sites - 1, 0].max,
|
|
217
|
-
namespace?(declaration) ? [declaration.descendants.count - 1, 0].max : 0,
|
|
218
|
-
safe_length(declaration, :references),
|
|
219
|
-
namespace?(declaration) ? safe_length(declaration, :members) : 0,
|
|
220
|
-
]
|
|
221
|
-
aggregation.add(
|
|
222
|
-
package_index:,
|
|
223
|
-
row:,
|
|
224
|
-
construct_index: ruby_construct_index(declaration),
|
|
225
|
-
)
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def inbound_workspace_references(graph, ordinal_by_name)
|
|
229
|
-
graph.constant_references.each_with_object(Hash.new(0)) do |reference, counts|
|
|
230
|
-
next unless reference.is_a?(Rubydex::ResolvedConstantReference)
|
|
231
|
-
|
|
232
|
-
ordinal = ordinal_by_name[reference.declaration.name]
|
|
233
|
-
next unless ordinal
|
|
234
|
-
|
|
235
|
-
counts[ordinal] += 1 if workspace_location?(reference.location)
|
|
236
|
-
rescue StandardError
|
|
237
|
-
next
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def package_index_for_location(location)
|
|
242
|
-
path = source_path(location.uri)
|
|
243
|
-
return nil unless path
|
|
244
|
-
return nil unless @indexed_package_document_paths.include?(path)
|
|
245
|
-
|
|
246
|
-
@manifest.package_index_for(path)
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def indexed_package_document_paths(graph)
|
|
250
|
-
audited = @manifest.packages.flat_map(&:files).to_set
|
|
251
|
-
graph.documents.each_with_object(Set.new) do |document, paths|
|
|
252
|
-
path = source_path(document.uri)
|
|
253
|
-
paths << path if path && audited.include?(path)
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# One sweep over a namespace's direct and singleton members yields the
|
|
258
|
-
# workspace member count, the method/constant construct counts, and the
|
|
259
|
-
# instance-variable count that previously took three walks each re-reading
|
|
260
|
-
# every member's definitions.
|
|
261
|
-
def member_statistics(declaration, count_instance_variables:)
|
|
262
|
-
member_count = 0
|
|
263
|
-
instance_variable_count = 0
|
|
264
|
-
ruby_counts = Array.new(4, 0)
|
|
265
|
-
own_construct = ruby_construct_index(declaration)
|
|
266
|
-
ruby_counts[own_construct] += 1 if own_construct == 0 || own_construct == 1
|
|
267
|
-
|
|
268
|
-
seen_names = Set.new
|
|
269
|
-
walk = lambda do |members, direct|
|
|
270
|
-
members.each do |member|
|
|
271
|
-
next unless seen_names.add?(member.name)
|
|
272
|
-
next unless member.definitions.any? { |definition| workspace_location?(definition.location) }
|
|
273
|
-
|
|
274
|
-
member_count += 1
|
|
275
|
-
construct_index = ruby_construct_index(member)
|
|
276
|
-
ruby_counts[construct_index] += 1 if construct_index && construct_index >= 2
|
|
277
|
-
if direct && count_instance_variables && member.is_a?(Rubydex::InstanceVariable)
|
|
278
|
-
instance_variable_count += 1
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
walk.call(declaration.members, true)
|
|
283
|
-
walk.call(declaration.singleton_class.members, false) if declaration.singleton_class
|
|
284
|
-
[member_count, ruby_counts, instance_variable_count]
|
|
285
|
-
rescue StandardError
|
|
286
|
-
[member_count, ruby_counts, instance_variable_count]
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def collect_category_stat(stats, declaration, summary)
|
|
290
|
-
construct_index = ruby_construct_index(declaration)
|
|
291
|
-
return unless construct_index
|
|
292
|
-
return if summary.workspace_count.zero?
|
|
293
|
-
|
|
294
|
-
category = scope_from(summary.workspace_relatives) == 1 ? "tests" : "core"
|
|
295
|
-
stats.fetch(category)[construct_index] += 1
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
def workspace_location?(location)
|
|
299
|
-
uri = location.uri
|
|
300
|
-
return @workspace_location_cache[uri] if @workspace_location_cache.key?(uri)
|
|
301
|
-
|
|
302
|
-
path = source_path(uri)
|
|
303
|
-
@workspace_location_cache[uri] = path ? @manifest.workspace_path?(path) : false
|
|
304
|
-
rescue StandardError
|
|
305
|
-
false
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def component_from(relatives)
|
|
309
|
-
candidates = relatives.map { |relative| SourcePath.component_for(relative) }
|
|
310
|
-
candidates.tally.max_by { |name, count| [count, name] }&.first || "root"
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def scope_from(relatives)
|
|
314
|
-
scopes = relatives.map do |relative|
|
|
315
|
-
relative.split(File::SEPARATOR).any? { |segment| TEST_SEGMENTS.include?(segment) } ? 1 : 0
|
|
316
|
-
end.uniq
|
|
317
|
-
scopes.length > 1 ? 2 : scopes.first || 0
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def site_key(location)
|
|
321
|
-
location.comparable_values
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
def namespace?(declaration)
|
|
325
|
-
declaration.is_a?(Rubydex::Namespace)
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
def model_eligible_declaration?(declaration)
|
|
329
|
-
name = declaration.name
|
|
330
|
-
return false if name.nil? || name.empty? || name.include?("<anonymous>")
|
|
331
|
-
return false if declaration.is_a?(Rubydex::Todo)
|
|
332
|
-
return true unless declaration.is_a?(Rubydex::SingletonClass)
|
|
333
|
-
|
|
334
|
-
!declaration.attached_class.is_a?(Rubydex::SingletonClass) &&
|
|
335
|
-
!declaration.attached_class.is_a?(Rubydex::Todo)
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def canonical_namespace_definition?(declaration, definition)
|
|
339
|
-
(declaration.is_a?(Rubydex::Class) && definition.is_a?(Rubydex::ClassDefinition)) ||
|
|
340
|
-
(declaration.is_a?(Rubydex::Module) && definition.is_a?(Rubydex::ModuleDefinition))
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def namespace_kind(declaration)
|
|
344
|
-
case declaration
|
|
345
|
-
when Rubydex::Class then 0
|
|
346
|
-
when Rubydex::Module then 1
|
|
347
|
-
else 2
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def ruby_construct_index(declaration)
|
|
352
|
-
case declaration
|
|
353
|
-
when Rubydex::SingletonClass then nil
|
|
354
|
-
when Rubydex::Class then 0
|
|
355
|
-
when Rubydex::Module then 1
|
|
356
|
-
when Rubydex::Method then 2
|
|
357
|
-
when Rubydex::Constant, Rubydex::ConstantAlias then 3
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def source_path(uri_string)
|
|
362
|
-
return @source_path_cache[uri_string] if @source_path_cache.key?(uri_string)
|
|
363
|
-
|
|
364
|
-
@source_path_cache[uri_string] = SourcePath.from_file_uri(uri_string)
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def safe_length(object, method)
|
|
368
|
-
records = object.public_send(method)
|
|
369
|
-
size = records.size
|
|
370
|
-
size.nil? ? safe_count(records) : size
|
|
371
|
-
rescue StandardError
|
|
372
|
-
safe_count(records)
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
def safe_count(records)
|
|
376
|
-
records ? records.count : 0
|
|
377
|
-
rescue StandardError
|
|
378
|
-
0
|
|
379
|
-
end
|
|
380
|
-
|
|
190
|
+
#: () -> String
|
|
381
191
|
def project_name
|
|
382
192
|
basename = @manifest.root.basename.to_s
|
|
383
193
|
return "IRB" if basename.casecmp("irb").zero?
|
|
@@ -6,13 +6,24 @@ module RubyLens
|
|
|
6
6
|
module Index
|
|
7
7
|
# Maps Rubydex document and location URIs back to workspace source paths.
|
|
8
8
|
module SourcePath
|
|
9
|
-
|
|
9
|
+
# The canonical shape Rubydex emits: an empty authority and a
|
|
10
|
+
# percent-encoded absolute path built from RFC 2396 path characters.
|
|
11
|
+
# Anything else falls back to full URI parsing below.
|
|
12
|
+
ENCODED_FILE_URI = %r{\Afile://(/(?:[A-Za-z0-9\-_.!~*'()/:@&=+$,;]|%[0-9A-Fa-f]{2})*)\z}
|
|
10
13
|
|
|
11
14
|
module_function
|
|
12
15
|
|
|
13
16
|
# Rubydex 0.2.9 percent-encodes file URIs and Location#to_file_path
|
|
14
17
|
# returns the path still encoded, so decode the URI ourselves.
|
|
15
18
|
def from_file_uri(uri_string)
|
|
19
|
+
match = ENCODED_FILE_URI.match(uri_string)
|
|
20
|
+
if match
|
|
21
|
+
path = match[1]
|
|
22
|
+
path = URI::RFC2396_PARSER.unescape(path) if path.include?("%")
|
|
23
|
+
path = path.delete_prefix("/") if Gem.win_platform?
|
|
24
|
+
return path
|
|
25
|
+
end
|
|
26
|
+
|
|
16
27
|
uri = URI.parse(uri_string)
|
|
17
28
|
return unless uri.scheme == "file"
|
|
18
29
|
return if uri.host && !uri.host.empty? && uri.host != "localhost"
|
|
@@ -24,16 +35,6 @@ module RubyLens
|
|
|
24
35
|
rescue URI::InvalidURIError
|
|
25
36
|
nil
|
|
26
37
|
end
|
|
27
|
-
|
|
28
|
-
def component_for(relative)
|
|
29
|
-
segments = relative.split(File::SEPARATOR)
|
|
30
|
-
first = segments.first || "root"
|
|
31
|
-
if COMPONENT_ROOTS.include?(first)
|
|
32
|
-
"#{first}/#{segments[1] || "root"}"
|
|
33
|
-
else
|
|
34
|
-
first
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -8,25 +8,29 @@ 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
|
+
column = SIGNAL_COLUMNS.begin
|
|
21
|
+
while column <= SIGNAL_COLUMNS.end
|
|
22
|
+
value = row[column]
|
|
23
|
+
index = column - 1
|
|
24
|
+
maxima[index] = value if value > maxima[index]
|
|
25
|
+
column += 1
|
|
22
26
|
end
|
|
23
|
-
@rows[package_index] << row.dup.freeze
|
|
27
|
+
@rows[package_index] << (row.frozen? ? row : row.dup.freeze)
|
|
28
|
+
local_index
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
def packages
|
|
27
|
-
@
|
|
32
|
+
@rows.each_index.map do |index|
|
|
28
33
|
{
|
|
29
|
-
declaration_count: @counts[index],
|
|
30
34
|
ruby_counts: @ruby_counts[index].dup.freeze,
|
|
31
35
|
declarations: @rows[index].dup.freeze,
|
|
32
36
|
}.freeze
|