[Pharo-users] How to construct a multi-part file reference
udo.schneider at homeaddress.de
Sat May 7 06:08:04 EDT 2016
> I always had the impression that Path is not meant for public use,
> just part of the implementation. Should it not be part of the -Public
> package then ?
I'm a bit schizophrenic on this one :-)
My "first self" argues that Path as a part of the FileSystem API should
indeed remain/be private. Especially because most of us still thing of
paths as strings. And manipulating them detached from a filesystem poses
some risks (mentioned below). In addition the class comment (IMHO
rightly) states: "I'm a private and abstract filesystem path,
independent of the string representation used to describe paths on a
My "second self" argues that a Path could denote so much more than an
identifier in filesystem space. Just think of a navigation path in
websites (breadcrumbs). In this context Path would be a generalized way
to deal of structuring an abstract space by mean of string identifiers.
And a "FileSystemPath" would be a (private - see above) subclass adding
the filesystem behavior (like detecting absolute filesystem paths).
> Maybe there are still other ways to parse a Windows path while
> running on a Mac or Linux ?
I'm not sure I'm getting this one.
Path works the same on all platforms. You might just have to manually
specify a delimiter if you want to parse a string. This IMHO is a good
thing - you should know the format of strings you're getting :-)
I've seen code like this:
"Path Splitting for Windows and *nix"
pathParts := pathString substrings: '/\'.
This works /most/ of the time. It just fails when considering that e.g.
"\" is a valid character in *nix filenames ...
'/home/udos/Hello\World' substrings: '/\'. "#('home' 'udos' 'Hello'
"Not specifying delimiter here - $/ is default then"
Path from: '/home/udos/Hello\World'. "Path / 'home' / 'udos' /
And considering that some platforms use totally different path
delimiters (e.g. ":" on Mac OS, "." on RISC/OS) it's IMHO cleaner to
only split on one Character (like Path>>#from:delimiter:) and assign the
responsibility of knowing which delimiter to use to the programmer.
However ignoring "minor" platforms and issues with delimiters in names
there is no big difference between String>>#substrings: and
Path>>#from:delimiter: at first. But the getting back a Path is IMHO
much more intention revealing than a collection of strings. Especially
because you're getting back an AbsolutePath or RelativePath which is
even more intention revealing and something you may completely miss when
absoluteArray := '/home/root/file' substrings: '/'. "#('home' 'root'
relativeArray := ('home/root/file' substrings: '/'). "#('home' 'root'
"The distinction between absolute and relative is lost here!"
absoluteArray = relativeArray. "true"
"Both paths may have referenced the same file - but also may not!!!!"
absolutePath := Path from: '/home/root/file' delimiter: $/. "Path /
'home' / 'root' / 'file'".
absolutePath class. "AbsolutePath"
relativePath := Path from: 'home/root/file' delimiter: $/. "Path *
'home' / 'root' / 'file'"
relativePath class. "RelativePath".
"The distinction between absolute and relative paths is kept.
In #printString and class"
absolutePath = relativePath. "false"
"No danger to confuse absolute and relative paths here"
Or did I get you completely wrong?
On 07/05/16 10:57, Sven Van Caekenberghe wrote:
> Hi Udo,
> That is a very good explanation, thank you.
> I always had the impression that Path is not meant for public use, just part of the implementation. Should it not be part of the -Public package then ?
> Maybe there are still other ways to parse a Windows path while running on a Mac or Linux ?
>> On 07 May 2016, at 10:46, Udo Schneider <udo.schneider at homeaddress.de> wrote:
>> Hi Johan,
>> I remember running into similar problems because I didn't understand the FileSystem philosophy ... and dealing with strings and concatenating them is so much easier, right? :-)
>> After reading the chapter on FileSystem several times over and over again the IMHO most important part of it to get the grasp of FileSystem is on page 13 (http://pharobooks.gforge.inria.fr/PharoByExampleTwo-Eng/latest/FileSystem.pdf):
>> FileReference = FileSystem + Path
>> Paths and filesystems are the lowest level of the FileSystem API. A FileReference combines a path and a filesystem into a single object which provides a simpler protocol for working with files as we show in the previous section. References implement the path protocol with methods like /, parent and resolve:.
>> So in your example ‘/home/jfabry’ and ‘test/code/foo.txt’ are just (relative) path strings. They do not reference anything outside of the context of a filesystem. The hard part for me to understand was the fact that a path may not be unique. The same path might reference different files in different filesystems.
>> This is especially strange coming from a *nix background where there is only one filesystem.
>> But even on Windows one could argue that the OS nowadays only knows one filesystem: You can reference any file via a UNC path ... drives, shares, partitions, URIs and other filesystems are simply aliases into the UNC space.
>> The nice thing of the FileSystem API is it's ability to transparently use files in-Memory, archives, FTP, WebDav, S3 ... . All from within Pharo with the same consistent API. E.g. if you have the FileSystemNetwork (http://smalltalkhub.com/#!/~UdoSchneider/FileSystemNetwork) installed you can do something like:
>> "Obtain a FTP FileSystem"
>> fs := FileSystem ftp: 'ftp://ftp.2600.com'.
>> "Get working directory"
>> wd := fs workingDirectory .
>> "Print the following expression!"
>> (wd / 'pub' / 'publications' / 'n0way') children.
>> (wd / 'pub' / 'publications' / 'n0way' / 'README') contents.
>> "Open a FileList on the FileSystem"
>> FileList openOn: wd.
>> "Remember to close if you are finished!"
>> fs close.
>> So to make a long story short: Both your strings contain paths. So we have to convert them into Paths and somehow combine them with a FileSystem to get a valid FileReference:
>> "I changed the second string to demonstrate dealing with different delimiters"
>> dirString := '/home/jfabry'.
>> fileString := 'test\code\foo.txt'.
>> "Convert path strings to Paths"
>> dirPath := Path from: dirString delimiter: $/.
>> filePath := Path from: fileString delimiter: $\.
>> "Please note the Paths do not reference anything. We have no FileSystem context yet"
>> "The FileSystem our Paths will be resolved within"
>> diskFs := FileSystem disk.
>> "FileReference for the root directory in the FS"
>> rootRef := diskFs root.
>> "Resolve our Paths in the Context of the Reference"
>> dirRef := rootRef resolve: dirPath.
>> fileRef := dirRef resolve: filePath. "File @ /home/jfabry/test/code/foo.txt"
>> "Please note the fileRef printString. 'File' denotes the FileSystem - not the fact that this is a file! Then you have the path after the at sign"
>> I hope this helps.
>> Final advice: In Pharo you should *never never never* assume that a Path is "enough" to reference a file. You will always need the context (it's FileSystem) as well. And that's exactly what a FileReference (see above) is. So if you only have a Path it's questionable to assume that you can simply combine it with a DiskFileSystem to get a valid reference. What if the Path references a file in a ZIP in-Memory archive? Trying to access the Path on disk will yield no result. So always always always store/pass a FileReference if possible!!!!
>> I did run into that issue in the past with archive/net FileSystems where some dev tools at one point extracted the Path from the FileReference and passed them arround. Down the stack some other methods needed the file contents. Because only the Path was passed they assumed they could retrieve the contents by simply combining it with a DiskFileSystem ... and boom!
>> On 07/05/16 00:33, Johan Fabry wrote:
>>> Hi all,
>>> I have a question about the filesystem that I could not resolve using the documentation. The problem is as follows: I have a file reference that is 2 separate strings that I need to join into one complete file ref but I don’t know how because I don’t know what the platform’s file separator is.
>>> For example, on a unix-like OS I get ‘/home/jfabry’ as one part and ‘test/code/foo.txt’ as the other part, and I need to construct a FileReference to ‘/home/jfabry/test/code/foo.txt’.On M$ I guess this would be 'C:\users\jfabry' and ‘test\code\foo.txt’, so I need to construct FileReference to 'C:\users\jfabry\test\code\foo.txt’
>>> And the bingo question is: what do I do if I both strings use different kinds of separators?
>>> ---> Save our in-boxes! http://emailcharter.org <---
>>> Johan Fabry - http://pleiad.cl/~jfabry
>>> PLEIAD and RyCh labs - Computer Science Department (DCC) - University of Chile
More information about the Pharo-users