namespaced-gem 0.1.0.pre

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.
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "uri_dependency"
4
+
5
+ module Namespaced
6
+ module Gem
7
+ # Implements a `Gem.done_installing` hook that processes deferred
8
+ # namespace dependencies stored in a gem's metadata.
9
+ #
10
+ # ## Why This Exists
11
+ #
12
+ # When `gem install my-gem` runs and `namespaced-gem` is listed as a
13
+ # runtime dependency of `my-gem`, the following sequence occurs:
14
+ #
15
+ # 1. RubyGems resolves ALL dependencies before installing ANY.
16
+ # 2. URI-style deps (e.g. "https://beta.gem.coop/@ns/foo") can't be
17
+ # resolved on rubygems.org — they'd cause resolution to fail.
18
+ # 3. After resolution, gems install in topological order (leaves first).
19
+ # 4. `namespaced-gem` installs first → `load_plugin` fires →
20
+ # `rubygems_plugin.rb` is hot-loaded → this hook registers.
21
+ # 5. After ALL gems in the batch finish installing, `done_installing`
22
+ # fires and this hook processes the deferred namespace deps.
23
+ #
24
+ # ## Gemspec Convention
25
+ #
26
+ # Gem authors encode namespace dependencies in metadata:
27
+ #
28
+ # spec.metadata["namespaced_dependencies"] = <<~DEPS
29
+ # https://beta.gem.coop/@myspace/foo ~> 1.0
30
+ # @myorg/bar >= 2.0
31
+ # pkg:gem/@myorg/baz ~> 3.0
32
+ # DEPS
33
+ #
34
+ # Each line is: `<uri-name> <version-constraint>...`
35
+ #
36
+ # For Bundler workflows, the same deps should ALSO be declared via
37
+ # `add_dependency` (Bundler handles URI deps natively via
38
+ # BundlerIntegration). The metadata field is only needed for the
39
+ # `gem install` code path.
40
+ #
41
+ # ## Helper for Gem Authors
42
+ #
43
+ # Use `Namespaced::Gem.add_namespaced_dependency` in your gemspec to
44
+ # automatically write both `add_dependency` and the metadata field:
45
+ #
46
+ # Namespaced::Gem.add_namespaced_dependency(spec, "https://beta.gem.coop/@ns/foo", "~> 1.0")
47
+ #
48
+ module MetadataDepsHook
49
+ METADATA_KEY = "namespaced_dependencies"
50
+
51
+ # Register the done_installing hook. Called from rubygems_plugin.rb.
52
+ def self.register!
53
+ return if @registered
54
+
55
+ ::Gem.done_installing do |_dep_installer, specs|
56
+ MetadataDepsHook.process_batch(specs)
57
+ end
58
+
59
+ @registered = true
60
+ end
61
+
62
+ # Process a batch of just-installed gem specs, looking for deferred
63
+ # namespace dependencies in their metadata.
64
+ def self.process_batch(specs)
65
+ specs.each do |spec|
66
+ process_spec(spec)
67
+ end
68
+ end
69
+
70
+ # Process a single spec's metadata for namespace dependencies.
71
+ def self.process_spec(spec)
72
+ raw = spec.metadata[METADATA_KEY]
73
+ return unless raw.is_a?(String) && !raw.strip.empty?
74
+
75
+ deps = parse_metadata_deps(raw)
76
+ return if deps.empty?
77
+
78
+ install_namespace_deps(deps, spec)
79
+ end
80
+
81
+ # Parse the metadata string into an array of [uri_name, version_constraints].
82
+ #
83
+ # Format: one dep per line, `<uri-name> <version constraints...>`
84
+ # https://beta.gem.coop/@myspace/foo ~> 1.0
85
+ # @myorg/bar >= 2.0, < 3.0
86
+ # pkg:gem/@myorg/baz ~> 3.0
87
+ def self.parse_metadata_deps(raw)
88
+ raw.strip.lines.filter_map do |line|
89
+ line = line.strip
90
+ next if line.empty? || line.start_with?("#")
91
+
92
+ # Split into URI name and version constraints.
93
+ # The URI name is the first whitespace-delimited token.
94
+ parts = line.split(/\s+/, 2)
95
+ uri_name = parts[0]
96
+ version_str = parts[1]
97
+
98
+ next unless UriDependency.uri?(uri_name)
99
+
100
+ version_reqs = if version_str && !version_str.strip.empty?
101
+ # Split on comma for multiple constraints: "~> 1.0, < 2.0"
102
+ version_str.split(",").map(&:strip).reject(&:empty?)
103
+ else
104
+ [">= 0"]
105
+ end
106
+
107
+ [uri_name, version_reqs]
108
+ end
109
+ end
110
+
111
+ # Install namespace dependencies using the now-active patches.
112
+ #
113
+ # At this point, rubygems_plugin.rb has been hot-loaded, so:
114
+ # - DependencyPatch is active (uri_gem?, uri_dependency)
115
+ # - GemResolverPatch is active (RequestSet/InstallerSet handle URI deps)
116
+ # - ApiSpecPatch is active (synthesizes specs from Compact Index)
117
+ # - DownloadPatch is active (clear namespace download errors)
118
+ def self.install_namespace_deps(deps, parent_spec)
119
+ deps.each do |uri_name, version_reqs|
120
+ uri_dep = UriDependency.parse(uri_name)
121
+
122
+ warn "[namespaced-gem] Installing deferred namespace dependency: " \
123
+ "#{uri_dep.gem_name} (#{version_reqs.join(", ")}) " \
124
+ "from #{uri_dep.source_url} " \
125
+ "(required by #{parent_spec.name})"
126
+
127
+ begin
128
+ installer = ::Gem::DependencyInstaller.new(
129
+ domain: :both,
130
+ force: false,
131
+ # Let the user's existing gem path config apply
132
+ )
133
+ # The GemResolverPatch will intercept this URI dep during
134
+ # resolution and route it to the correct namespace source.
135
+ installer.install(uri_name, ::Gem::Requirement.new(*version_reqs))
136
+ rescue ::Gem::UnsatisfiableDependencyError, Namespaced::Gem::Error => e
137
+ warn "[namespaced-gem] WARNING: Failed to install namespace dependency " \
138
+ "#{uri_dep.gem_name} from #{uri_dep.source_url}: #{e.message}"
139
+ warn "[namespaced-gem] This may be a server-side issue. See ISSUE.md."
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # Convenience method for gem authors to declare a namespaced dependency
146
+ # in both `add_dependency` (for Bundler) and `metadata` (for gem install).
147
+ #
148
+ # Usage in gemspec:
149
+ # require "namespaced/gem"
150
+ # Namespaced::Gem.add_namespaced_dependency(spec, "https://beta.gem.coop/@ns/foo", "~> 1.0")
151
+ #
152
+ def self.add_namespaced_dependency(spec, uri_name, *version_constraints)
153
+ # Standard add_dependency — works with Bundler via BundlerIntegration
154
+ spec.add_dependency(uri_name, *version_constraints)
155
+
156
+ # Also store in metadata for the gem install hot-load path
157
+ version_str = version_constraints.join(", ")
158
+ entry = "#{uri_name} #{version_str}"
159
+
160
+ existing = spec.metadata[MetadataDepsHook::METADATA_KEY]
161
+ spec.metadata[MetadataDepsHook::METADATA_KEY] = if existing && !existing.strip.empty?
162
+ "#{existing.strip}\n#{entry}"
163
+ else
164
+ entry
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Namespaced
4
+ module Gem
5
+ # Thread-safe registry of namespace source URLs.
6
+ #
7
+ # When the GemResolverPatch or other code creates a Gem::Source for a
8
+ # namespace URL (e.g. "https://beta.gem.coop/@kaspth"), it registers
9
+ # that URL here. Later, patches like ApiSpecPatch and DownloadPatch
10
+ # can check whether a given Gem::Source is a namespace source — without
11
+ # relying on heuristics like scanning the URL for "@" segments.
12
+ #
13
+ # This registry is the single source of truth for "is this source a
14
+ # namespace source that our plugin manages?"
15
+ module NamespaceSourceRegistry
16
+ @mutex = Mutex.new
17
+ @sources = {} # normalized_url_string => true
18
+
19
+ # Register a namespace source URL. Safe to call multiple times with
20
+ # the same URL (idempotent).
21
+ def self.register(source_url)
22
+ normalized = normalize(source_url)
23
+ @mutex.synchronize { @sources[normalized] = true }
24
+ end
25
+
26
+ # Returns true if +source+ is a Gem::Source whose URI was previously
27
+ # registered as a namespace source.
28
+ def self.namespace_source?(source)
29
+ return false unless source.is_a?(::Gem::Source)
30
+
31
+ uri = source.respond_to?(:uri) && source.uri
32
+ return false unless uri
33
+
34
+ namespace_source_url?(uri)
35
+ end
36
+
37
+ # Returns true if the given URL string (or URI object) was registered.
38
+ def self.namespace_source_url?(url)
39
+ normalized = normalize(url)
40
+ @mutex.synchronize { @sources.key?(normalized) }
41
+ end
42
+
43
+ # Clear all registered sources. Primarily for testing.
44
+ def self.clear!
45
+ @mutex.synchronize { @sources.clear }
46
+ end
47
+
48
+ # Returns a frozen copy of all registered source URLs (for debugging).
49
+ def self.registered_urls
50
+ @mutex.synchronize { @sources.keys.freeze }
51
+ end
52
+
53
+ class << self
54
+ private
55
+
56
+ def normalize(url)
57
+ url.to_s.chomp("/")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Namespaced
6
+ module Gem
7
+ # Represents a gem dependency specified as a URI, enabling namespaced gem
8
+ # sources (e.g. gem.coop namespaces) to be declared directly in gemspecs.
9
+ #
10
+ # Supported formats:
11
+ # Full URI: "https://beta.gem.coop/@myspace/my-gem"
12
+ # Shorthand: "@myspace/my-gem" (defaults to https://beta.gem.coop)
13
+ # Package URL (purl-spec):
14
+ # "pkg:gem/@myspace/my-gem"
15
+ # "pkg:gem/@myspace/my-gem?repository_url=https://beta.gem.coop"
16
+ # "pkg:gem/my-gem?repository_url=https://beta.gem.coop/@myspace"
17
+ #
18
+ # Usage in a gemspec:
19
+ # spec.add_dependency "https://beta.gem.coop/@myspace/my-gem", "~> 1.0"
20
+ # spec.add_dependency "pkg:gem/@myspace/my-gem", "~> 1.0"
21
+ class UriDependency
22
+ DEFAULT_SERVER = "https://beta.gem.coop"
23
+
24
+ # Pattern matching full HTTPS/HTTP URIs to a namespaced gem.
25
+ # e.g. https://beta.gem.coop/@namespace/gem-name
26
+ FULL_URI_PATTERN = %r{
27
+ \A
28
+ (https?://[^/]+) # server base, e.g. https://beta.gem.coop
29
+ /
30
+ (@[^/]+) # namespace, e.g. @myspace
31
+ /
32
+ ([a-zA-Z0-9._\-]+) # gem name (dots allowed per RubyGems convention)
33
+ \z
34
+ }x
35
+
36
+ # Shorthand: @namespace/gem-name (no server; defaults to beta.gem.coop)
37
+ SHORTHAND_PATTERN = %r{
38
+ \A
39
+ (@[^/]+) # namespace, e.g. @myspace
40
+ /
41
+ ([a-zA-Z0-9._\-]+) # gem name (dots allowed per RubyGems convention)
42
+ \z
43
+ }x
44
+
45
+ # Package URL (purl-spec): pkg:gem/[@namespace/]gem-name[@version][?qualifiers]
46
+ # See https://github.com/package-url/purl-spec
47
+ #
48
+ # Supported forms:
49
+ # pkg:gem/@myspace/my-gem — namespace, default server
50
+ # pkg:gem/@myspace/my-gem?repository_url=https://beta.gem.coop — namespace + explicit server
51
+ # pkg:gem/my-gem?repository_url=https://beta.gem.coop/@myspace — namespace embedded in qualifier
52
+ #
53
+ # The @version component (if present) is captured but ignored — version
54
+ # constraints come from the second argument to add_dependency.
55
+ PURL_PATTERN = %r{
56
+ \A
57
+ pkg:gem/
58
+ (?:(@[^/]+)/)? # optional namespace, e.g. @myspace
59
+ ([a-zA-Z0-9._\-]+) # gem name
60
+ (?:@[^?]+)? # optional @version (ignored)
61
+ (?:\?(.+))? # optional ?qualifiers
62
+ \z
63
+ }x
64
+
65
+ attr_reader :original, :server_base, :namespace, :gem_name
66
+
67
+ # Returns true if +name+ looks like a URI dependency (full URI, shorthand,
68
+ # or purl).
69
+ def self.uri?(name)
70
+ return false unless name.is_a?(String)
71
+
72
+ FULL_URI_PATTERN.match?(name) || SHORTHAND_PATTERN.match?(name) || PURL_PATTERN.match?(name)
73
+ end
74
+
75
+ # Parses +name+ into a UriDependency. Raises ArgumentError if not a URI dep.
76
+ def self.parse(name)
77
+ new(name)
78
+ end
79
+
80
+ def initialize(name)
81
+ @original = -String(name)
82
+
83
+ if (m = FULL_URI_PATTERN.match(@original))
84
+ @server_base = -m[1]
85
+ @namespace = -m[2]
86
+ @gem_name = -m[3]
87
+ elsif (m = SHORTHAND_PATTERN.match(@original))
88
+ @server_base = DEFAULT_SERVER
89
+ @namespace = -m[1]
90
+ @gem_name = -m[2]
91
+ elsif (m = PURL_PATTERN.match(@original))
92
+ parse_purl(m)
93
+ else
94
+ raise ArgumentError, "Not a valid URI dependency: #{name.inspect}"
95
+ end
96
+
97
+ freeze
98
+ end
99
+
100
+ # The Bundler source URL for this dependency's namespace.
101
+ # This is what you'd put in a Gemfile `source` block.
102
+ def source_url
103
+ "#{server_base}/#{namespace}"
104
+ end
105
+
106
+ # Value equality — two UriDependency objects are equal when they refer
107
+ # to the same gem in the same namespace on the same server.
108
+ def ==(other)
109
+ other.is_a?(self.class) &&
110
+ server_base == other.server_base &&
111
+ namespace == other.namespace &&
112
+ gem_name == other.gem_name
113
+ end
114
+
115
+ alias eql? ==
116
+
117
+ def hash
118
+ [self.class, server_base, namespace, gem_name].hash
119
+ end
120
+
121
+ def to_s
122
+ "#{source_url}/#{gem_name}"
123
+ end
124
+
125
+ def inspect
126
+ "#<#{self.class} gem_name=#{gem_name.inspect} source_url=#{source_url.inspect}>"
127
+ end
128
+
129
+ private
130
+
131
+ # Parse a purl match into server_base, namespace, and gem_name.
132
+ #
133
+ # Three forms:
134
+ # 1. pkg:gem/@ns/name → namespace from purl, default server
135
+ # 2. pkg:gem/@ns/name?repository_url=https://server
136
+ # → namespace from purl, server from qualifier
137
+ # 3. pkg:gem/name?repository_url=https://server/@ns
138
+ # → both from qualifier
139
+ def parse_purl(match)
140
+ purl_namespace = match[1] # may be nil
141
+ @gem_name = -match[2]
142
+ qualifiers = parse_qualifiers(match[3])
143
+ repo_url = qualifiers["repository_url"]
144
+
145
+ if purl_namespace
146
+ # Forms 1 & 2: namespace is in the purl path
147
+ @namespace = -purl_namespace
148
+ @server_base = repo_url ? -repo_url.chomp("/") : DEFAULT_SERVER
149
+ elsif repo_url
150
+ # Form 3: namespace is embedded in repository_url
151
+ # e.g. https://beta.gem.coop/@myspace
152
+ repo_uri = URI.parse(repo_url)
153
+ path_segments = repo_uri.path.split("/").reject(&:empty?)
154
+ ns_segment = path_segments.find { |s| s.start_with?("@") }
155
+
156
+ unless ns_segment
157
+ raise ArgumentError,
158
+ "purl qualifier repository_url must include a @namespace or the purl path must: #{@original.inspect}"
159
+ end
160
+
161
+ @namespace = -ns_segment
162
+ @server_base = -"#{repo_uri.scheme}://#{repo_uri.host}#{repo_uri.port == repo_uri.default_port ? "" : ":#{repo_uri.port}"}"
163
+ else
164
+ raise ArgumentError,
165
+ "purl for a namespaced gem must include a @namespace in the path or a repository_url qualifier: #{@original.inspect}"
166
+ end
167
+ end
168
+
169
+ # Parse a URL query string into a Hash. Returns an empty Hash for nil.
170
+ def parse_qualifiers(raw)
171
+ return {} unless raw
172
+
173
+ URI.decode_www_form(raw).to_h
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Namespaced
4
+ module Gem
5
+ VERSION = "0.1.0.pre"
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gem/version"
4
+ require_relative "gem/uri_dependency"
5
+ require_relative "gem/namespace_source_registry"
6
+ require_relative "gem/dependency_patch"
7
+ require_relative "gem/api_spec_patch"
8
+ require_relative "gem/download_patch"
9
+ require_relative "gem/bundler_integration"
10
+ require_relative "gem/bundler_resolver_patch"
11
+ require_relative "gem/gem_resolver_patch"
12
+ require_relative "gem/metadata_deps_hook"
13
+
14
+ module Namespaced
15
+ # Namespaced::Gem is a RubyGems plugin/shim that enables gemspec dependencies
16
+ # to be declared as full URIs, pointing to namespaced gem sources such as
17
+ # gem.coop namespaces (e.g. https://beta.gem.coop/@myspace/my-gem).
18
+ #
19
+ # When installed, this gem's rubygems_plugin.rb is loaded at RubyGems boot,
20
+ # patching Gem::Dependency to accept URI names, Bundler::Dsl to automatically
21
+ # inject source blocks, and both Bundler's and RubyGems' resolvers to remap
22
+ # URI-named transitive deps to their real names and namespace sources.
23
+ module Gem
24
+ class Error < StandardError; end
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RubyGems plugin for namespaced-gem.
4
+ #
5
+ # This file is automatically loaded by RubyGems at boot time (before any
6
+ # gemspecs are evaluated) because it matches the `rubygems*_plugin.rb` naming
7
+ # convention in the gem's require path.
8
+ #
9
+ # What it does:
10
+ # 1. Patches Gem::Dependency to accept URI-style gem names such as
11
+ # "https://beta.gem.coop/@namespace/gem-name".
12
+ # 2. Patches Gem::RequestSet and Gem::Resolver::InstallerSet so that
13
+ # `gem install` can resolve URI-named deps against the correct namespace
14
+ # source, both as direct deps and as transitive deps.
15
+ # 3. Patches Gem::Resolver::APISpecification#spec to synthesize a
16
+ # Gem::Specification from Compact Index data for namespace sources —
17
+ # bypassing the missing Marshal endpoint.
18
+ # 4. Patches Gem::Source#download to provide clear error messages when
19
+ # namespace servers fail to serve .gem files.
20
+ # 5. Registers deferred patches for Bundler::Dsl, Bundler::Definition, and
21
+ # Bundler::Resolver so that `bundle install` / `bundle add` automatically
22
+ # resolve URI deps (both direct and transitive) against namespace sources.
23
+ # 6. Registers a Gem.done_installing hook that processes deferred namespace
24
+ # dependencies stored in spec.metadata["namespaced_dependencies"]. This
25
+ # enables the "hot-load" path: when namespaced-gem is installed as a leaf
26
+ # dependency, Gem::Installer#load_plugin hot-loads this file, and the
27
+ # done_installing hook triggers a second install pass for URI deps.
28
+ #
29
+ # See lib/namespaced/gem/dependency_patch.rb — Gem::Dependency patch
30
+ # lib/namespaced/gem/gem_resolver_patch.rb — Gem::RequestSet / InstallerSet
31
+ # lib/namespaced/gem/api_spec_patch.rb — Gem::Resolver::APISpecification
32
+ # lib/namespaced/gem/download_patch.rb — Gem::Source#download
33
+ # lib/namespaced/gem/bundler_integration.rb — Bundler::Dsl patch
34
+ # lib/namespaced/gem/bundler_resolver_patch.rb — Bundler::Definition / Resolver
35
+
36
+ require_relative "namespaced/gem/dependency_patch"
37
+ require_relative "namespaced/gem/gem_resolver_patch"
38
+ require_relative "namespaced/gem/api_spec_patch"
39
+ require_relative "namespaced/gem/download_patch"
40
+ require_relative "namespaced/gem/bundler_integration"
41
+ require_relative "namespaced/gem/bundler_resolver_patch"
42
+ require_relative "namespaced/gem/metadata_deps_hook"
43
+
44
+ # 1. Patch Gem::Dependency immediately — must run before any gemspec is parsed.
45
+ Namespaced::Gem::DependencyPatch.apply!
46
+
47
+ # 2. Patch Gem::RequestSet / InstallerSet for `gem install` — always available.
48
+ Namespaced::Gem::GemResolverPatch.apply!
49
+
50
+ # 3. Patch Gem::Resolver::APISpecification#spec to synthesize specs for
51
+ # namespace sources (bypasses the missing Marshal endpoint).
52
+ Namespaced::Gem::ApiSpecPatch.apply!
53
+
54
+ # 4. Patch Gem::Source#download for clear namespace download error messages.
55
+ Namespaced::Gem::DownloadPatch.apply!
56
+
57
+ # 5. Patch Bundler if loaded, or defer until it is.
58
+ Namespaced::Gem::BundlerIntegration.apply_when_ready!
59
+ Namespaced::Gem::BundlerResolverPatch.apply_when_ready!
60
+
61
+ # 6. Register the done_installing hook for deferred namespace dependencies.
62
+ #
63
+ # This enables the "hot-load" path for `gem install my-gem` where my-gem
64
+ # depends on namespaced-gem and stores URI deps in metadata:
65
+ #
66
+ # spec.metadata["namespaced_dependencies"] = "https://beta.gem.coop/@ns/foo ~> 1.0"
67
+ #
68
+ # When namespaced-gem is installed as a leaf dependency (topological order),
69
+ # Gem::Installer#load_plugin hot-loads this file into the running process.
70
+ # The done_installing hook then fires after ALL gems in the batch finish
71
+ # installing, reads the metadata, and triggers a second resolution pass
72
+ # for the deferred namespace deps — with all patches now active.
73
+ Namespaced::Gem::MetadataDepsHook.register!
@@ -0,0 +1,87 @@
1
+ module Namespaced
2
+ module Gem
3
+ VERSION: String
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class UriDependency
9
+ DEFAULT_SERVER: String
10
+ FULL_URI_PATTERN: Regexp
11
+ SHORTHAND_PATTERN: Regexp
12
+ PURL_PATTERN: Regexp
13
+
14
+ attr_reader original: String
15
+ attr_reader server_base: String
16
+ attr_reader namespace: String
17
+ attr_reader gem_name: String
18
+
19
+ def self.uri?: (untyped name) -> bool
20
+ def self.parse: (String name) -> UriDependency
21
+
22
+ def initialize: (String name) -> void
23
+
24
+ def source_url: () -> String
25
+ def ==: (untyped other) -> bool
26
+ alias eql? ==
27
+ def hash: () -> Integer
28
+ def to_s: () -> String
29
+ def inspect: () -> String
30
+
31
+ private
32
+
33
+ def parse_purl: (MatchData match) -> void
34
+ def parse_qualifiers: (String? raw) -> Hash[String, String]
35
+ end
36
+
37
+ module DependencyPatch
38
+ def self.apply!: () -> void
39
+
40
+ module InstanceMethods
41
+ def uri_gem?: () -> bool
42
+ def uri_dependency: () -> UriDependency?
43
+ end
44
+ end
45
+
46
+ module BundlerIntegration
47
+ def self.apply!: () -> void
48
+ def self.apply_when_ready!: () -> void
49
+
50
+ module DslPatch
51
+ def gemspec: (?Hash[Symbol, untyped]? opts) -> void
52
+
53
+ private def inject_uri_sources_for: (Gem::Specification spec) -> void
54
+ end
55
+ end
56
+
57
+ module BundlerResolverPatch
58
+ def self.apply!: () -> void
59
+ def self.apply_when_ready!: () -> void
60
+
61
+ module DefinitionPatch
62
+ def initialize: (untyped lockfile, Array[untyped] dependencies, untyped sources, *untyped) -> void
63
+
64
+ private def remap_uri_dependencies: (Array[untyped] dependencies) -> [Array[untyped], Array[String]]
65
+ private def dep_options_for: (untyped dep, String source_url) -> Hash[String, untyped]
66
+ end
67
+
68
+ module ResolverPatch
69
+ def to_dependency_hash: (Array[untyped] dependencies, untyped packages) -> untyped
70
+ end
71
+ end
72
+
73
+ module GemResolverPatch
74
+ def self.apply!: () -> void
75
+
76
+ module RequestSetPatch
77
+ def resolve: (?untyped set) -> untyped
78
+ end
79
+
80
+ module InstallerSetPatch
81
+ def find_all: (untyped req) -> Array[untyped]
82
+
83
+ private def _namespaced_resolver_set: (String source_url) -> untyped
84
+ end
85
+ end
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: namespaced-gem
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Peter H. Boling
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: "\U0001F50C A RubyGems plugin that allows gemspec dependencies to be
13
+ declared as full\nURIs pointing to namespaced gem sources such as gem.coop namespaces\n(e.g.
14
+ `https://beta.gem.coop/@myspace/my-gem`).\n\nWhen installed, this gem patches both
15
+ RubyGems' native resolver (`gem\ninstall`) and Bundler's resolver (`bundle install`)
16
+ to parse URI dependency\nnames, route them to the correct namespace source, and
17
+ remap transitive deps\non the fly — so `gem install @kaspth/oaken` and `bundle install`
18
+ with URI\ndeps in gemspecs both Just Work™.\n\nSee https://github.com/gem-coop/gem.coop/issues/12
19
+ for the original discussion.\n"
20
+ email:
21
+ - peter.boling@gmail.com
22
+ executables: []
23
+ extensions: []
24
+ extra_rdoc_files: []
25
+ files:
26
+ - ".idea/.gitignore"
27
+ - ".idea/git_toolbox_prj.xml"
28
+ - ".idea/modules.xml"
29
+ - CHANGELOG.md
30
+ - CODE_OF_CONDUCT.md
31
+ - HOT_HOOK.md
32
+ - HOT_LOAD_ANALYSIS.md
33
+ - ISSUE.md
34
+ - README.md
35
+ - Rakefile
36
+ - WORKAROUND.md
37
+ - lib/example_hooks.rb
38
+ - lib/namespaced/gem.rb
39
+ - lib/namespaced/gem/api_spec_patch.rb
40
+ - lib/namespaced/gem/bundler_integration.rb
41
+ - lib/namespaced/gem/bundler_resolver_patch.rb
42
+ - lib/namespaced/gem/dependency_patch.rb
43
+ - lib/namespaced/gem/download_patch.rb
44
+ - lib/namespaced/gem/gem_resolver_patch.rb
45
+ - lib/namespaced/gem/metadata_deps_hook.rb
46
+ - lib/namespaced/gem/namespace_source_registry.rb
47
+ - lib/namespaced/gem/uri_dependency.rb
48
+ - lib/namespaced/gem/version.rb
49
+ - lib/rubygems_plugin.rb
50
+ - sig/namespaced/gem.rbs
51
+ homepage: https://gitlab.com/galtzo-floss/namespaced-gem
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ allowed_push_host: https://rubygems.org
56
+ homepage_uri: https://gitlab.com/galtzo-floss/namespaced-gem
57
+ source_code_uri: https://gitlab.com/galtzo-floss/namespaced-gem
58
+ changelog_uri: https://gitlab.com/galtzo-floss/namespaced-gem/-/blob/main/CHANGELOG.md
59
+ rubygems_mfa_required: 'true'
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 3.2.0
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 4.0.5
73
+ requirements: []
74
+ rubygems_version: 4.0.7
75
+ specification_version: 4
76
+ summary: "\U0001F50C RubyGems plugin enabling URI-style gemspec dependencies for namespaced
77
+ gem sources (e.g. gem.coop namespaces)."
78
+ test_files: []