ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings
@ 2025-06-01  9:42 lovro-bikic via ruby-core
  2025-06-01 13:02 ` [ruby-core:122360] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: lovro-bikic via ruby-core @ 2025-06-01  9:42 UTC (permalink / raw)
  To: ruby-core; +Cc: lovro-bikic

Issue #21391 has been reported by lovro-bikic (Lovro Bikić).

----------------------------------------
Bug #21391: Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings
https://bugs.ruby-lang.org/issues/21391

* Author: lovro-bikic (Lovro Bikić)
* Status: Open
* ruby -v: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin23]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
File.join('/usr', '')
# => "/usr/"

Pathname.new('/usr').join('').to_s
# => "/usr" # no trailing slash

File.join('/usr', ' ')
# => "/usr/ "

Pathname.new('/usr').join(' ').to_s
# => "/usr/ "
```

`File.join` with an empty string adds a trailing slash, `Pathname#join` doesn't.
When `Pathname#join` argument is a string with empty whitespace, a trailing slash is added (plus whitespace).

I think it's a common use-case to append a trailing slash to `Pathname`, and currently you have to resort to other methods such as string interpolation (e.g. in Rails, `"#{Rails.root}/"`) or `File.join` (e.g. `File.join(Rails.root, '')`).

In other popular languages, both approaches have been taken:
- [`os.path.join` in Python](https://docs.python.org/3.12/library/os.path.html#os.path.join) adds a trailing slash:
```python
import os
os.path.join('/usr', '')
# '/usr/'
```
- [Path.join in Rust](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) adds a trailing slash:
```rust
use std::path::{Path};

fn main() {
    println!("{}", Path::new("/usr").join("").display());
    // prints "/usr/"
}
```
- [path.join in Node](https://nodejs.org/api/path.html#pathjoinpaths) doesn't add a trailing slash:
```js
const path = require('path');

path.join('/usr', '');
// '/usr'
```
- [filepath.Join in Go](https://pkg.go.dev/path/filepath#Join) doesn't add a trailing slash:
```go
package main

import ("fmt"; "path/filepath")

func main() {
  fmt.Println(filepath.Join("/usr", ""))
  // prints "/usr"
}
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:122360] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
  2025-06-01  9:42 [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings lovro-bikic via ruby-core
@ 2025-06-01 13:02 ` Dan0042 (Daniel DeLorme) via ruby-core
  2025-06-01 14:03 ` [ruby-core:122361] " lovro-bikic via ruby-core
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2025-06-01 13:02 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #21391 has been updated by Dan0042 (Daniel DeLorme).


It's not the only inconsistent behavior:
```ruby
File.join("/usr","/var")               #=> "/usr/var"
Pathname.new("/usr").join("/var").to_s #=> "/var"

File.join("/usr","../var")               #=> "/usr/../var"
Pathname.new("/usr").join("../var").to_s #=> "/var"

File.join("/usr","/../var")               #=> "/usr/../var"
Pathname.new("/usr").join("/../var").to_s #=> "/../var"
```

`File.join` simply joins two strings together with a separator, whereas `Pathname#join` is a logical operation on two paths. It's normal for there to be differences.

That being said, I feel that `Pathname.new('/usr').join('')` is a nonsensical operation. It seems to result in a no-op, but it might be better to warn or raise an error.

----------------------------------------
Bug #21391: Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
https://bugs.ruby-lang.org/issues/21391#change-113503

* Author: lovro-bikic (Lovro Bikić)
* Status: Open
* ruby -v: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin23]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
File.join('/usr', '')
# => "/usr/"

Pathname.new('/usr').join('').to_s
# => "/usr" # no trailing slash

File.join('/usr', ' ')
# => "/usr/ "

Pathname.new('/usr').join(' ').to_s
# => "/usr/ "
```

`File.join` with an empty string adds a trailing slash, `Pathname#join` doesn't.
When `Pathname#join` argument is a string with empty whitespace, a trailing slash is added (plus whitespace).

I think it's a common use-case to append a trailing slash to `Pathname`, and currently you have to resort to other methods such as string interpolation (e.g. in Rails, `"#{Rails.root}/"`) or `File.join` (e.g. `File.join(Rails.root, '')`).

In other popular languages, both approaches have been taken:
- [`os.path.join` in Python](https://docs.python.org/3.12/library/os.path.html#os.path.join) adds a trailing slash:
```python
import os
os.path.join('/usr', '')
# '/usr/'
```
- [Path.join in Rust](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) adds a trailing slash:
```rust
use std::path::{Path};

fn main() {
    println!("{}", Path::new("/usr").join("").display());
    // prints "/usr/"
}
```
- [path.join in Node](https://nodejs.org/api/path.html#pathjoinpaths) doesn't add a trailing slash:
```js
const path = require('path');

path.join('/usr', '');
// '/usr'
```
- [filepath.Join in Go](https://pkg.go.dev/path/filepath#Join) doesn't add a trailing slash:
```go
package main

import ("fmt"; "path/filepath")

func main() {
  fmt.Println(filepath.Join("/usr", ""))
  // prints "/usr"
}
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:122361] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
  2025-06-01  9:42 [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings lovro-bikic via ruby-core
  2025-06-01 13:02 ` [ruby-core:122360] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join " Dan0042 (Daniel DeLorme) via ruby-core
@ 2025-06-01 14:03 ` lovro-bikic via ruby-core
  2025-06-02 15:26 ` [ruby-core:122381] " Dan0042 (Daniel DeLorme) via ruby-core
  2025-06-05 10:13 ` [ruby-core:122448] " akr (Akira Tanaka) via ruby-core
  3 siblings, 0 replies; 5+ messages in thread
From: lovro-bikic via ruby-core @ 2025-06-01 14:03 UTC (permalink / raw)
  To: ruby-core; +Cc: lovro-bikic

Issue #21391 has been updated by lovro-bikic (Lovro Bikić).


Dan0042 (Daniel DeLorme) wrote in #note-2:
> It's not the only inconsistent behavior: (absolute and relative paths example)

To clarify, I don't expect the result of the two to be equivalent in all cases, it's clearly documented what each method does.

What I am reporting is that there's undocumented and possibly inconsistent behavior when it comes to empty strings. Furthermore, the example with a whitespace string shows that `Pathname#join` is capable of adding trailing slashes under certain conditions.

[`File.join` behavior has been tested for empty strings](https://github.com/ruby/spec/blob/f3a071a0fea213c6e909e253e43144a8c49483c6/core/file/join_spec.rb#L67-L101), but [`Pathname#join` hasn't](https://github.com/ruby/spec/blob/f3a071a0fea213c6e909e253e43144a8c49483c6/library/pathname/join_spec.rb), so it's unclear if this is a bug or an expected difference in behavior.

Dan0042 (Daniel DeLorme) wrote in #note-2:
> That being said, I feel that Pathname.new('/usr').join('') is a nonsensical operation. It seems to result in a no-op, but it might be better to warn or raise an error.

Perhaps, [but it's already a common pattern with `File.join`](https://github.com/search?q=%2FFile%5C.join%5C(%5B%5Cw._()%5D%2B%2C%20(%22%22%7C%27%27)%5C)%2F%20lang%3Aruby&type=code). Whether it's a nonsensical operation is up for debate, but I think there should be a clear way for `Pathname#join` to allow appending trailing slashes to pathnames.

----------------------------------------
Bug #21391: Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
https://bugs.ruby-lang.org/issues/21391#change-113504

* Author: lovro-bikic (Lovro Bikić)
* Status: Open
* ruby -v: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin23]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
File.join('/usr', '')
# => "/usr/"

Pathname.new('/usr').join('').to_s
# => "/usr" # no trailing slash

File.join('/usr', ' ')
# => "/usr/ "

Pathname.new('/usr').join(' ').to_s
# => "/usr/ "
```

`File.join` with an empty string adds a trailing slash, `Pathname#join` doesn't.
When `Pathname#join` argument is a string with empty whitespace, a trailing slash is added (plus whitespace).

I think it's a common use-case to append a trailing slash to `Pathname`, and currently you have to resort to other methods such as string interpolation (e.g. in Rails, `"#{Rails.root}/"`) or `File.join` (e.g. `File.join(Rails.root, '')`).

In other popular languages, both approaches have been taken:
- [`os.path.join` in Python](https://docs.python.org/3.12/library/os.path.html#os.path.join) adds a trailing slash:
```python
import os
os.path.join('/usr', '')
# '/usr/'
```
- [Path.join in Rust](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) adds a trailing slash:
```rust
use std::path::{Path};

fn main() {
    println!("{}", Path::new("/usr").join("").display());
    // prints "/usr/"
}
```
- [path.join in Node](https://nodejs.org/api/path.html#pathjoinpaths) doesn't add a trailing slash:
```js
const path = require('path');

path.join('/usr', '');
// '/usr'
```
- [filepath.Join in Go](https://pkg.go.dev/path/filepath#Join) doesn't add a trailing slash:
```go
package main

import ("fmt"; "path/filepath")

func main() {
  fmt.Println(filepath.Join("/usr", ""))
  // prints "/usr"
}
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:122381] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
  2025-06-01  9:42 [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings lovro-bikic via ruby-core
  2025-06-01 13:02 ` [ruby-core:122360] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join " Dan0042 (Daniel DeLorme) via ruby-core
  2025-06-01 14:03 ` [ruby-core:122361] " lovro-bikic via ruby-core
@ 2025-06-02 15:26 ` Dan0042 (Daniel DeLorme) via ruby-core
  2025-06-05 10:13 ` [ruby-core:122448] " akr (Akira Tanaka) via ruby-core
  3 siblings, 0 replies; 5+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2025-06-02 15:26 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #21391 has been updated by Dan0042 (Daniel DeLorme).


Dan0042 (Daniel DeLorme) wrote in #note-2:
> That being said, I feel that `Pathname.new('/usr').join('')` is a nonsensical operation. It seems to result in a no-op, but it might be better to warn or raise an error.

Ah, looks like I might have to retract that statement. In bash, `cd ""` is a no-op
```
cd /usr
cd ""
pwd  #=> /usr
```
So `Pathname#join`, which is equivalent to `cd`, has the same behavior. Not sure it makes sense, but at least it's consistent.
Although I should note that `Dir.chdir("")` raises an error.

lovro-bikic (Lovro Bikić) wrote in #note-3:
> Furthermore, the example with a whitespace string shows that `Pathname#join` is capable of adding trailing slashes under certain conditions.

That's not a trailing slash, that the file/directory " " inside "usr". But it's true that if you do `.join("a/")` the resulting Pathname has a trailing slash, so indeed `Pathname#join` is capable of adding trailing slashes under certain conditions.

> I think there should be a clear way for `Pathname#join` to allow appending trailing slashes to pathnames.

Perhaps, but personally I don't think that joining with an empty string should be it.

----------------------------------------
Bug #21391: Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
https://bugs.ruby-lang.org/issues/21391#change-113527

* Author: lovro-bikic (Lovro Bikić)
* Status: Open
* ruby -v: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin23]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
File.join('/usr', '')
# => "/usr/"

Pathname.new('/usr').join('').to_s
# => "/usr" # no trailing slash

File.join('/usr', ' ')
# => "/usr/ "

Pathname.new('/usr').join(' ').to_s
# => "/usr/ "
```

`File.join` with an empty string adds a trailing slash, `Pathname#join` doesn't.
When `Pathname#join` argument is a string with empty whitespace, a trailing slash is added (plus whitespace).

I think it's a common use-case to append a trailing slash to `Pathname`, and currently you have to resort to other methods such as string interpolation (e.g. in Rails, `"#{Rails.root}/"`) or `File.join` (e.g. `File.join(Rails.root, '')`).

In other popular languages, both approaches have been taken:
- [`os.path.join` in Python](https://docs.python.org/3.12/library/os.path.html#os.path.join) adds a trailing slash:
```python
import os
os.path.join('/usr', '')
# '/usr/'
```
- [Path.join in Rust](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) adds a trailing slash:
```rust
use std::path::{Path};

fn main() {
    println!("{}", Path::new("/usr").join("").display());
    // prints "/usr/"
}
```
- [path.join in Node](https://nodejs.org/api/path.html#pathjoinpaths) doesn't add a trailing slash:
```js
const path = require('path');

path.join('/usr', '');
// '/usr'
```
- [filepath.Join in Go](https://pkg.go.dev/path/filepath#Join) doesn't add a trailing slash:
```go
package main

import ("fmt"; "path/filepath")

func main() {
  fmt.Println(filepath.Join("/usr", ""))
  // prints "/usr"
}
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:122448] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
  2025-06-01  9:42 [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings lovro-bikic via ruby-core
                   ` (2 preceding siblings ...)
  2025-06-02 15:26 ` [ruby-core:122381] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2025-06-05 10:13 ` akr (Akira Tanaka) via ruby-core
  3 siblings, 0 replies; 5+ messages in thread
From: akr (Akira Tanaka) via ruby-core @ 2025-06-05 10:13 UTC (permalink / raw)
  To: ruby-core; +Cc: akr (Akira Tanaka)

Issue #21391 has been updated by akr (Akira Tanaka).


I don't recommend trailing slash on a pathname because it is not portable between operating systems.

The behavior of Pathname (it doesn't add a trailing slash) reflects this my opinion.

https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html

| Two types of implementation have been prevalent; those that ignored trailing <slash> characters on all pathnames regardless, and those that permitted them only on existing directories.

It seems the standard tries to fix this situation, though.

----------------------------------------
Bug #21391: Inconsistent trailing slash behavior of File.join and Pathname#join with empty strings
https://bugs.ruby-lang.org/issues/21391#change-113629

* Author: lovro-bikic (Lovro Bikić)
* Status: Open
* ruby -v: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-darwin23]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
File.join('/usr', '')
# => "/usr/"

Pathname.new('/usr').join('').to_s
# => "/usr" # no trailing slash

File.join('/usr', ' ')
# => "/usr/ "

Pathname.new('/usr').join(' ').to_s
# => "/usr/ "
```

`File.join` with an empty string adds a trailing slash, `Pathname#join` doesn't.
When `Pathname#join` argument is a string with empty whitespace, a trailing slash is added (plus whitespace).

I think it's a common use-case to append a trailing slash to `Pathname`, and currently you have to resort to other methods such as string interpolation (e.g. in Rails, `"#{Rails.root}/"`) or `File.join` (e.g. `File.join(Rails.root, '')`).

In other popular languages, both approaches have been taken:
- [`os.path.join` in Python](https://docs.python.org/3.12/library/os.path.html#os.path.join) adds a trailing slash:
```python
import os
os.path.join('/usr', '')
# '/usr/'
```
- [Path.join in Rust](https://doc.rust-lang.org/std/path/struct.Path.html#method.join) adds a trailing slash:
```rust
use std::path::{Path};

fn main() {
    println!("{}", Path::new("/usr").join("").display());
    // prints "/usr/"
}
```
- [path.join in Node](https://nodejs.org/api/path.html#pathjoinpaths) doesn't add a trailing slash:
```js
const path = require('path');

path.join('/usr', '');
// '/usr'
```
- [filepath.Join in Go](https://pkg.go.dev/path/filepath#Join) doesn't add a trailing slash:
```go
package main

import ("fmt"; "path/filepath")

func main() {
  fmt.Println(filepath.Join("/usr", ""))
  // prints "/usr"
}
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-06-05 10:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-01  9:42 [ruby-core:122359] [Ruby Bug#21391] Inconsistent trailing slash behavior of File#join and Pathname#join with empty strings lovro-bikic via ruby-core
2025-06-01 13:02 ` [ruby-core:122360] [Ruby Bug#21391] Inconsistent trailing slash behavior of File.join " Dan0042 (Daniel DeLorme) via ruby-core
2025-06-01 14:03 ` [ruby-core:122361] " lovro-bikic via ruby-core
2025-06-02 15:26 ` [ruby-core:122381] " Dan0042 (Daniel DeLorme) via ruby-core
2025-06-05 10:13 ` [ruby-core:122448] " akr (Akira Tanaka) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).