Own RubyGems: internal paths - ruby

I'm actually writing my own RubyGem.
How do I get the path to the RubyGem, i.e. for requiring?
require "./mygeminternally.rb"
# => the path (actually "./") must be the path to the internals of the gem while it is being compiled
Is it just Dir.getwd? I need a method that brings me the path to the gem so I can use internal templates. Be aware that templates or configuration files have to be accessed while the gem is compiled, is this so easily possible like picking the right path?
Yours,
Joern

In a gem you should always use paths relative to your lib dir and never try to evaluate absolute paths because this can cause problems in some edge cases.
When I say lib I actually mean one of the directories added to require_paths in the .gemspec file, but in almost all cases this is set to just ['lib'].
This is all described in detail in the RubyGems Guide (as mentioned by #an4rco in his comment -- give him an upvote).

Related

What is the built gemspec file (with version) for?

When I run the command
$: gem build example_gem.gemspec
I get a file called example_gem-0.0.0.gem. What is in this file and what is it for?
Does this file play a part with how gems get included into Ruby code that requires them once the gem is already installed on the machine?
I noticed that when I go into my INSTALLATION_DIRECTORY, I can see the gems in folders with names matching matching the format of this versioned gem file, but inside the directory, I only see the other library code, gemspec file, etc.
Does this mean it is for uploading/downloading/installing only?
The .gem file is, well, the Gem. It contains the Gemspec (more precisely, a Marshal serialized version of the object graph of the Gemspec, rather than the executable .gemspec while which generates that object graph) as well as a compressed archive of all the files that are part of that gem (listed in the Gemspec). It also contains a cryptographically secure checksum and an optional cryptographic signature.
When you install a .gem, RubyGems checks the cryptographic checksum to make sure the Gem was not tampered with, checks the optional signature to make sure the Gem was created by who you think it was, reads the Gemspec to figure out what to do with the Gem, and unpacks it into the $GEM_HOME directory.
Kernel#require simply goes through the search path until it finds a file matching the name you provided and runs the file. It knows nothing about Gems. (It does know how to find the unpacked Gem directory, though.)
[Note: this is somewhat simplified. Kernel#require may have implementation-specific features, for example, YARV's Kernel#require also knows how to load dynamic libraries (e.g. .dlls, .sos, .dylibs, depending on the OS), JRuby's Kernel#require also knows how to load .jars, and so on.]

What is the conventional purpose of the "lib" directory in Ruby projects?

Take this ruby gem: https://github.com/ai/autoprefixer-rails
The gem's structure includes a "lib", "spec" and "vendor" directory, and some gemfiles in the root directory.
What exactly is the purpose of directories called "lib" in ruby gems?
It looks like "lib" contains all of the ruby code provided by the gem. So does "lib" mean "the source code files that constitute the library?" (It's hard to say if "lib" means "the library," since arguably the gemfiles are "part of the library," too.) Or does it mean something else? And is the meaning of the directory names in ruby gems defined anywhere? Or is directory naming completely arbitrary?
I am trying to determine if the name "lib" has any special significance as I set out to write my own gem. I'd like to know, because if it doesn't, I might like to pick a different name.
The default "require path," i.e. the directory which will by default be added to $LOAD_PATH when the gem is activated, is "lib". See the rubygems specification:
REQUIRE_PATHS=(val)
Paths in the gem to add to $LOAD_PATH when this gem is activated. If
you have an extension you do not need to add "ext" to the require
path, the extension build process will copy the extension files into
“lib” for you.
The default value is "lib"
Usage:
# If all library files are in the root directory...
spec.require_paths = ['.']
The fact the name is chosen as a default implies it is also a good default naming decision to avoid extra configuration and to help navigate newcomers to the gem.

How to require ruby gem from a specific location?

I am new to ruby, and is trying to write a git hook in ruby. I want to use the rugged gem in my script. The gem is already available in /opt/gitlab/embedded/service/gem/ruby/2.1.0/ as part of GitLab installation (Files List). How can I require that gem in my script?
I have tried $LOAD_PATH.unshift "/opt/gitlab/embedded/service/gem/ruby/2.1.0/" and then require "rugged", as mentioned in another stackoverflow answer, but it did not work.
Look carefully at how $LOAD_PATH is configured for each gem after you include it. Normally it's the full path to where the base gemname.rb is located, like in your case where rugged.rb is.
The way the $LOAD_PATH works is it scans through that list looking for rugged.rb and if it doesn't find it, moves on to the next possible location. You're specifying a directory which doesn't have that, so find the right path and fill that in, and you should be good.

How to I load a gem from source?

I have git cloned a repo from Github, now I want to experiment with it, as in I want to poke around the code and mess with it. I've created a file test.rb that should load this gem, but I want to load my locally checked out version, what's the right way to do this?
Right now I'm just using a bunch of "require_relative 'the_gem_name/lib/file'", which feels wrong.
When you require 'foo' Ruby checks all the directories in the load path for a file foo.rb and loads the first one it finds. If no file named foo.rb is found, and you’re not using Rubygems, a LoadError is raised.
If you are using Rubygems (which is likely given that it is included in Ruby 1.9+), then instead of immediately raising a LoadError all the installed Gems are searched to see if one contains a file foo.rb. If such a Gem is found, then it is added to the load path and the file is loaded.
You can manipulate the load path yourself if you want to ensure a particular version of a library is used. Normally this isn’t something that’s recommended, but this is the kind of situation that you’d want to do it.
There are two ways of adding directories to the load path. First you can do it in the actual code, using the $LOAD_PATH (or $:) global variable:
$LOAD_PATH.unshift '/path/to/the/gems/lib/'
require 'the_gem'
Note that you normally want to add the lib dir of the gem, not the top level dir of the gem (actually this can vary depending on the actual Gem, and it’s possible to need to add more than one dir, but lib is the norm).
The other way is to use the -I command line switch to the ruby executable:
$ ruby -I/path/to/the/gems/lib/ test.rb
This way might be a bit cleaner, as normally you don’t want to be messing with the load path from inside your code, but if you’re just testing the library it probably doesn’t matter much.
Following apneadiving's suggestion in the comments, I created a Gemfile and added this line
source "http://rubygems.org"
gem 'gem_name', path: '~/path/to/gem/source/folder'
Then bundle install, and bundle exec ruby test.rb and it worked.

Creating a gem with Bundler

I'm trying to create a gem with Bundler, following this guide: http://rakeroutes.com/blog/lets-write-a-gem-part-one/. In it, it says:
I incorrectly thought after taking my first look through the gemspec
that I would need to add more require statements as I developed my
gem. That isn’t the case: the files just need to be in git.
I am trying to clean up some of my old gems to use this convention, but when I install my gem, the classes from the other files are not available. I have a couple directories nested under my /lib dir, but I wouldnt think that would be an issue. Is there anything simple to overlook that would prevent my other files from being required? Any help would be appreciated.
In the link, when he says he doesn't need to add a lot of "require" statements, he must mean adding files to the s.files, s.executables, and s.test_files arrays--these determine what files get packaged up into the gem and what files get ignored. As you can see from the gem spec, whatever's tracked by git in certain directories is going to be included in the packaged gem.
Ruby's require is a different story. Standard require rules still apply.
Ruby's gem system works by adding a bunch of different places for Ruby to look for "foo.rb" when you run require "foo". If "lib" is your only require path for your gem, when you require "my_gem" Ruby is only going to run the code in lib/my_gem.rb. If lib/my_gem.rb doesn't require any other files in your gem, then Ruby hasn't seen them and so you'll get undefined constant errors when you try to use the classes from those files.
For examples, you might take a look at two simple gems I've written; both were started with bundle gem: HashToHiddenFields and SimpleStats. In both gems, main Ruby file in lib/ requires everything that needs to be loaded for the gem to work correctly. For example, hash_to_hidden_fields.rb requires action_view/helpers/hash_to_hidden_fields so that the ActionView::Helpers::HashToHiddenFields constant+module exists so we can include it into ActionView::Base.
Hope that answers the question. I know Ruby requiring was pretty fuzzy to me for a while.

Resources