adamwiggins / rush

Ruby replacement for bash+ssh
http://rush.heroku.com/
518 stars 73 forks source link

File::create/File::touch, shall we make it more like the native unix touch? #21

Open najamelan opened 10 years ago

najamelan commented 10 years ago

Currently File::create will truncate an existing file. It is an alias for File::write( '' ), whereas Dir::create will call FileUtils::mkdir and thus will throw if the directory already exists.

That seems dangerous and counter-intuitive to me. I would like to propose going towards a model that mimics unix commands a bit more faithfully. I would make touch just a frontend to unix touch, or at least for now of FileUtils::touch and change the functionality of create.

Right now, you need to make sure you don't forget the trailing slash when using something like home[ 'newdir/' ]. For directories we could have mkdir (alias for create_dir, which could be used as: home[ 'some/unexisting/dir' ].mkdir or home.mkdir 'some/dir' or a combination of both.

If home or 'some/dir' would be files and exist on disk or have been created by the user by calling Rush::File.new explicitly, this should throw an exception.

Right now the type of an entry is determined at creation time, whereas for scenarios such as this that's a limitation. We could wait to determine that until the file/dir actually exists on disk or until the user does something which can only be done on either a file or directory, like the methods mentioned above.

It makes sense to keep create though, because it's a method that allows you to create a mixture of files and directories from say an array of paths, where you can use the slash at the end to determine the type of the entry. That's powerful, but should in my opinion be reserved to this use only. I would prefer it to throw an exception if the entry already exists.

Implementation?

If other people think this is all good reasoning, I'd be willing to design and implement the change. It's probably going to break backwards compatibility.

So what are the consequences of not knowing the type until usage. It basically means that, Box::[] and Rush::[] create a Rush::Entry. This entry would have a role rather than a subclass. Basically the restraints and powers of a file or a dir could come in through object composition. Any hierarchy could be pushed down to the role level.

The roles would register themselves with Rush::FsRole, their baseclass, along with the method names that would cause an entry to get that role. Now Rush::FsRole can function as a factory for filesystem roles, which would return Rush::File, Rush::Dir, maybe Rush::Socket, etc.

When someone calls Rush::Entry::touch, the method_missing of Rush::Entry would create @role || @role = Rush::FsRole.create( 'touch' ), which would cause Rush::FsRole to return a Rush::File. If successful it would then either mixin @role or would call @role.touch. Mixin would be more efficient I imagine, but I don't really know right now which would be best. We could also override .is_a?, although .class will always be Entry I think.