Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7a88a90
[ruby/rubygems] Make DEFAULT_INSTALL_EXTENSION_IN_LIB false by defaul…
hsbt Jan 9, 2026
2233822
[ruby/rubygems] Add assertion for install_extension_in_lib default va…
Copilot Jan 9, 2026
be8f647
[Bug #21551] changing the exception to be isolationerror rather than …
AlexaCampusano Feb 12, 2026
d153586
Skip shipit benchmark because sassc is not working with artifact unde…
hsbt Feb 13, 2026
6dbece0
[ruby/stringio] [DOC] Japanese for multi-byte characters
BurdetteLamar Feb 13, 2026
5d39c1a
[ruby/rubygems] Add support for help flag in plugin commands
hsbt Jan 26, 2026
3dc8ce2
[ruby/rubygems] Add test for bundle help greet command
Copilot Jan 26, 2026
e064d0d
[ruby/rubygems] Raise error when gem contains capital letters
okuramasafumi Mar 27, 2022
0044087
[ruby/rubygems] Relax gem name validation to warn on capital letters
hsbt Feb 13, 2026
7e20c95
[ruby/rubygems] fix(bundler): only preload git sources for requested …
seuros Jan 5, 2026
5d40a6d
[ruby/rubygems] bin/rubocop -a --only Style/HashSyntax
hsbt Jan 26, 2026
27204dc
[ruby/rubygems] [rust gem] Make cargo test work by default
ianks Apr 25, 2024
54ffff0
[ruby/rubygems] Include debug symbols by default in release builds fo…
ianks May 31, 2024
c0d4f3e
[ruby/rubygems] Default to using rb_sys for Cargo gem builds
ianks May 31, 2024
e785e46
[ruby/rubygems] Improve testing config
ianks May 31, 2024
211c17d
[ruby/rubygems] Support precompiled rust gems out of the box
ianks May 31, 2024
fe25c2c
[ruby/rubygems] Remove hard coded constant
ianks May 31, 2024
e0ab415
[ruby/rubygems] update template dependencies
gjtorikian Feb 8, 2025
f94f284
[ruby/rubygems] remove outdated rubies
gjtorikian Feb 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ jobs:
matrix:
include:
# Using the same setup as ZJIT jobs
- bench_opts: '--warmup=1 --bench=1 --excludes=lobsters'
- bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,shipit'

runs-on: ubuntu-24.04

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ jobs:
include:
# Test --call-threshold=2 with 2 iterations in total
- ruby_opts: '--zjit-call-threshold=2'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,shipit'
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow

runs-on: macos-14
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ jobs:
include:
# Test --call-threshold=2 with 2 iterations in total
- ruby_opts: '--zjit-call-threshold=2'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters'
bench_opts: '--warmup=1 --bench=1 --excludes=lobsters,shipit'
configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow

runs-on: ubuntu-24.04
Expand Down
2 changes: 1 addition & 1 deletion bootstraptest/test_ractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ def ractor_local_globals
}

# given block Proc will be isolated, so can not access outer variables.
assert_equal 'ArgumentError', %q{
assert_equal 'Ractor::IsolationError', %q{
begin
a = true
r = Ractor.new do
Expand Down
4 changes: 2 additions & 2 deletions doc/language/ractor.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ This isolation occurs at Ractor creation time (when `Ractor.new` is called). If
begin
a = true
r = Ractor.new do
a #=> ArgumentError because this block accesses outer variable `a`.
a #=> Ractor::IsolationError because this block accesses outer variable `a`.
end
r.join # wait for ractor to finish
rescue ArgumentError
rescue Ractor::IsolationError
end
```

Expand Down
5 changes: 1 addition & 4 deletions doc/stringio/each_byte.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ returns +self+:
strio.each_byte {|byte| bytes.push(byte) }
strio.eof? # => true
bytes # => [104, 101, 108, 108, 111]
bytes = []
strio = StringIO.new('тест') # Four 2-byte characters.
strio.each_byte {|byte| bytes.push(byte) }
bytes # => [209, 130, 208, 181, 209, 129, 209, 130]

bytes = []
strio = StringIO.new('こんにちは') # Five 3-byte characters.
strio.each_byte {|byte| bytes.push(byte) }
Expand Down
5 changes: 1 addition & 4 deletions doc/stringio/each_char.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ returns +self+:
strio.each_char {|char| chars.push(char) }
strio.eof? # => true
chars # => ["h", "e", "l", "l", "o"]
chars = []
strio = StringIO.new('тест')
strio.each_char {|char| chars.push(char) }
chars # => ["т", "е", "с", "т"]

chars = []
strio = StringIO.new('こんにちは')
strio.each_char {|char| chars.push(char) }
Expand Down
5 changes: 1 addition & 4 deletions doc/stringio/each_codepoint.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ Each codepoint is the integer value for a character; returns self:
strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
strio.eof? # => true
codepoints # => [104, 101, 108, 108, 111]
codepoints = []
strio = StringIO.new('тест')
strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
codepoints # => [1090, 1077, 1089, 1090]

codepoints = []
strio = StringIO.new('こんにちは')
strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
Expand Down
7 changes: 0 additions & 7 deletions doc/stringio/getbyte.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ Returns +nil+ if at end-of-stream:

Returns a byte, not a character:

s = 'Привет'
s.bytes
# => [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
strio = StringIO.new(s)
strio.getbyte # => 208
strio.getbyte # => 159

s = 'こんにちは'
s.bytes
# => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175]
Expand Down
4 changes: 0 additions & 4 deletions doc/stringio/getc.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ Returns +nil+ if at end-of-stream:

Returns characters, not bytes:

strio = StringIO.new('Привет')
strio.getc # => "П"
strio.getc # => "р"

strio = StringIO.new('こんにちは')
strio.getc # => "こ"
strio.getc # => "ん"
Expand Down
16 changes: 8 additions & 8 deletions doc/stringio/gets.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ With no arguments given, reads a line using the default record separator
strio.eof? # => true
strio.gets # => nil

strio = StringIO.new('Привет') # Six 2-byte characters
strio = StringIO.new('こんにちは') # Five 3-byte characters.
strio.pos # => 0
strio.gets # => "Привет"
strio.pos # => 12
strio.gets # => "こんにちは"
strio.pos # => 15

<b>Argument +sep+</b>

Expand Down Expand Up @@ -67,11 +67,11 @@ but in other cases the position may be anywhere:

The position need not be at a character boundary:

strio = StringIO.new('Привет') # Six 2-byte characters.
strio.pos = 2 # At beginning of second character.
strio.gets # => "ривет"
strio.pos = 3 # In middle of second character.
strio.gets # => "\x80ивет"
strio = StringIO.new('こんにちは') # Five 3-byte characters.
strio.pos = 3 # At beginning of second character.
strio.gets # => "んにちは"
strio.pos = 4 # Within second character.
strio.gets # => "\x82\x93にちは"

<b>Special Record Separators</b>

Expand Down
1 change: 0 additions & 1 deletion doc/stringio/size.rdoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Returns the number of bytes in the string in +self+:

StringIO.new('hello').size # => 5 # Five 1-byte characters.
StringIO.new('тест').size # => 8 # Four 2-byte characters.
StringIO.new('こんにちは').size # => 15 # Five 3-byte characters.
16 changes: 9 additions & 7 deletions doc/stringio/stringio.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,15 @@ strio.pos = 24
strio.gets # => "Fourth line\n"
strio.pos # => 36

strio = StringIO.new('тест') # Four 2-byte characters.
strio.pos = 0 # At first byte of first character.
strio.read # => "тест"
strio.pos = 1 # At second byte of first character.
strio.read # => "\x82ест"
strio.pos = 2 # At first of second character.
strio.read # => "ест"
strio = StringIO.new('こんにちは') # Five 3-byte characters.
strio.pos = 0 # At first byte of first character.
strio.read # => "こんにちは"
strio.pos = 1 # At second byte of first character.
strio.read # => "\x81\x93んにちは"
strio.pos = 2 # At third byte of first character.
strio.read # => "\x93んにちは"
strio.pos = 3 # At first byte of second character.
strio.read # => "んにちは"

strio = StringIO.new(TEXT)
strio.pos = 15
Expand Down
4 changes: 4 additions & 0 deletions lib/bundler/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ def self.default_command(meth = nil)
def help(cli = nil)
cli = self.class.all_aliases[cli] if self.class.all_aliases[cli]

if Bundler.settings[:plugins] && Bundler::Plugin.command?(cli) && !self.class.all_commands.key?(cli)
return Bundler::Plugin.exec_command(cli, ["--help"])
end

case cli
when "gemfile" then command = "gemfile"
when nil then command = "bundle"
Expand Down
8 changes: 8 additions & 0 deletions lib/bundler/cli/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def run
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
if extension == "rust"
templates.merge!("github/workflows/build-gems.yml.tt" => ".github/workflows/build-gems.yml")
end
config[:ignore_paths] << ".github/"
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
Expand Down Expand Up @@ -228,6 +231,7 @@ def run
templates.merge!(
"Cargo.toml.tt" => "Cargo.toml",
"ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
"ext/newgem/build.rs.tt" => "ext/#{name}/build.rs",
"ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
)
Expand Down Expand Up @@ -435,6 +439,10 @@ def ensure_safe_gem_name(name, constant_array)
exit 1
end

if /[A-Z]/.match?(name)
Bundler.ui.warn "Gem names with capital letters are not recommended. Please use only lowercase letters, numbers, and hyphens."
end

constant_name = constant_array.join("::")

existing_constant = constant_array.inject(Object) do |c, s|
Expand Down
19 changes: 16 additions & 3 deletions lib/bundler/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1125,11 +1125,24 @@ def preload_git_source_worker
end

def preload_git_sources
sources.git_sources.each {|source| preload_git_source_worker.enq(source) }
needed_git_sources.each {|source| preload_git_source_worker.enq(source) }
ensure
preload_git_source_worker.stop
end

# Git sources needed for the requested groups (excludes sources only used by --without groups)
def needed_git_sources
needed_deps = dependencies_for(requested_groups)
sources.git_sources.select do |source|
needed_deps.any? {|d| d.source == source }
end
end

# Git sources that should be excluded (only used by --without groups)
def excluded_git_sources
sources.git_sources - needed_git_sources
end

def find_source_requirements
if Gem.ruby_version >= Gem::Version.new("3.3")
# Ruby 3.2 has a bug that incorrectly triggers a circular dependency warning. This version will continue to
Expand All @@ -1141,10 +1154,10 @@ def find_source_requirements
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
all_requirements = source_map.all_requirements
all_requirements = source_map.all_requirements(excluded_git_sources)
{ default: default_source }.merge(all_requirements)
else
{ default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
{ default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements)
end
source_requirements.merge!(source_map.locked_requirements) if nothing_changed?
metadata_dependencies.each do |dep|
Expand Down
5 changes: 4 additions & 1 deletion lib/bundler/source/rubygems_aggregate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ class Source
class RubygemsAggregate
attr_reader :source_map, :sources

def initialize(sources, source_map)
def initialize(sources, source_map, excluded_sources = [])
@sources = sources
@source_map = source_map
@excluded_sources = excluded_sources

@index = build_index
end
Expand All @@ -31,6 +32,8 @@ def build_index
dependency_names = source_map.pinned_spec_names

sources.all_sources.each do |source|
next if @excluded_sources.include?(source)

source.dependency_names = dependency_names - source_map.pinned_spec_names(source)
idx.add_source source.specs
dependency_names.concat(source.unmet_deps).uniq!
Expand Down
8 changes: 6 additions & 2 deletions lib/bundler/source_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ def pinned_spec_names(skip = nil)
direct_requirements.reject {|_, source| source == skip }.keys
end

def all_requirements
def all_requirements(excluded_sources = [])
requirements = direct_requirements.dup

unmet_deps = sources.non_default_explicit_sources.map do |source|
explicit_sources = sources.non_default_explicit_sources.reject do |source|
excluded_sources.include?(source)
end

unmet_deps = explicit_sources.map do |source|
(source.spec_names - pinned_spec_names).each do |indirect_dependency_name|
previous_source = requirements[indirect_dependency_name]
if previous_source.nil?
Expand Down
6 changes: 6 additions & 0 deletions lib/bundler/templates/newgem/Cargo.toml.tt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@
[workspace]
members = ["./ext/<%= config[:name] %>"]
resolver = "2"

[profile.release]
# By default, debug symbols are stripped from the final binary which makes it
# harder to debug if something goes wrong. It's recommended to keep debug
# symbols in the release build so that you can debug the final binary if needed.
debug = true
7 changes: 7 additions & 0 deletions lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ crate-type = ["cdylib"]

[dependencies]
magnus = { version = "0.8.2" }
rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }

[build-dependencies]
rb-sys-env = "0.2.2"

[dev-dependencies]
rb-sys-test-helpers = { version = "0.2.2" }
5 changes: 5 additions & 0 deletions lib/bundler/templates/newgem/ext/newgem/build.rs.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = rb_sys_env::activate()?;

Ok(())
}
15 changes: 13 additions & 2 deletions lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use magnus::{function, prelude::*, Error, Ruby};

fn hello(subject: String) -> String {
format!("Hello from Rust, {subject}!")
pub fn hello(subject: String) -> String {
format!("Hello {subject}, from Rust!")
}

#[magnus::init]
Expand All @@ -10,3 +10,14 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
module.define_singleton_method("hello", function!(hello, 1))?;
Ok(())
}

#[cfg(test)]
mod tests {
use rb_sys_test_helpers::ruby_test;
use super::hello;

#[ruby_test]
fn test_hello() {
assert_eq!("Hello world, from Rust!", hello("world".to_string()));
}
}
Loading