Description
Expected behavior
When a Rails or Sprockets project is placed in a directory with spaces in the folder names in the path, it shouldn't throw any errors. It should just escape the path name and work like normal.
Actual behavior
Once you put your project in a folder that has spaces in the path name, it throws the following error:
bad URI(is not URI?): "file-digest:///Users/username/Company Name Dropbox/User Name/ProjectName/myapp/app/assets/config/bootstrap"`
System configuration
- Sprockets 4.0.2 (also been able to replicate it in 3.7.2)
- Ruby 2.7.1 (also been able to replicate it in 2.5.1)
- Rails 6.0.3.2 (also been able to replicate it in 5.2.4.3)
Example App (Reproduction)
I have successfully created a failing test on a fork of Sprockets. You can simply pull the following branch, run bundle exec rake test
and you will see the failing test.
This is what you should see:
Finished in 46.357470s, 19.4143 runs/s, 84.9701 assertions/s.
1) Error:
TestURIUtils#test_split_file_uri:
URI::InvalidURIError: bad URI(is not URI?): "file:///usr/Company Name Dropbox/local/bin/myapp/assets"
/.rvm/rubies/ruby-2.7.1/lib/ruby/2.7.0/uri/rfc3986_parser.rb:67:in `split'
/.rvm/rubies/ruby-2.7.1/lib/ruby/2.7.0/uri/common.rb:197:in `split'
/sprockets/lib/sprockets/uri_utils.rb:49:in `split_file_uri'
/sprockets/test/test_uri_utils.rb:39:in `test_split_file_uri'
900 runs, 3939 assertions, 0 failures, 1 errors, 4 skips
You have skipped tests. Run with --verbose for details.
rake aborted!
Command failed with status (1)
/.rvm/gems/ruby-2.7.1@myapp/gems/rake-12.3.3/exe/rake:27:in `<top (required)>'
/.rvm/gems/ruby-2.7.1@myapp/bin/ruby_executable_hooks:24:in `eval'
/.rvm/gems/ruby-2.7.1@myapp/bin/ruby_executable_hooks:24:in `<main>'
Tasks: TOP => test
(See full trace by running task with --trace)
Steps Taken To Begin Fixing
I have isolated the problem to the following method:
def split_file_uri(uri)
# We need to parse out any potential spaces in the path
scheme, _, host, _, _, path, _, query, _ = URI.split(uri)
path = URI::Generic::DEFAULT_PARSER.unescape(path)
path.force_encoding(Encoding::UTF_8)
# Hack for parsing Windows "/C:/Users/IEUser" paths
if File::ALT_SEPARATOR && path[2] == ':'
path = path[1..-1]
end
[scheme, host, path, query]
end
One solution I have tried is to essentially escape uri
at the beginning of the method, so I inserted this line at the top of that method:
uri = URI::Generic::DEFAULT_PARSER.escape(uri)
The issue this creates is that it escapes paths that were previously escaped, e.g. the following test from test/test_uri_utils.rb
has begun to fail with that fix:
parts = split_file_uri("file:///usr/local/bin/ruby%20on%20rails")
assert_equal ['file', nil, '/usr/local/bin/ruby on rails', nil], parts
Primarily because it returns the original string as a URI, i.e. file:///usr/local/bin/ruby%20on%20rails
, as opposed to the unescaped version.
The reason this is the case is because the first thing it does is now escapes the previously escaped string and then unescapes that double escaped string, which reverts it to the original string.
There are also other Failures, Errors & Skips that this small change has created.
So rather than digging much further, I figured I would reach out for some help.