On ruby-doc, the documentation entries for File::exist? and File::exists? are duplicated with different semantics: one entry says returns true if file_name is a directory; the other says returns true if file_name is a file.
I don't think either entry is correct. Both methods seem to be implemented in file.c using rb_file_exist_p, which, seems to try to call fstat() if the value passed is an IO, or stat() if it's a string. Both fstat() and stat() return 0 on success and -1 on error, and this is passed back to rb_file_exist_p, and turned into a boolean result. It seems to me that
there are two methods for making code read more easily; there are no semantic differences
neither really relates to a file existing, but to whether a file-like item exists, e.g. a file, a dir, a socket, a fifo etc.
perhaps the document could say that the methods tell the caller whether or not a thing that has file-like semantics is there, but more specific tests will tell what it actually is: e.g. directory?, file?, socket? etc.
Is my understanding of the (lack of) difference in the methods correct, and is it worth suggesting a change to the document ?
Note that the answer to this question depends on the Ruby version. See the other answers for newer versions of Ruby. AFAIK exists? was deprecated in 2.2.
If we look at the C source, we see this:
rb_cFile = rb_define_class("File", rb_cIO);
/* ... */
define_filetest_function("exist?", rb_file_exist_p, 1);
define_filetest_function("exists?", rb_file_exist_p, 1);
So File.exist? and File.exists? are exactly the same thing and the corresponding documentation is:
Return <code>true</code> if the named file exists.
The rb_file_exist_p C function is just a very thin wrapper around rb_stat, that's a wrapper for the STAT macro, and STAT is just a portability wrapper for the stat system call. So, the documentation above is correct: File#exist? returns true if the file exists.
If we check file.c for the documentation snippet that talks about directories, we find this:
/*
* Document-method: exist?
*
* call-seq:
* Dir.exist?(file_name) -> true or false
* Dir.exists?(file_name) -> true or false
*
* Returns <code>true</code> if the named file is a directory,
* <code>false</code> otherwise.
*
*/
So it looks like the documentation generator is getting confused because Dir.exist? and File.exist? are documented in file.c even though Dir is defined in dir.c.
The underlying problem seems to be that the source code arrangement doesn't match what the documentation generator expects and the result is confused and incorrect documentation. I'm not sure how this should be fixed though.
Since ruby 2.2.0 File.exists? is deprecated use instead File.exist?
http://ruby-doc.org/core-2.2.0/File.html#exist-3F-method
File.exist? and File.exists? are NOT exactly the same thing anymore. See https://github.com/ruby/ruby/blob/ruby_2_3/file.c#L5920
define_filetest_function("exist?", rb_file_exist_p, 1);
define_filetest_function("exists?", rb_file_exists_p, 1);
rb_file_exists_p contains this line:
rb_warning("%sexists? is a deprecated name, use %sexist? instead", s, s);
So you should stick with File.exist?.
git pull made it go away - this was fixed here - not sure why generated doco on ruby-doc and apidock still wrong
Related
I have (had) some Ruby code which for historical reasons is (was) essentially
Dir.mktmpdir do |dir|
path_list = something_which_creates_files_in(dir)
path_list.each(&:delete)
end
Occasionally I get (got) exceptions from this code:
Errno::ENOENT: No such file or directory # dir_s_rmdir - /tmp/d2..4w/file.csv
:
/path/to/source.rb:124:in `unlink'
/path/to/source.rb:124:in `delete'
/path/to/source.rb:124:in `each'
:
/usr/lib/ruby/2.5.0/tmpdir.rb:93:in `mktmpdir'
:
so it appears to me that that my "cleanup" of the list of paths at the end of the block is not entirely synchronous, that (some of) the files still exist after it completes, then mktmpdir removes the temporary directory so that the asynchronous (?) unlink fails, its target has gone away. Is this a reasonable interpretation?
This is more an academic question than anything else; the behaviour puzzles me. Just removing the cleanup (the path_list.each(&:delete)) and leaving the deletion to Dir.mktmpdir seems to stop these exceptions.
If it makes a difference, this is Ruby 2.5 (MRI) running on Linux.
Your assumption seems to be correct.
If you would check the source of File.unlink you could see the following:
static VALUE
rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
{
return apply2files(unlink_internal, argc, argv, 0);
}
Here unlink_internal is a trivial thing (just a thin wrapper around a system call?), but what is interesting is the implementation of apply2files. You could see there the following call:
...
rb_thread_call_without_gvl(no_gvl_apply2files, aa, RUBY_UBF_IO, 0);
...
where aa is some fancy struct that contains among other things a pointer to the thing that we want to apply - unlink in our case.
The name of this function is quite self-descriptive, but the source contains some documentation too, so we can just refer to it:
/*
* rb_thread_call_without_gvl - permit concurrent/parallel execution.
* rb_thread_call_without_gvl2 - permit concurrent/parallel execution
* without interrupt process.
...
So from what I see (disclaimer: without the really careful analysis of the source code :)) the deletions within the block in question 1) do happen concurrently and 2) without GIL "protection" - so "surprises" are more than possible if one tries to delete temporary files twice (1st time explicitly and 2nd time implicitly when the mktmpdir block exits).
Marshal.dump is unable to dump classes that include a singleton(eigenclass). I'd like to see how the method determines the presence of this singleton but I do not know how to go about finding the source. I did try the following:
dump_method = Marshal.method(:dump) #=> #<Method: Marshal.dump>
dump_method.source_location # => nil
I also took a quick look at https://github.com/ruby/ruby but, without a search capability, I have no idea where to find the source for Marshal.
The check is here, but it is in C - and MRI code is infamously opaque.
I believe a check similar to that, but in Ruby, would be:
obj.singleton_class.instance_methods(false).empty? &&
obj.singleton_class.instance_variables.empty?
If that is false (i.e. if anything is defined on the singleton), no dump for you.
I am writing a ruby DSL that will be used to code-generate a number of Objective-C++ functions. I would like the name of each function to be derived from the name of its ruby DSL source file.
For example, given this source file clusterOptions.rb:
require './vMATCodeMonkey'
VMATCodeMonkey.new(:print).options_processor <<EOS
-cutoff: flag: set('useCutoff', true), arg: vector('double')
-depth: flag: set('useInconsistent', true), arg: scalar('double', default: 2.0)
-maxclust: flag: set('useCutoff', false), arg: vector('index')
EOS
When the VMATCodeMonkey.new(:print) expression is evaluated I would ideally somehow like the new object to capture the clusterOptions.rb source filename. Is that possible?
And if (as I suspect) it is not, is there a good idiom for accomplishing this functionality [e.g. making the source file name effectively part of the specification captured by a DSL] in ruby?
[While I suspect it's not possible to do exactly as I've described, I ask anyway, because I've been surprised by ruby's obscure capabilities more than once.]
EDIT: I'm aware of __FILE__; what I'm looking for is some DSL-centric way of capturing the name of a DSL source file without explicitly mentioning __FILE__ in the DSL source. Hmm, and now that I'm trying to explain it, maybe crawling up a stack trace from the class initialize method?
Solution
With thanks to tadman, here is my VMATCodeMonkey#initialize method:
def initialize(out_opt = :print)
#caller_file = caller(1)[0].split(':')[0]
case out_opt
when :pbcopy
#out = IO.popen('pbcopy', 'w')
when :print
#out = $stdout
else
raise ArgumentError, "#{out_opt} is not an option!"
end
#out.puts "// vMATCodeMonkey's work; do not edit by hand!\n\n"
initialize_options_processor
end
And here's what it captures:
#caller_file = "/Users/Shared/Source/vMAT/ruby/clusterOptions.rb"
The full path to the source file being evaluated is stored in __FILE__. If you want just the filename, you'd use:
File.basename(__FILE__)
The __FILE__ constant is common to C, C++, Perl and Python, among others.
If you need to know what file made the call to the currently running routine, this could work:
caller(1)[0].split(':')[0]
This presumes your filenames do not have : in them, but in most cases that should be a fairly safe assumption. You'll also need to call this at the entry point into your library. If it's a method deeper in the stack, test caller(2) and so on.
I would like to check for the value of a node attribute. This case statement is what I have so far, and it works:
case node[:languages][:ruby][:host_cpu]
when "x86_64"
...
when "i686"
...
end
What I would like to do is use an if statement instead. This is what I tried:
if node[:languages][:ruby][:host_cpu]?("X86_64")
...
end
This is based on the following, Which worked.
if platform?("ubuntu")
...
end
However, my try didn't work. it gave a syntax error on the if line saying that there was an unexpected \n and $end was expected.
I found that there are two kinds of ways of performing an if. The first being the one I demonstrated above, which (apparently) only works with resources, and if_only, which works with nodes. like so
if_only {node[:languages]}
which seems to work only for checking the presence of nodes, and within a do context.
How do I check the value of a node using an if statement? One method does check values, but only of resources, the other checks nodes, but only for their presence, and not their values.
You are mixing up way to many different variants for conditionals, most of which are part of Chef, not Ruby. Let me try to describe the different options one by one.
Generally, a case is roughly comparable to a series of if and elsif statements. Your case above
case node[:languages][:ruby][:host_cpu]
when "x86_64"
...
when "i686"
...
end
is thus roughly equivalent to
if node[:languages][:ruby][:host_cpu] == "x86_64"
...
elsif node[:languages][:ruby][:host_cpu] == "i686"
...
end
As a side remark, case actually uses the === operator which is often not commutative but more powerful. For simple comparisons it works the same as == though. Both these variants are part of the Ruby language, in which you write your cookbooks.
The other options you mentioned are actually part of the API which Chef defined on top of Ruby. This is often called the Chef DSL (which stands for Domain Specific Language, i.e. an extension or adaption of a language, in this case Ruby for a specific usage domain, in this case configuration management.)
The platform? method is a method defined by Chef that checks whether the curent platform is one of the passed values. You can read more about that (and similar methods, e.g. the now recommended platform_family? method at the Chef docs for recipes in general and some often used ruby idioms.
As a side-remark: you might be surprised by the fact that Ruby allows the ? and ! characters to appear at the end of method names, which makes Ruby rather unique among similar languages in this regard. These characters are simply part of the method name and have no special meaning to the language. They are only used by convention to programmers to better identify the purpose of a method. If a method has a ? at the end, it is to be used to check some condition and is expected to return either a truthy or falsy value. Methods with a ! at the end often perform some potentially dangerous operation, e.g. change object in place, delete stuff, ... Again, this is only a convention and is not interpreted by the language.
The last option you mentioned, the only_if and by extension not_if are used to define conditionals on Chef resources to make sure they are only executed when a certain condition is true (or when using not_if, if it is false). As these attributes are used on Chef resources only, they are naturally also defined by Chef.
To understand why they are useful it is necessary to understand how a Chef run works. The details can be found at the description of the Anatomy of a Chef Run. What is important there is, that you basically have two execution phases: Resource Compilation and Convergence. In the first step, the actual code to define the resources is executed. Here, also the code in your case statement would be run. After all the recipes have been loaded and all the resources have been defined, Chef enters the second phase, the Convergence phase. There, the actual implementation of the resources which performs the changes (create files and directories, in stall packages, ...) is run. Only in this phase, the only_if and not_if conditions are checked.
In fact, you can observe the difference between
file "/tmp/helloworld"
action :create
content "hello world"
end
if File.exist?("/tmp/helloworld")
file "/tmp/foobar"
action :create
content "foobar"
end
end
and
file "/tmp/helloworld"
action :create
content "hello world"
end
file "/tmp/foobar"
action :create
content "foobar"
only_if{ File.exist?("/tmp/helloworld") }
end
In the first variant, the condition whether /tmp/foobar exists is checked during resource compilation. At this time, the code to actually create the /tmp/helloworld file has not been run, as it does that only in the Conversion step. Thus, during your first run, the /tmp/foobar file would not be created.
In the second variant however, the check is done with only_if which is evaluated during conversion. Here you will notice that both files get created in the first run.
If you want to read a bit more on how the definition of the conditionals works in terms of Ruby (and you definitely should), you can read about Ruby Blocks which are more or less pieces of code that can be passed around for later execution.
Apologies for the poorly worded question title - no idea how to put it better!
In the following code, when I execute ruby bar.rb, how can I make it output bar.rb, rather than foo.rb?
In foo.rb:
module Foo
def filename
__FILE__
end
end
In bar.rb:
require_relative 'foo'
include Foo
puts filename # outputs 'foo.rb'
This is for a library function that, each time some code is executed, records the location (and git ref) of that code.
Your question stimulated me to crack open the Ruby interpreter source and see how __FILE__ actually works. The answer is pretty interesting: it's implemented right inside the parser. The lexer has a special token type for __FILE__. When the parser sees that token, it converts it to a string constant, which contains the name of the file the parser is working on.
From line 14948 of ext/ripper/ripper.c:
case keyword__FILE__:
return NEW_STR(rb_external_str_new_with_enc(ruby_sourcefile, strlen(ruby_sourcefile),
rb_filesystem_encoding()));
I think this should make it clear that trying to make __FILE__ return the name of the including file is completely impossible, unless you hack the Ruby interpreter source, or write your own preprocessor which transforms __FILE__ to something else before passing the Ruby source to the interpreter!
There is a trick you might be a able to use. If you pass a block to the method you could use the blocks closure to determine it's source. Something like:
def filename(&blk)
blk.eval "__FILE__"
end
But again, that means you have to pass a block.
Honestly I wonder what you are trying to accomplish, b/c outside of make some common core extension method, this is probably something you really don't want to do.