stuck in rvm hell trying to get a simple rspec running - ruby

Ruby Settings From terminal
% ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]
=> ~/ruby/grounded/test
% where ruby
/home/mike/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
/home/mike/.rvm/bin/ruby
/usr/local/bin/ruby
/usr/bin/ruby
=> ~/ruby/grounded/Person/test
% which ruby
/home/mike/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
% rvm current
ruby-1.9.2-p180
Directory Structure
% tree
.
├── bowling.rb
└── bowling_spec.rb
File Contents
bowling.rb
class Bowling
end
bowling_spec.rb
require 'rubygems'
require 'rspec'
require 'bowling'
describe Bowling, "#score" do
it "returns 0 for all gutter game" do
bowling = Bowling.new
20.times { bowling.hit(0) }
bowling.score.should eq(0)
end
end
% ruby bowling_spec.rb
/home/mike/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': no such file to load -- bowling (LoadError)
from /home/mike/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from bowling_spec.rb:3:in `<main>'
Questions
Why am I getting a LoadError when bowling.rb and bowling_spec.rb
are in the same folder?
In the error what the heck is .../site_ruby/1.9.1/... when I am running ruby 1.9.2 then why would 1.9.1 even show up?
how do I get over this hump and start having fun with ruby.

When requiring files, the file must be in a list of directories called the $LOAD_PATH. The current directory used to be in this list, but as of 1.9.2, it has been removed for security reasons.
You have four options (listed in order of how good I think they are)
1 Change your directory structure to look like this
.
|-- lib
| `-- bowling.rb
`-- spec
`-- bowling_spec.rb
And then run as rspec spec instead of ruby bowling_spec.rb
This works because RSpec will see that lib is in your current directory, and then add it to the $LOAD_PATH for you.
If you do this, you also don't have to require 'rspec'.
2 Run with ruby -I . bowling_spec.rb
which will add the current directory to the $LOAD_PATH
3 Use require_relative 'bowling' instead of require 'bowling'.
This will look for the bowling.rb file relative to the current file being run (bowling_spec.rb)
4 Use require('../bowling', __FILE__) instead of require 'bowling'
This is basically the same as the above.
Other questions:
Q: Why am I getting a LoadError when bowling.rb and bowling_spec.rb are in the same folder?
A: Because the current directory (the directory you are running the script from, not the directory the files are located in) is not in the $LOAD_PATH.
Q: In the error what the heck is .../site_ruby/1.9.1/... when I am running ruby 1.9.2 then why would 1.9.1 even show up?
A: Hmm. Not sure I remember exactly, but IIRC, it was something like they're so similar that the interface hadn't changed, so they could be compatible with 1.9.1 from a system perspective.
Q: how do I get over this hump and start having fun with ruby.
A: I suppose that depends. If the issue is that you want to be able to run files that are in your CWD, then you can add the environment variable RUBYLIB to . to your .bash_profile (or whatever the equivalent is on your system) which will tell Ruby to look in the current directory for files. This is prone to bugs, though (and it could lead to unintentional execution of Ruby files, which is a security risk). If you just mean "how do I start learning" or whats a fun project? Then check out one of my projects, Ruby Kickstart which, in six sessions, will take you through a pretty big portion of Ruby, and have you write and deploy a simple web app by the end of it.

When you require a file and don't specify an absolutely path to the file, Ruby looks on its load path (accessed within ruby as $LOAD_PATH or $:) and checks each directory there for the file you want. You cannot load bowling.rb because it's not in a directory on your load path.
The solution is one of two things:
Put the current directory on the load path:
$:.unshift File.expand_path('.')
This puts the full path to the current working directory on the load path.
Use require with the absolute path to the file:
require File.expand_path('../bowling', __FILE__)
A little additional info: File.expand_path returns the absolute path to the first parameter from the current working directory, unless a second parameter is given; then it uses that as the starting point. So the whole line could be read:
require /home/mike/src/something/bowling_spec.rb/../bowling

Related

Ruby: require works in gem, fails when running from source

Try creating a gem based on bundler's official guide on developing a Ruby gem.
Running bundle gem foodie will create a structure and generate files in the lib directory:
foodie
version.rb
foodie.rb
foodie.rb reads
require "foodie/version"
module Foodie
# Your code goes here...
end
Running ruby lib/foodie.rb (or also from different directories) will result in
C:/Ruby23-x64/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- foodie/versio
n (LoadError)
from C:/Ruby23-x64/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from foodie.rb:1:in `<main>'
On the other hand installing the gem via rake install and then requiring the gem works just fine.
It works from source if require "foodie/version" is changed to require_relative "foodie/version" in foodie.rb. As I understand
require works based on modules
require_relative works based on directory structure
To me the latter looks like a hack. It'd no longer make sense to structure your code via modules as it wouldn't be enforced (maybe it'd still make sense but you could make mistakes and never notice).
My questions are:
Is it possible to test a gem from source without installing it while following the bundler convention (using require instead of require_relative)?
Why does the gem work after installed?
Is there any best practice for the usage of require, require_relative, modules, files and general structure?
Thank you.
You need to add your lib dir to Ruby’s load path. The load path is a list of directories that Ruby searches for files in when you call require. Rubygems also manages the load path when you are using gems, which is why your code works when installed as a gem.
You say “as I understand ... require works based on modules”, this is not correct. require works with files, it’s just convention that a class or module is defined in a file with a matching name, e.g. MyModule might be in my_module.rb.
There are a few ways to add a dir to the load path. From the command line you can use the -I option:
$ ruby -I lib lib/foodie.rb
If you wanted to avoid typing -I lib you could use the RUBYLIB environment variable. Ruby adds the contents of this to the load path:
$ export RUBYLIB=lib
$ ruby lib/foodie.rb
(On Windows I think you will need to use set rather than export.)
You can also manipulate the load path from withing the program itself. It is stored in the global variable $LOAD_PATH, aliased as :$. This is how Rubygems and Bundler manage your gems.

How can I load other scripts into the main script? [duplicate]

Is there any major difference between load and require in the Ruby on Rails applications? Or do they both have the same functionality?
require searches for the library in all the defined search paths and also appends
.rb or .so to the file name you enter. It also makes sure that a library is only
included once. So if your application requires library A and B and library B requries library A too A would be loaded only once.
With load you need to add the full name of the library and it gets loaded every time you
call load - even if it already is in memory.
Another difference between Kernel#require and Kernel#load is that Kernel#load takes an optional second argument that allows you to wrap the loaded code into an anonymous empty module.
Unfortunately, it's not very useful. First, it's easy for the loaded code to break out of the module, by just accessing the global namespace, i.e. they still can monkeypatch something like class ::String; def foo; end end. And second, load doesn't return the module it wraps the code into, so you basically have to fish it out of ObjectSpace::each_object(Module) by hand.
I was running a Rails application and in Gemfile, I had a specific custom gem I created with the option "require: false". Now when I loaded up rails server or rails console, I was able to require the gem in the initializer and the gem was loaded. However, when I ran a spec feature test with rspec and capybara, I got a load error. And I was completely bewildered why the Gem was not found in $LOAD_PATH when running a test.
So I reviewed all the different ways that load, require, rubygems and bundler interact. And these are a summary of my findings that helped me discover the solution to my particular problem:
load
1) You can pass it an absolute path to a ruby file and it will execute the code in that file.
load('/Users/myuser/foo.rb')
2) You can pass a relative path to load. If you are in same directory as file, it will find it:
> load('./foo.rb')
foo.rb loaded!
=> true
But if you try to load a file from different directory with load(), it will not find it with a relative path based on current working directory (e.g. ./):
> load('./foo.rb')
LoadError: cannot load such file -- foo.rb
3) As shown above, load always returns true (if the file could not be loaded it raises a LoadError).
4) Global variables, classes, constants and methods are all imported, but not local variables.
5) Calling load twice on the same file will execute the code in that file twice. If the specified file defines a constant, it will define that constant twice, which produces a warning.
6) $LOAD_PATH is an array of absolute paths. If you pass load just a file name, it will loop through $LOAD_PATH and search for the file in each directory.
> $LOAD_PATH.push("/Users/myuser")
> load('foo.rb')
foo.rb loaded!
=> true
require
1) Calling require on the same file twice will only execute it once. It’s also smart enough not to load the same file twice if you refer to it once with a relative path and once with an absolute path.
2) require returns true if the file was executed and false if it wasn’t.
3) require keeps track of which files have been loaded already in the global variable $LOADED_FEATURES.
4) You don’t need to include the file extension:
require 'foo'
5) require will look for foo.rb, but also dynamic library files, like foo.so, foo.o, or foo.dll. This is how you can call C code from ruby.
6) require does not check the current directory, since the current directory is by default not in $LOAD_PATH.
7) require_relative takes a path relative to the current file, not the working directory of the process.
Rubygems
1) Rubygems is a package manager designed to easily manage the installation of Ruby libraries called gems.
2) It packages its content as a zip file containing a bunch of ruby files and/or dynamic library files that can be imported by your code, along with some metadata.
3) Rubygems replaces the default require method with its own version. That version will look through your installed gems in addition to the directories in $LOAD_PATH. If Rubygems finds the file in your gems, it will add that gem to your $LOAD_PATH.
4) The gem install command figures out all of the dependencies of a gem and installs them. In fact, it installs all of a gem’s dependencies before it installs the gem itself.
Bundler
1) Bundler lets you specify all the gems your project needs, and optionally what versions of those gems. Then the bundle command installs all those gems and their dependencies.
2) You specify which gems you need in a file called Gemfile.
3) The bundle command also installs all the gems listed in Gemfile.lock at the specific versions listed.
4) Putting bundle exec before a command, e.g. bundle exec rspec, ensures that require will load the version of a gem specified in your Gemfile.lock.
Rails and Bundler
1) In config/boot.rb, require 'bundler/setup' is run. Bundler makes sure that Ruby can find all of the gems in the Gemfile (and all of their dependencies). require 'bundler/setup' will automatically discover your Gemfile, and make all of the gems in your Gemfile available to Ruby (in technical terms, it puts the gems “on the load path”). You can think of it as an adding some extra powers to require 'rubygems'.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
2) Now that your code is available to Ruby, you can require the gems that you need. For instance, you can require 'sinatra'. If you have a lot of dependencies, you might want to say “require all of the gems in my Gemfile”. To do this, put the following code immediately following require 'bundler/setup':
Bundler.require(:default)
3) By default, calling Bundler.require will require each gem in your Gemfile. If the line in the Gemfile says gem 'foo', :require => false then it will make sure foo is installed, but it won’t call require. You’ll have to call require('foo') if you want to use the gem.
So given this breadth of knowledge, I returned to the issue of my test and realized I had to explicitly require the gem in rails_helper.rb, since Bundler.setup added it to $LOAD_PATH but require: false precluded Bundler.require from requiring it explicitly. And then the issue was resolved.

Why doesn't Ruby 'require' allow relative paths?

This SO post sort of answers the question by claiming that require will only search relative to the path that the script was run in. But this really does not appear to be true. I will elaborate.
I created a quick C extension and compiled it to mytest.so. Then, in the same directory I fired up irb.
irb(main):009:0> require 'mytest.so'
LoadError: cannot load such file -- mytest.so
This is expected because the claim in the other answer is that require is searching relative to where irb was run from. In my case that would be /usr/bin/irb. So I tried the required_relative solution from the other question:
irb(main):006:0> require_relative './mytest.so'
LoadError: cannot infer basepath
No luck. And FYI - mytest.so is tab-completing here so irb is clearly aware it is in the current working directory. Furthermore, I can easily prove this:
irb(main):004:0> system("pwd")
/home/mike/Documents/ruby_test
# => true
irb(main):005:0> File.expand_path("./")
# => "/home/mike/Documents/ruby_test"
Okay final test, I will assume that irb is being executed in /usr/bin despite the evidence pointing against that.
irb(main):011:0> require '../../home/mike/Documents/ruby_test/mytest.so'
LoadError: cannot load such file -- ../../home/mike/Documents/ruby_test/mytest.so
I would greatly appreciate if anyone can shed some light on what is going on with require?
BTW, I am aware I can solve this issue by giving the exact file path. This question is about trying understand what is happening beneath the surface.
require '/home/mike/Documents/ruby_test/mytest.so' # this works
tl;dr: IRB is special and has some odd rules. Ruby in general works just fine with relative paths.
require will search the load path (which you can see by inspecting $: or $LOAD_PATH). This will not include the directory that you launched IRB from:
> $:
=> ["/usr/local/rvm/rubies/jruby-head/lib/ruby/2.2/site_ruby", "/usr/local/rvm/rubies/jruby-head/lib/ruby/stdlib"]
So there's no joy there, unless you explicitly add your directory to the load path. This is what Rubygems and Bundler spends most of their time doing - they manage the load paths for gems so you don't have to worry about it. However, this doesn't help you with single files.
Additionally, require_relative will search from the directory that __FILE__ is in, but in IRB, this is a non-directory (irb) value! This is why you get the "can't infer basepath" issue when trying require_relative from IRB; since the currently executing file, __FILE__, isn't a proper path, require_relative can't figure out where to start from.
When you are not running from IRB, this isn't really an issue; require_relative 'mytest.so' should work just fine when you execute it in a script, since the currently-executing script will populate __FILE__. That is, if you have loader.rb and mytest.so and execute loader via ruby loader.rb, require_relative should work just fine.
If you want to run this in IRB, consider something like:
require "#{__dir__}/mytest.so"
which will expand out to the current working directory, which should by default be the directory you've launched it from. I would recommend that you not do this in a script, though, since it depends on __dir__ not having been changed, and that may be difficult to guarantee.
From the documentation:
Loads the given name, returning true if successful and false if the
feature is already loaded.
If the filename does not resolve to an absolute path, it will be
searched for in the directories listed in $LOAD_PATH ($:).
If the filename has the extension “.rb”, it is loaded as a source
file; if the extension is “.so”, “.o”, or “.dll”, or the default
shared library extension on the current platform, Ruby loads the
shared library as a Ruby extension. Otherwise, Ruby tries adding
“.rb”, “.so”, and so on to the name until found. If the file named
cannot be found, a LoadError will be raised.
For Ruby extensions the filename given may use any shared library
extension. For example, on Linux the socket extension is “socket.so”
and require 'socket.dll' will load the socket extension.
The absolute path of the loaded file is added to $LOADED_FEATURES
($"). A file will not be loaded again if its path already appears in
$". For example, require 'a'; require './a' will not load a.rb again.
The answer to your question is that it simply isn't designed to.
You could use require_relative if you want to require files relative to your files' path.
You could add your projects library folder to the $LOAD_PATH to get the functionality you are asking for, that is, require 'my_library'
In IRB for loading local files, you may want to use load instead, as it gives you the ability to load file/library multiple times, where require will load the file/library only once. The __FILE__ variable works in IRB just like it should. It is the identification of the file (in this case the open process) in which the variable is invoked.

How to change working directory in JRuby?

I have to run my Ruby script from path that is higher than a script. My Ruby file is in folder lib. I start it in console:
jruby --1.9 -Clib main.rb
but it doesn't work correctly. It changes Dir.pwd, but require doesn't see it and another library DataMapper doesn't see it too.
I know I can add path to be seen by require by -Ilib, but it doesn't fix DataMapper issue and it is ugly I think.
require loads a file from the $LOAD_PATH. If the directory the file you want to load is in is not on the $LOAD_PATH, then require won't find it. If you want to load a file not from the $LOAD_PATH but relative to the position of the currently executing file, you need to use require_relative.
Assuming this is your folder structure
app/other/some_class.rb
app/lib/main.rb
If you navigate to the lib folder
cd app/lib
Then run your main.rb script
jruby main.rb
You can refer to the some_class.rb file in your main.rb script with this line
require "../other/some_class.rb"

Ruby Module Help -- Looking in wrong directory?

Hey everyone! I am having trouble with understanding modules -- I have two files, one named "modfile.rb" with the module, and one named "main.rb" that runs the code:
# modfile.rb
module Module1
def method1
puts "SUCCESS!"
end
end
# main.rb
require 'modfile'
Module1.method1
Unfortunately, instead of SUCCESS! appearing on my screen, I get this:
<internal:lib/rubygems/custom_require>:29:in 'require': no such file to load -- modfile (LoadError)
from <internal:lib/rubygems/custom_require>:29:in 'require'
from main.rb:1:in '<main>'
I think (though I may be wrong) that Ruby is looking to the lib/.... file inside the Ruby directory on my computer, while modfile.rb is saved in the same directory as main.rb. How do I fix this problem (other than by moving the module's file?)
PS. one guide suggested I add the line "require 'rubygems'" but I already did and got the same error.
Check into the differences between require and require_relative in Ruby: require vs require_relative - best practice to workaround running in both Ruby <1.9.2 and >=1.9.2
In Ruby 1.9 the . directory was removed from the search path. To fix the problem this generated they added require_relative.
If modfile.rb and main.rb are in the same directory, make sure that you aare calling main.rb from the directory it's in, ie:
ruby main.rb
As I believe that is the directory that the Ruby interpreter will be looking in for any require files.
Edit: as #the-tin-man points out, the behaviour has changed for Ruby 1.9.
To be completely on the safe side, you can do:
require File.join(File.dirname(__FILE__), "modfile")
One other thing:
def method1
... should be:
def self.method1
... since you are calling the method as a class level method.

Resources