Let's assume a script needs access a directory, say /some/where/abc on an "arbitrary" OS. There are a couple options to build the path in Ruby:
File.join('', 'some', 'where', 'abc')
File.absolute_path("some#{File::SEPARATOR}where#{File::SEPARATOR}abc", File::SEPARATOR)
Pathname in the standard API
I believe the first solution is clear enough, but idiomatic. In my experience, some code reviews ask for a comment to explain what it does...
The Question
Is there a better way to build an absolute path is Ruby, where better means "does the job and speaks for itself"?
What I would pick up if I was doing a code review is that on Windows /tmp is not necessarily the best place to create a temporary directory, and also the initial '', argument is perhaps not obvious to the casual reviewed that it creates <nothing>/tmp/abc. Therefore, I would recommend this code:
File.join(Dir.tmpdir(), 'abc')
See Ruby-doc for an explanation.
UPDATE
If we expand the problem to a more generic solution that does not involve using tmpdir(), I cannot see a way round using the initial '' idiom (hack?). On Linux this is not too much of a problem, perhaps, but on Windows with multiple drive letters it will be. Furthermore, there does not appear to be a Ruby API or gem for iterating the mount points.
Therefore, my recommendation would be to delegate the mount point definition to a configuration option that might be '/' for Linux, 'z:/' for Windows, and smb://domain;user#my.file.server.com/mountpoint for a Samba share, then use File.join(ProjectConfig::MOUNT_POINT, 'some', 'where', 'abc').
File#join is THE canonical way to build a portable path in Ruby. I'm wondering who is doing the review. Perhaps Ruby is new to your organization.
I agree with #ChrisHeald that referring to the documentation is the best way to explain the code to a reviewer.
Related
It's very strange, I cannot find any standard way with Ruby to copy a directory recursively while dereferencing symbolic links. The best I could find is FindUtils.cp_r but it only supports dereferencing the root src directory.
copy_entry is the same although documentation falsely shows that it has an option dereference. In source it is dereference_root and it does only that.
Also I can't find a standard way to recurse into directories. If nothing good exists, I can write something myself but wanted something simple and tested to be portable across Windows and Unix.
The standard way to recurse into directories is to use the Find class but I think you're going to have to write something. The built-in FileUtils methods are building blocks for normal operations but your need is not normal.
I'd recommend looking at the Pathname class which comes with Ruby. It makes it easy to walk directories using find, look at the type of the file and dereference it if necessary. In particular symlink? will tell you if a file is a soft-link and realpath will resolve the link and return the path to the real file.
For instance I have a soft-link in my home directory from .vim to vim:
vim = Pathname.new ENV['HOME'] + '/.vim'
=> #<Pathname:/Users/ttm/.vim>
vim.realpath
=> #<Pathname:/Users/ttm/vim>
Pathname is quite powerful, and I found it very nice when having to do some major directory traversals and working with soft-links. The docs say:
The goal of this class is to manipulate file path information in a neater way than standard Ruby provides. [...]
All functionality from File, FileTest, and some from Dir and FileUtils is included, in an unsurprising way. It is essentially a facade for all of these, and more.
If you use find, you'll probably want to implement the prune method which is used to skip entries you don't want to recurse into. I couldn't find it in Pathname when I was writing code so I added it using something like:
class Pathname
def prune
Find.prune
end
end
Here's my implementation of find -follow in ruby:
https://gist.github.com/akostadinov/05c2a976dc16ffee9cac
I could have isolated it into a class or monkey patch Find but I decided to do it as a self-contained method. There might be room for improvement because it doesn't work with jruby. If anybody has an idea, it will be welcome.
Update: found out why not working with jruby - https://github.com/jruby/jruby/issues/1895
I'll try to workaround. I implemented a workaround.
Update 2: now cp_r_dereference method ready - https://gist.github.com/akostadinov/fc688feba7669a4eb784
I have a sizable vim script (a .vim file, in viml syntax). I'd like to check (but not execute!) the file for simple syntax errors.
How do I accomplish this?
I just want a very rough syntax check. Something along the lines of perl -c or pyflakes.
Here is a syntax checker for VimL.
https://github.com/syngan/vim-vimlint/
I don't think (I'm relatively sure, as much as one can be) one exists. VimL is an internal language of Vim (and only Vim), and there aren't many tools developed for it.
I tried searching on vim.org and several other places, with no luck. Not suprising, because I've never heard of one either.
So you're either stuck with running the script, or switching to an outside language like Python, Perl or Ruby.
https://github.com/osyo-manga/vim-watchdogs
vim-watchdogs, apparently, is a syntax checker for vim, it says that it supports many languages, including vimL
if you use vundle, you can just drop this into your vimrc:
Plugin 'git://github.com/osyo-manga/vim-watchdogs.git'
..and then run:
:PluginInstall
..to set it up (vundle is a very nifty plugin manager) If you have syntastic, you might want to be careful and disable it first, and then see if it is an adequate replacement (since it says it supports all those languages anyway).
It is a safe bet that when you have multiple syntax checkers going, you will need to put your "dogs on a leash", so to speak; by configuring one to check languages that the other one does not, and vice-versa. If you do not, there will be at best collisions, duplications, or misdirections. At worst, you will have all of the above and more.
Make sure that you always backup your ~/.vim directory (or your VIMRUNTIME directory if you install things on a global level), you will be glad you did. Hope that helped you or someone else out, good luck! Sorry you had to wait 7.5 months for a response, heh :)
There's now a second option: vim-lint (as opposed to vimlint)
I wonder why I should use:
puts "In folder #{File.join ENV[HOME], projects}"
Instead of:
puts "In folder #{ENV[HOME]/projects}"
I am aware of that File.join will put the appropriate separator (/ vs \) depending on the OS.
The script is already so tightly tied to what version of ruby you are using, what gems you have installed and so on. My scripts tend not to be like an ORM, (in this case) independent of OS.
I will never run this on Windows (the other dependencies will make the script not to work anyway).
So seems not to be a strong reason for using it, right?
Any of the following :
File.join("first","second")
File.join("first/","second")
File.join("first","/second")
File.join("first/","/second")
Will return
=> "first/second"
Could it be a good reason for you ?
That's only one example I can think of.
Actually, your goal is not to concatenate 2 strings, your goal is creating a path. This looks like a strong reason to use File.join to me.
Haven't used Ruby, but I expect a Path.join to handle corner cases, like paths ending with or without directory separators. Besides, it expresses intent a bit more clearly than string concatenation, and clarity is IMHO almost always a good idea.
I expect join to handle corner cases gracefully, like when ENV[HOME] is empty for some weird reason.
In addition to the other answers your code will be more portable, the correct separator will be used regardless of unix/windows/etc.
be aware of difference between RUBY and PYTHON
RUBY: File.join("","somthing") → "/something"
PYTHON: os.path.join("","somthing") → "something"
RUBY treat empty string as path → I call this a BUG
I realize this sounds a little crazy, but I'm working on a project for which I need a server to run user-provided Ruby code and return the result.
I'm looking to prevent something like this:
system("rm -rf /")
eval("something_evil")
# etc...
I'm sure there must be some reasonably safe way to do this, as it already exists at places like tryruby.org. Any help is greatly appreciated, thanks!
Three suggestions:
1) Take a look at Ruby taint levels. This provides some degree of protection against, eval('evil_code') type things, etc.
2) Unless user's actually need access to the local file system, use something like fakefs
3) No matter what else you do follow Tronic's suggestion (can be a pain to setup, but limited chroot jails are about the only way to make absolutely sure that user's cannot access resources you don't explicitly want them to).
Run the program ptraced with a whitelist of allowed syscalls, as user/group nobody, with resource limits (memory usage etc), in a minimal chroot.
A "blank slate" is an object stripped of (most of) its methods.
A "clean room" is an object within which you evaluate potentially unsafe room.
If you evaluate the code in a "clean room" which is also a "blank slate," cranking the safe level up as high as it will go, you will afford yourself a great deal of protection. Nothing in security is sure, so this should be considered a layer in your security, not necessarily the only layer.
This answer shows how to do it.
I had the same problem but then came across eval.so and decided to write an API wrapper for it, called Sandie. It's as easy as:
sandie = Sandie.new(language: 'ruby')
# => #<Sandie:0x00000002e30650>
sandie.evaluate(code: 'puts "hello world"')
# => {"stdout"=>"hello world\n", "stderr"=>"", "wallTime"=>487, "exitCode"=>0}
It also supports a whole lot of other languages as well like C#, Perl, Lua, and Java.
What are the things you wish Ruby (and more generally the Ruby community) would improve?
I read somewhere that Ruby is the love-child of Smalltalk and LISP, with Miss Perl as the Nanny.
I have a lot of respect for Ruby's parents, but I'm not sure I like the influence Miss Perl had on the child. Specifically, I don't like the predefined variables: I need a cheat sheet to know what they mean. You could say "just don't use them". Well, I don't... but other people do. And when I download a plugin on the Web, I have no choice but to fetch my cheat-sheet if I ever need to go and touch the source code. I just wish they would remove those from the language itself.
Also, I think that Ruby is too much of a moving target. My code breaks on every new Ruby upgrade, even on minor releases. This is true also of Ruby on Rails and most Rails plugins I have worked with: they just change all the time, and nobody seems to care whether the changes break everything or not. IMHO, although I love a lot of things in Ruby, this lack of stability is almost a show-stopper.
I wish people would consider backward compatibility between minor releases as an unbreakable rule when releasing a new language (or library or framework) version.
I wish that some of the lesser used modules of the standard library were documented.
Make require-ing files less painful. Don't ask me how, but maybe have one file dedicated to knowing the paths involved and just get rid of the relative path crud from everything else.
Getting rid of the artificial distinction between Modules and Classes would be nice.
Both Modules and Classes are Namespaces. Modules are also Mixins, while Classes aren't. Classes can also be instantiated while Modules can't. This distinction is unnecessary. Just get rid of Modules and allow Classes to be used as Mixins.
An example of a language where this works is Newspeak.
I'd appreciate being able to install ruby 1.9 as an RPM rather than having to use the source.
Make Ruby completely Message Sending based, get rid of everything that is not a message send: local variables, global variables, instance variables, class hierarchy variables, constants, magic globals, magic constants, builtin operators, builtin keywords, even literals. See Self, Ioke or Newspeak for the incredible power and elegance this gains.
I wish they would get rid of the predefined variables: $!, $&, $+, etc.
I would like to have support for static compile-time metaprogramming. The Converge Programming Language might be a good starting point.
Replace the Mixin system with a Traits system.
Replace Exceptions with a Common Lisp style Conditions system.