Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
249eef4
ZJIT: Shrink Opnd from 24 bytes to 16
tekknolagi Jun 30, 2026
11c59ab
ZJIT: Box PatchPoint data
tekknolagi Jun 30, 2026
fe64d6c
ZJIT: Fit rest of CCall data into CCallData
tekknolagi Jun 30, 2026
420b2ef
ZJIT: Box all big Target data
tekknolagi Jun 30, 2026
d88016d
[DOC] Update Set#divide documentation
BurdetteLamar Jun 30, 2026
f081603
Add opt-in per-example runtime log for the bundler specs
hsbt Jun 30, 2026
67933ea
Write newgem global git config in-process instead of shelling out
hsbt Jun 30, 2026
b64785a
Lock instead of install where lock_spec only asserts the lockfile
hsbt Jun 30, 2026
ce50913
Merge consecutive update_repo2 blocks in outdated_spec
hsbt Jun 30, 2026
2f308ae
Reduce redundant subprocesses in add_spec
hsbt Jun 30, 2026
462f93a
Lock instead of install in lockfile-only specific_platform examples
hsbt Jun 30, 2026
4ba819f
Skip the install in major_deprecation flag-removal examples
hsbt Jun 30, 2026
ee34298
Trim redundant subprocesses in path_spec
hsbt Jun 30, 2026
ce4b381
Lock instead of install in lockfile-format examples
hsbt Jun 30, 2026
4a3254a
Drop a duplicated include_gems assertion in dependency_api_spec
hsbt Jun 30, 2026
99802b0
Rebalance the spec shards by measured runtime
hsbt Jun 30, 2026
597dcfb
[DOC] Update Set#keep_if documentation
BurdetteLamar Jun 30, 2026
29e4bf1
[DOC] Update Set#reject! documentation
BurdetteLamar Jun 30, 2026
c5eb119
[DOC] Update Set#hash documentation
BurdetteLamar Jul 1, 2026
1a8320b
[DOC] Update Set#merge documentation
BurdetteLamar Jul 1, 2026
298791a
[DOC] Update Set#proper_subset? documentation
BurdetteLamar Jul 1, 2026
54eead3
[DOC] Update Set#proper_superset? documentation
BurdetteLamar Jul 1, 2026
a39081e
[DOC] Update Set#replace documentation
BurdetteLamar Jul 1, 2026
a86a1a7
[DOC] Update Set#reset documentation
BurdetteLamar Jul 1, 2026
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
167 changes: 124 additions & 43 deletions set.c
Original file line number Diff line number Diff line change
Expand Up @@ -858,11 +858,19 @@ set_i_delete_if(VALUE set)

/*
* call-seq:
* reject! { |o| ... } -> self
* reject! {|element| ... } -> self or nil
* reject! -> enumerator
*
* Equivalent to Set#delete_if, but returns nil if no changes were made.
* Returns an enumerator if no block is given.
* With a block given, like #delete_if, but returns +nil+ if no changes were made:
*
* set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* set.reject! {|element| element.even? } # => Set[1, 3, 5, 7, 9]
* set.reject! {|element| element.even? } # => nil
* set.reject! {|element| element.odd? } # => Set[]
*
* With no block given, returns an Enumerator.
*
* Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
*/
static VALUE
set_i_reject(VALUE set)
Expand Down Expand Up @@ -999,26 +1007,48 @@ static void set_merge_enum_into(VALUE set, VALUE arg);

/*
* call-seq:
* divide { |o1, o2| ... } -> set
* divide { |o| ... } -> set
* divide {|ele| ... } -> new_set
* divide {|ele0, ele1| ... } -> new_set
* divide -> enumerator
*
* Divides the set into a set of subsets according to the commonality
* defined by the given block.
* With a block given, returns a set of sets.
*
* If the arity of the block is 2, elements o1 and o2 are in common
* if both block.call(o1, o2) and block.call(o2, o1) are true.
* Otherwise, elements o1 and o2 are in common if
* block.call(o1) == block.call(o2).
* For a block that accepts one argument,
* calls the block with each element;
* creates a set for each distinct block return value:
*
* numbers = Set[1, 3, 4, 6, 9, 10, 11]
* set = numbers.divide { |i,j| (i - j).abs == 1 }
* set #=> Set[Set[1],
* # Set[3, 4],
* # Set[6],
* # Set[9, 10, 11]]
* set = Set[*0..9]
* # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* # Divide into mod 3 sets.
* set.divide {|ele| ele % 3 }
* # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
* # Divide into mod 5 sets.
* set.divide {|ele| ele % 5 }
* # => Set[Set[0, 5], Set[1, 6], Set[2, 7], Set[3, 8], Set[4, 9]]
*
* Returns an enumerator if no block is given.
* Set[0].divide {|ele| anything } # => Set[Set[0]]
* Set[].divide {|ele| not called } # => Set[]
*
* For a block that accepts two arguments,
* divides +self+ into connected components based on the binary
* relation defined by the block, calling the block with each 2-element
* permutation of the elements of +self+:
*
* set = Set[*0..9]
* # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* # Divide into mod 2 sets.
* set.divide {|i, j| (i - j) % 2 == 0 }
* # => Set[Set[0, 2, 4, 6, 8], Set[1, 3, 5, 7, 9]]
* # Divide into mod 3 sets.
* set.divide {|i, j| (i - j) % 3 == 0 }
* # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
*
* Set[0].divide {|i, j| not called } # => Set[Set[0]]
* Set[].divide {|i, j| not called } # => Set[]
*
* With no block given, returns an Enumerator.
*
* Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
*/
static VALUE
set_i_divide(VALUE set)
Expand Down Expand Up @@ -1216,10 +1246,17 @@ set_merge_enum_into(VALUE set, VALUE arg)

/*
* call-seq:
* merge(*enums, **nil) -> self
* merge(*enumerables, **nil) -> self
*
* Adds each element of each of the given +enumerables+ to +self+;
* returns +self+:
*
* set = Set[*0..2] # => Set[0, 1, 2]
* set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
* set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
*
* Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
*
* Merges the elements of the given enumerable objects to the set and
* returns self.
*/
static VALUE
set_i_merge(int argc, VALUE *argv, VALUE set)
Expand Down Expand Up @@ -1545,11 +1582,21 @@ set_keep_if_i(st_data_t key, st_data_t into)

/*
* call-seq:
* keep_if { |o| ... } -> self
* keep_if {|element| ... } -> self
* keep_if -> enumerator
*
* Deletes every element of the set for which block evaluates to false, and
* returns self. Returns an enumerator if no block is given.
* With a block given,
* calls the block with each element in +self+,
* deleting the element if the block returns +false+ or +nil+;
* returns +self+:
*
* set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* set.keep_if {|i| i.even? } # => Set[0, 2, 4, 6, 8]
* set.keep_if {|i| i.odd? } # => Set[]
*
* With no block given, returns an Enumerator.
*
* Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
*/
static VALUE
set_i_keep_if(VALUE set)
Expand Down Expand Up @@ -1585,14 +1632,15 @@ set_i_select(VALUE set)

/*
* call-seq:
* replace(enum) -> self
* replace(enumerable) -> self
*
* Replaces the contents +self+ with the contents of the given +enumerable+;
* returns +self+:
*
* Replaces the contents of the set with the contents of the given
* enumerable object and returns self.
* set = Set[1, 'c', :s] # => Set[1, "c", :s]
* set.replace([1, 2]) # => Set[1, 2]
*
* set = Set[1, 'c', :s] #=> Set[1, "c", :s]
* set.replace([1, 2]) #=> Set[1, 2]
* set #=> Set[1, 2]
* Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
*/
static VALUE
set_i_replace(VALUE set, VALUE other)
Expand Down Expand Up @@ -1621,8 +1669,22 @@ set_i_replace(VALUE set, VALUE other)
* call-seq:
* reset -> self
*
* Resets the internal state after modification to existing elements
* and returns self. Elements will be reindexed and deduplicated.
* Resets the internal state of +self+; return +self+.
*
* A set relies on the #hash results of each element being consistent.
* Modifying an element in a way that changes the results of #hash
* may allow duplicate elements in the set:
*
* array = [1]
* set = Set[array] # => Set[[1]]
* array << 2
* set.add(array) # => Set[[1, 2], [1, 2]]
*
* Calling #reset will recalculate all of the hash values and remove
* duplicate elements:
*
* set.reset # => Set[[1, 2]]
*
*/
static VALUE
set_i_reset(VALUE set)
Expand Down Expand Up @@ -1763,9 +1825,17 @@ set_le(VALUE set, VALUE other)

/*
* call-seq:
* proper_subset?(set) -> true or false
* proper_subset?(other_set) -> true or false
*
* Returns whether +self+ is
* a {proper subset}[https://en.wikipedia.org/wiki/Subset]
* of the given +other_set+:
*
* set = Set[*'b'..'e']
* set.proper_subset?(set) # => false
* set.proper_subset?(Set[*'a'..'f']) # => true
*
* Returns true if the set is a proper subset of the given set.
* Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
*/
static VALUE
set_i_proper_subset(VALUE set, VALUE other)
Expand All @@ -1791,9 +1861,17 @@ set_i_subset(VALUE set, VALUE other)

/*
* call-seq:
* proper_superset?(set) -> true or false
* proper_superset?(other_set) -> true or false
*
* Returns true if the set is a proper superset of the given set.
* Returns whether +self+ is
* a {proper superset}[https://en.wikipedia.org/wiki/Subset]
* of the given +other_set+:
*
* set = Set[*'a'..'f']
* set.proper_superset?(set) # => false
* set.proper_superset?(Set[*'b'..'e']) # => true
*
* Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
*/
static VALUE
set_i_proper_superset(VALUE set, VALUE other)
Expand Down Expand Up @@ -2015,7 +2093,12 @@ set_hash_i(st_data_t item, st_data_t(arg))
* call-seq:
* hash -> integer
*
* Returns hash code for set.
* Returns the integer hash value for +self+.
*
* Two sets with the same content have the same hash value.
*
* Set[0, 1].hash == Set[1, 0].hash # => true
* Set[0, 1].hash == Set[0].hash # => false
*/
static VALUE
set_i_hash(VALUE set)
Expand Down Expand Up @@ -2334,15 +2417,13 @@ rb_set_size(VALUE set)
* === Methods for Converting
*
* - #classify:
* Returns a hash that classifies the elements,
* Returns a hash that partitions the elements,
* as determined by the given block.
* - #collect! (aliased as #map!):
* Replaces each element with a block return-value.
* - #divide:
* Returns a hash that classifies the elements,
* as determined by the given block;
* differs from #classify in that the block may accept
* either one or two arguments.
* Returns a set of sets that partition the elements,
* as determined by the given block.
* - #flatten:
* Returns a new set that is a recursive flattening of +self+.
* - #flatten!:
Expand All @@ -2367,7 +2448,7 @@ rb_set_size(VALUE set)
* === Other Methods
*
* - #reset:
* Resets the internal state; useful if an object
* Resets the internal state; useful if an element
* has been modified while an element in the set.
*
*/
Expand Down
16 changes: 9 additions & 7 deletions spec/bundler/commands/add_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,10 @@

describe "with --git and --ref" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))}"
ref = revision_for(lib_path("foo-2.0"))
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{ref}"

expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}"/)
expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{ref}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
Expand Down Expand Up @@ -229,9 +230,10 @@

describe "with --git and --ref and --glob" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))} --glob='./*.gemspec'"
ref = revision_for(lib_path("foo-2.0"))
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{ref} --glob='./*.gemspec'"

expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}", glob: "\./\*\.gemspec"})
expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{ref}", glob: "\./\*\.gemspec"})
expect(the_bundle).to include_gems "foo 2.0"
end
end
Expand Down Expand Up @@ -397,7 +399,7 @@ def run

describe "when a gem is added which is already specified in Gemfile with version" do
it "shows an error when added with different version requirement" do
install_gemfile <<-G
lock_gemfile <<-G
source "https://gem.repo2"
gem "myrack", "1.0"
G
Expand All @@ -409,7 +411,7 @@ def run
end

it "shows error when added without version requirements" do
install_gemfile <<-G
lock_gemfile <<-G
source "https://gem.repo2"
gem "myrack", "1.0"
G
Expand All @@ -424,7 +426,7 @@ def run

describe "when a gem is added which is already specified in Gemfile without version" do
it "shows an error when added with different version requirement" do
install_gemfile <<-G
lock_gemfile <<-G
source "https://gem.repo2"
gem "myrack"
G
Expand Down
7 changes: 4 additions & 3 deletions spec/bundler/commands/lock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,9 @@
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end

# establish a lockfile set to 1.4.3
install_gemfile <<-G
# establish a lockfile set to 1.4.3 (these examples only assert on the
# generated lockfile, so resolve-and-lock without the install)
lock_gemfile <<-G
source "https://gem.repo4"
gem 'foo', '1.4.3'
gem 'bar', '2.0.3'
Expand Down Expand Up @@ -988,7 +989,7 @@
end

simulate_platform "x86_64-darwin-22" do
install_gemfile <<~G
lock_gemfile <<~G
source "https://gem.repo4"

gem "nokogiri"
Expand Down
13 changes: 10 additions & 3 deletions spec/bundler/commands/newgem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,16 @@ def installed_go?
let(:gem_name) { "mygem" }

before do
git("config --global user.name 'Bundler User'")
git("config --global user.email user@example.com")
git("config --global github.user bundleuser")
# Write the global git config directly instead of shelling out to `git
# config --global` three times per example: this `before` runs for every
# example in the file, and each `git` call is a separate subprocess.
File.write(home(".gitconfig"), <<~GITCONFIG)
[user]
name = Bundler User
email = user@example.com
[github]
user = bundleuser
GITCONFIG

bundle_config_global "gem.mit false"
bundle_config_global "gem.test false"
Expand Down
12 changes: 8 additions & 4 deletions spec/bundler/commands/outdated_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@
end
G

update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
update_repo2 do
build_gem "activesupport", "3.0"
build_gem "terranova", "9"
end

bundle "outdated", raise_on_error: false

Expand Down Expand Up @@ -122,8 +124,10 @@
it "shows the location of the latest version's gemspec if installed" do
bundle_config "clean false"

update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
update_repo2 do
build_gem "activesupport", "3.0"
build_gem "terranova", "9"
end

install_gemfile <<-G
source "https://gem.repo2"
Expand Down
4 changes: 1 addition & 3 deletions spec/bundler/install/gemfile/path_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@
s.add_dependency "graphql", "~> 2.0"
end

bundle "install", dir: lib_path("foo")
bundle "lock", dir: lib_path("foo")
expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
end

Expand Down Expand Up @@ -512,8 +512,6 @@
G

expect(the_bundle).to include_gems "foo 1.0"

expect(the_bundle).to include_gems "foo 1.0"
end

it "works when the path does not have a gemspec but there is a lockfile" do
Expand Down
Loading