Extraordinarily Simple Ruby Question: Where's My Class? - ruby

[I'm just starting with Ruby, but "no question is ever too newbie," so I trudge onwards...]
Every tutorial and book I see goes from Ruby with the interactive shell to Ruby on Rails. I'm not doing Rails (yet), but I don't want to use the interactive shell. I have a class file (first_class.rb) and a Main (main.rb). If I run the main.rb, I of course get the uninitialized constant FirstClass. How do I tell ruby about the first_class.rb?

The easiest way is to put them both in the same file.
However you can also use require, e.g.:
require 'first_class'

You can also use autoload as follows:
autoload :FirstClass, 'first_class'
This code will automatically load first_class.rb as soon as FirstClass is used. Note, however, that the current implementations of autoload are not thread safe (see http://www.ruby-forum.com/topic/174036).

There's another point worth noting: you wouldn't typically use a main file in ruby. If you're writing a command line tool, standard practice would be to place the tool in a bin subdirectory. For normal one-off scripts the main idiom is:
if __FILE__ == $0
# main does here
# `__FILE__` contains the name of the file the statement is contained in
# `$0` contains the name of the script called by the interpreter
#
# if the file was `required`, i.e. is being used as a library
# the code isn't executed.
# if the file is being passed as an argument to the interpreter, it is.
end

Related

IRB require mongo fails cannot load such file -- mongo [duplicate]

The latest changesets to Ruby 1.9.2 no longer make the current directory . part of your LOAD_PATH. I have a non-trivial number of Rakefiles that assume that . is part of the LOAD_PATH, so this broke them (they reported "no such file to load" for all require statements that based off the project path). Was there a particular justification for doing this?
As for a fix, adding $: << "." everywhere works, but seems incredibly hacky and I don't want to do that. What's the preferred way to make my Rakefiles 1.9.2+ compatible?
It was deemed a "security" risk.
You can get around it by using absolute paths
File.expand_path(__FILE__) et al
or doing
require './filename' (ironically).
or by using
require_relative 'filename'
or adding an "include" directory
ruby -I . ...
or the same, using irb;
$irb -I .
There's two reasons:
robustness and
security
Both are based on the same underlying principle: in general, you simply cannot know what the current directory is, when your code is run. Which means that, when you require a file and depend on it being in the current directory, you have no way of controlling whether that file will even be there, or whether it is the file that you actually expect to be there.
As others answers point out, it's a security risk because . in your load path refers to the present working directory Dir.pwd, not the directory of the current file being loaded. So whoever is executing your script can change this simply by cding to another directory. Not good!
I've been using full paths constructed from __FILE__ as an alternative.
require File.expand_path(File.join(File.dirname(__FILE__), 'filename'))
Unlike require_relative, this is backward compatible with Ruby 1.8.7.
Use require_relative 'file_to_require'
Throw this in your code to make require_relative work in 1.8.7:
unless Kernel.respond_to?(:require_relative)
module Kernel
def require_relative(path)
require File.join(File.dirname(caller.first), path.to_str)
end
end
end
'.' in your path has long been considered a bad thing in the Unix world (see, for example, http://www.faqs.org/faqs/unix-faq/faq/part2/section-13.html). I assume the Ruby folks have been persuaded of the wisdom of not doing that.
I found this to be a confounding change until I realized a couple of things.
You can set RUBYLIB in your .profile (Unix) and go on with life as you did before:
export RUBYLIB="."
But as mentioned above, it's long been considered unsafe to do so.
For the vast majority of cases you can avoid problems by simply calling your Ruby scripts with a prepended '.' e.g. ./scripts/server.
As Jörg W Mittag pointed out, I think what you want to be using is require_relative so the file you require is relative to the source file of the require declaration and not the current working dir.
Your dependencies should be relative to your rake build file.

Ruby Project - Prevent a ruby file from directly being called from OS command line

I am doing a demo command line project in Ruby. The structure is like this:
/ROOT_DIR
init.rb
/SCRIPT_DIR
(other scripts and files)
I want users to only go into the application using init.rb, but as it stands, anyone can go into the sub-folder and call other ruby scripts directly.
Questions:
What ways can above scenario be prevented?
If I was to use directory permissions, would it get reset when running the code from a Windows machine to on Linux machine?
Is there anything that can be included in Ruby files itself to prevent it from being directly called from OS command line?
You can't do this with file permissions, since the user needs to read the files; removing the read permission means you can't include it either. Removing the execute permission is useful to signal that these file aren't intended to be executed, but won't prevent people from typing ruby incl.rb.
The easiest way is probably to set a global variable in the init.rb script:
#!/usr/bin/env ruby
FROM_INIT = true
require './incl.rb'
puts 'This is init!'
And then check if this variable is defined in the included incl.rb file:
unless defined? FROM_INIT
puts 'Must be called from init.rb'
exit 0
end
puts 'This is incl!'
A second method might be checking the value of $PROGRAM_NAME in incl.rb; this stores the current program name (like argv[0] in many other languages):
unless $PROGRAM_NAME.end_with? 'init.rb'
puts 'Must be called from init.rb'
exit 0
end
I don't recommend this though, as it's not very future-proof; what if you want to rename init.rb or make a second script?

Does the Ruby interpreter compile to byte-code in a lazy way? How?

For MRI 1.9+ and Rubinius implementation, Ruby source code is compiled into byte-code, and then that byte-code is interpreted by the VM. I want to know the details of this mechanism when running a Ruby script from command-line using the interpreter.
Does the interpreter compile all related source files required in the script first and then it run everything? Or does it execute some code and then compile other files while needed in a lazy way?
If it's the latter (which I suspect), is this process done by file or by a block of code?
At which point it stops the execution of byte-code and runs the compilation process again?
Does this process differ from MRI to Rubinius?
For example, if I run "ruby my_main_script.rb", which requires 3 other rb source files (and this files itself do not have any requirement), the possibility I imagine would be:
A: The interpreter parses my_main_script.rb and the 3 files. After parsing then, it compiles all AST trees to byte-code. It then proceeds to run the byte-code using the VM.
B: Ruby parses my_main_script.rb and compiles it into byte-code. It then runs the byte-code. When encountering a call to a method in another files, it first parse and compiles that files and the continues with the execution. If this is the case, I would like to understand this in detail.
C: Ruby parses and compiles some piece of code from my_main_script.rb according to some (unkwnon to me) criteria, it runs that byte-code and then parses-and-compiles another piece when needed. This process and that "when needed" condition detection method is what would be interesting for me to understand.
Update 30/03/16
I wrote this little experiment script to try to check if B is the right answer:
class RubyVM
class InstructionSequence
class << self
alias :old_compile_file :compile_file
def compile_file(code, opt)
puts "Injecting code..."
old_compile_file(code, opt)
end
alias :old_compile :compile
def compile(code)
puts "Injecting code..."
old_compile(code)
end
end
end
end
require_relative 'say_hi'
'say_hi.rb' only contains the line "puts 'hello'".
If B is the right answer, shouldn't the output be the following?
Injecting code...
hello
It just outputs "hello"...
For me B is the right answer.
Ruby allows us to load dynamically our code via autoload and to execute strings as code (eval) so it must be able to parse and execute code at any time.
Therefore first it will transform all the files required by your main program to YARV instructions, but if you use autoload or eval those files/code will be transformed later.
A very good book about that process is Ruby under a microscope

Run arbitrary ruby code in a chef cookbook

I have a simple chef cookbook and all it does is it sets the MOTD on a CentOS machine. It takes the content of the /tmp/mymotd.txt and turns it into the MOTD.
I also have a simple ruby script (a full-fledged ruby script) that simply reads the text from the web-server and puts in into the /tmp/mymotd.txt.
My questions are:
how do I run this ruby script from within the cookbook?
how do I pass some parameters to the script (e.g. the address of the web-server)
Thanks a lot beforehand.
Ad 1.
You can use libraries directory in scripts to place there your ruby script and declare it in a module. Example:
# includes
module MODULE_NAME
# here some code using your script
# Example function
def example_function (text)
# some code
end
end
You can use then
include MODULE_NAME
in your recipe to import those functions and just use it like
example_function(something)
What's good - you can use there also Chef functions and resources.
IMPORTANT INFO: Just remember that Chef has 2 compilation phases. First will be all of Ruby code, second all of Chef resources. This means, that you have to remember priority of code. I won't write here more info about it, since you haven't asked for this, but if you want, you can find it here.
Ad 2.
You can do this in several ways, but it seems to me, that the best option for you would be to use environments. You can find more info in here. Basically, you can set up environment for script before it will run - this way you can define some variables you would use later.
Hope this helps.

Stub require statement in rspec?

I have to maintain a Ruby script, which requires some libs I don't have locally and which won't work in my environment. Nevertheless I want to spec some methods in this script so that I can change them easily.
Is there an option to stub some of the require statements in the script I want to test so that it can be loaded by rspec and the spec can be executed within my environment?
Example (old_script.rb):
require "incompatible_lib"
class Script
def some_other_stuff
...
end
def add(a,b)
a+b
end
end
How can I write a test to check the add function without splitting the "old_Script.rb" file and without providing the incompatible_lib I don't have?
Instead of stubbing require which is "inherited" from Kernel, you could do this:
Create a dummy incompatible_lib.rb file somewhere that is not in your $LOAD_PATH. I.e., if this is a Ruby application (not Rails), don't put it in lib/ nor spec/.
You can do this a number of ways, but I'll tell you one method: in your spec file which tests Script, modify $LOAD_PATH to include the parent directory of your dummy incompatible_lib.rb.
Ordering is very important -- next you will include script.rb (the file which defines Script).
This will get you around the issue and allow you test test the add method.
Once you've successfully tested Script, I would highly recommend refactoring it so that you don't have to do this technique, which is a hack, IMHO.
Thanks, I also thought about the option of adding the files, but finally hacked the require itself within the test case:
module Kernel
alias :old_require :require
def require(path)
old_require(path) unless LIBS_TO_SKIP.include?(path)
end
end
I know that this is an ugly hack but as this is legacy code executed on a modified ruby compiler I can't easily get these libs running and it's sufficient to let me test my modifications...

Resources