go-bundler 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d96473cb71dd04ce70c9fa99f229ad2eb964648357fc5aba88ab78d09c9788a
4
+ data.tar.gz: b892c5a023963a814a017bd74b0a5649f930be12625208efdfdcccb89d430326
5
+ SHA512:
6
+ metadata.gz: e36cefecc73d2c3b8628bdd28a9890e8d32e11fb450c769d145629fed5ce8534ce1e3831dd2e34a5eb9d5f6afd69e48407feba41749d8fcc00dca0561a25c013
7
+ data.tar.gz: c095f603d7e27d087c7562ca4df4ef3818642436de295bdb5ebbd5439052d44bede906332e0a59776c6c4efe67c9b4911dd9de23b9b7d69c88aaf7454f1630d6
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-12-24
4
+
5
+ - Initial release
@@ -0,0 +1,10 @@
1
+ # Code of Conduct
2
+
3
+ "go-bundler" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
4
+
5
+ * Participants will be tolerant of opposing views.
6
+ * Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
+ * When interpreting the words and actions of others, participants should always assume good intentions.
8
+ * Behaviour which can be reasonably considered harassment will not be tolerated.
9
+
10
+ If you have any concerns about behaviour within this project, please contact us at ["andrewnez@gmail.com"](mailto:"andrewnez@gmail.com").
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # go-bundler
2
+
3
+ **Do not use this.** It's a joke that happens to work.
4
+
5
+ Go-style imports for Ruby:
6
+
7
+ ```ruby
8
+ require "github.com/rack/rack"
9
+ require "github.com/rails/rails"
10
+ ```
11
+
12
+ Uses Go's module proxy to fetch Ruby gems. See [the blog post](https://nesbitt.io/2025/12/25/go-get-for-rubygems.html) for the full explanation.
13
+
14
+ ## Usage
15
+
16
+ Create a `Gofile` listing dependencies:
17
+
18
+ ```
19
+ github.com/rack/rack v3.1.8
20
+ github.com/sinatra/sinatra v4.1.1
21
+ ```
22
+
23
+ Then in your Ruby code:
24
+
25
+ ```ruby
26
+ require "go/bundler"
27
+
28
+ Go::Bundler.install("Gofile")
29
+
30
+ require "github.com/rack/rack"
31
+ require "github.com/sinatra/sinatra"
32
+ ```
33
+
34
+ Or fetch manually:
35
+
36
+ ```ruby
37
+ require "go/bundler"
38
+
39
+ Go::Bundler.fetch("github.com/rack/rack", "v3.1.8")
40
+
41
+ require "github.com/rack/rack"
42
+ require "github.com/rack/rack/request"
43
+ ```
44
+
45
+ ## Requirements
46
+
47
+ - Go installed and in PATH
48
+ - Ruby gems must have a `go.mod` file at repo root
49
+ - Versions come from git tags
50
+
51
+ ## How it works
52
+
53
+ Go's module proxy caches any repo with a `go.mod` file and logs its hash in a transparency log. It doesn't check that the repo contains Go code. This gem hooks Ruby's `require` to resolve `github.com/org/repo` paths to modules fetched via `go get`.
54
+
55
+ Modules land in `~/.go-bundler/pkg/mod/github.com/org/repo@version/`. The require hook finds the right `lib/` directory and loads from there.
56
+
57
+ Seriously, don't use this.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
data/example/Gofile ADDED
@@ -0,0 +1,5 @@
1
+ # Dependencies in github.com/org/repo version format
2
+ # These would need go.mod files in their repos to work
3
+
4
+ github.com/rack/rack v3.1.8
5
+ github.com/sinatra/sinatra v4.1.1
data/example/app.rb ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "go/bundler"
5
+
6
+ # Install dependencies from Gofile
7
+ Go::Bundler.install("Gofile")
8
+
9
+ # Now require using Go-style paths
10
+ require "github.com/rack/rack"
11
+ require "github.com/sinatra/sinatra"
12
+
13
+ class App < Sinatra::Base
14
+ get "/" do
15
+ "Hello from go-bundler!"
16
+ end
17
+ end
18
+
19
+ run App
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "go/bundler"
5
+
6
+ # Fetch a single module
7
+ Go::Bundler.fetch("github.com/rack/rack", "v3.1.8")
8
+
9
+ # Require using Go-style path
10
+ require "github.com/rack/rack"
11
+
12
+ puts "Rack version: #{Rack::VERSION}"
13
+
14
+ # You can also require submodules
15
+ require "github.com/rack/rack/request"
data/go.mod ADDED
@@ -0,0 +1,3 @@
1
+ module github.com/andrew/go-bundler
2
+
3
+ go 1.21
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Go
4
+ module Bundler
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
data/lib/go/bundler.rb ADDED
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bundler/version"
4
+
5
+ module Go
6
+ module Bundler
7
+ class Error < StandardError; end
8
+
9
+ class << self
10
+ def gopath
11
+ @gopath ||= File.join(Dir.home, ".go-bundler")
12
+ end
13
+
14
+ def gopath=(path)
15
+ @gopath = path
16
+ end
17
+
18
+ def mod_path
19
+ File.join(gopath, "pkg", "mod")
20
+ end
21
+
22
+ def fetch(module_path, version = nil)
23
+ spec = version ? "#{module_path}@#{version}" : module_path
24
+ env = { "GOPATH" => gopath }
25
+ system(env, "go", "get", spec, exception: true)
26
+ module_root(module_path, version)
27
+ end
28
+
29
+ def module_root(module_path, version = nil)
30
+ escaped = escape_path(module_path)
31
+ pattern = File.join(mod_path, escaped + (version ? "@#{version}" : "@*"))
32
+ dirs = Dir.glob(pattern).sort
33
+ dirs.last
34
+ end
35
+
36
+ def install(gofile_path = "Gofile")
37
+ deps = parse_gofile(gofile_path)
38
+ deps.each { |mod, ver| fetch(mod, ver) }
39
+ end
40
+
41
+ def parse_gofile(path)
42
+ return {} unless File.exist?(path)
43
+ deps = {}
44
+ File.readlines(path).each do |line|
45
+ line = line.strip
46
+ next if line.empty? || line.start_with?("#")
47
+ parts = line.split(/\s+/)
48
+ deps[parts[0]] = parts[1]
49
+ end
50
+ deps
51
+ end
52
+
53
+ def escape_path(path)
54
+ path.gsub(/[A-Z]/) { |c| "!#{c.downcase}" }
55
+ end
56
+
57
+ def resolve_require(path)
58
+ return nil unless path.include?("/")
59
+ parts = path.split("/")
60
+ return nil unless parts.length >= 3
61
+
62
+ # Try github.com/org/repo first
63
+ mod_path_candidate = parts[0, 3].join("/")
64
+ root = module_root(mod_path_candidate)
65
+ return nil unless root
66
+
67
+ gem_name = parts[2]
68
+ remainder = parts[3..]
69
+
70
+ if remainder.empty?
71
+ # require "github.com/rack/rack" -> look for lib/rack.rb
72
+ lib_dir = File.join(root, "lib")
73
+ main_file = File.join(lib_dir, "#{gem_name}.rb")
74
+ return main_file if File.exist?(main_file)
75
+ return lib_dir if File.directory?(lib_dir)
76
+ else
77
+ # require "github.com/rack/rack/request" -> lib/rack/request.rb
78
+ lib_path = File.join(root, "lib", gem_name, *remainder) + ".rb"
79
+ return lib_path if File.exist?(lib_path)
80
+ end
81
+ nil
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ module Kernel
88
+ alias_method :original_require, :require
89
+
90
+ def require(path)
91
+ if path.start_with?("github.com/", "gitlab.com/", "bitbucket.org/")
92
+ resolved = Go::Bundler.resolve_require(path)
93
+ if resolved
94
+ if File.directory?(resolved)
95
+ $LOAD_PATH.unshift(resolved) unless $LOAD_PATH.include?(resolved)
96
+ return original_require(File.basename(path))
97
+ else
98
+ return original_require(resolved)
99
+ end
100
+ end
101
+ end
102
+ original_require(path)
103
+ end
104
+ end
@@ -0,0 +1,6 @@
1
+ module Go
2
+ module Bundler
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: go-bundler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Nesbitt
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Use Go's module system to fetch Ruby gems with require "github.com/org/repo"
13
+ syntax
14
+ email:
15
+ - andrewnez@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - CODE_OF_CONDUCT.md
22
+ - README.md
23
+ - Rakefile
24
+ - example/Gofile
25
+ - example/app.rb
26
+ - example/manual_fetch.rb
27
+ - go.mod
28
+ - lib/go/bundler.rb
29
+ - lib/go/bundler/version.rb
30
+ - sig/go/bundler.rbs
31
+ homepage: https://github.com/andrew/go-bundler
32
+ licenses: []
33
+ metadata:
34
+ homepage_uri: https://github.com/andrew/go-bundler
35
+ source_code_uri: https://github.com/andrew/go-bundler
36
+ changelog_uri: https://github.com/andrew/go-bundler/blob/main/CHANGELOG.md
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.2.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 4.0.1
52
+ specification_version: 4
53
+ summary: Go-style imports for Ruby using Go's module proxy
54
+ test_files: []