#2619 Uri#toFile behaviour

fraya Thu 20 Jul 2017

Let's say that I have a pod named foo with a resource file called test.sql in a directory sql. After compiling the pod, in fansh I create this Uri

uri1 := `fan://foo/sql/test.sql`

get does work

f2 := uri1.get as File
f2.readAllStr
SELECT * FROM FOO

but toFile does not work

uri1.toFile

sys::ArgErr: Invalid Uri scheme for local file: fan://foo/sql/test.sql
fan.sys.LocalFile.uriToFile (LocalFile.java:64)
fan.sys.File.make (File.java:26)
fan.sys.File.make (File.java:23)
fan.sys.Uri.toFile (Uri.java:1255)

Uri#.toFile is creating a File from the Uri and expects an Uri starting with file://. Although the doc says "Convenience for File.make(this) - no guarantee is made that the file exists." it seems to me counter-intuitive.

matthew Thu 20 Jul 2017

If you are trying to get a file out of a Pod, use Pod.file

f := Pod.find("foo").file(`/sql/test.sql`)

SlimerDude Thu 20 Jul 2017

Confusingly, the following also works!

Pod.find("sys").file(`fan://foo/sql/test.sql`)

But Fraya mentioned that he can already get hold of a file in a pod, I think he just finds the semantics between Uri.get() and Uri.toFile() a bit unclear.

Uri.toFile() / File.make(Uri)

These are for files on the local file system - the standard files and directories on your computer. These don't need a scheme, and may be absolute or relative:

`readme.md`.toFile()                      // -> relative
`/C:/Apps/Fantom/readme.md`.toFile()      // -> absolute
`file:/C:/Apps/Fantom/readme.md`.toFile() // -> fully qualified

The "no guarantee is made that the file exists" part just means File objects may be instantiated regardless of whether the actual underlying system file exists or not. (Which has to be the case; because the file system is constantly changing and is not controlled by Fantom or Java, we can never be certain of what state it is in.)

// readme.moo doesn't exist, but no error is thrown
`/wot/ever/gibberish/readme.moo`.toFile()

Use File.exists() to check if the system file exists or not at any given point in time. (Be aware that other programs / processes (if they're evil enough) may create / delete the same files you're trying to use!)

Uri.get

Not all files are on the file system (think the Internet) and not all URIs point to files (URI is essentially a naming system). So Fantom lets you create your own Scheme where you can contribute to the URI namespace to resolve any kind of object.

(But Fantom and indexed properties are a poor man's IoC, so I don't really use it.)

As far as the fan:// scheme goes, think of it as resolving to a subclass of sys::File where the in() and various read() methods have been overridden to return content from the .pod file as oppose to the file system.

I think this is where the confusion is, and is perhaps a small leaky abstraction on Fantom's behalf. The public API just shows File but behind the scenes there is actually File (abstract class), LocalFile, MemFile, and ZipEntryFile.

I hope this helps, and if you want to suggest updates to the documentation that clarify matters, I'm sure Brian and Andy would be accommodating! :)

fraya Fri 21 Jul 2017

I thank you all for your response.

@matthew: That's exactly what I was using. My confusion is about the semantics.

@SlimerDude: My expectation on Uri#.toFile is that if an Uri can be converted to a File like fan://foo/sql/test.sql or https://bitbucket.org/AlienFactory/afgundam/downloads/afGundam-2.1.2.zip (in a future http Uri), either I have a File or a null or an Err if it is not possible to create the file.

Maybe a warning in Uri#.toFile only works for local filesystems or only works for file: Uris would helped me but I would prefer "more abstraction" even though "every abstraction leaks".

brian Fri 21 Jul 2017

The semantics for Uri.toFile were convenience for File.make which is documented explicitly as constructor for local file system files only. But I see how it might be confusing, and really no good reason to have Uri.toFile not route to get in that case. I changed the logic for Uri.toFile to be:

public File toFile()
{
  if (scheme == null) return File.make(this);
  return (File)get();
}

fraya Sat 22 Jul 2017

It make sense, thank you for your work.

Login or Signup to reply.