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

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.]

Related

Own RubyGems: internal paths

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).

Why is bundler using the wrong gemspec file?

I've got a custom gem that has been working just fine with regards to bundling, building, distributing, & implementing. The gem is the core of a framework from which other gems are derived. Since most derived gems will have the same basic structure, I want to include a Ruby script in the bin path of the gem that can be used to basically copy files from a template folder into a new folder where the user will develop their own gem.
The problem I'm having is that the template folder has a gemspec file named $name$.gemspec with similarly named classes/modules in the file (e.g.: module $Name$), where the $name$ gets replaced with a name provided by the user.
Unfortunately, when I run bundle install from my gem's top-most path, I get an error:
There was a SyntaxError while evaluating $name$.gemspec:
C:/my_gem/template/$name$.gemspec:8: syntax error, unexpected tGVAR
gem.version = MyGem::$Name$::VERSION
It looks like Bundler is using the wrong Gemfile, even if I explicitly pass the Gemfile or path via one of the following:
bundle install --gemfile=Gemfile
bundle install --path=C:\my_gem
I also tried updating the gemspec line of my Gemfile to no avail:
gemspec name: 'my_gem'
Lastly, I've ensured that the template folder isn't even included in my_gem.gemspec, but that doesn't seem to matter:
gem.files = Dir.glob("lib/**/*") + %w(LICENSE.txt README.md)
Does anyone know why Bundler is trying to read the ./template/$name$.gemspec instead of ./my_gem.gemspec?
Inspecting the Bundler source, I may have spotted the culprit in lib/bundler/source/path.rb. There's GLOB used to find gemspecs in load_spec_files. The default glob is "{,*,*/*}.gemspec". This will find *.gemspec in the root directory of your gem or any directory one descendant from root (which will include your template dir).
If this is indeed the culprit, you could work around this by placing your template directory deeper in your gem's dir hierarchy or changing the name of the template file so it doesn't end in .gemspec. The Bundler::Source::Pathobject looks like it can take a different glob at initilization but I haven't dug deep enough to see if there's a viable way to specify this alternative glob in bundle execution via config or cmdline options.

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.

Getting started with gems and jeweler

With Jeweler I created a gem folder structure with ease.
However, I still have some questions:
Why are params like --gemcutter and --rubyforge still available for Jeweler. Aren't these replaced by RubyGems? Do I have to specify anything to create a gem for RubyGems?
In the Rakefile I have information about the gem, and when I run "rake install" it created a gemspec. Why is the same information in two places?
What is a manifest? Just read about it, haven't seen such file.
How do I make my gem callable from the shell once I have installed it, like rails. Cause right now it's just accessible through a Ruby script using require.
Should I use "jeweler release" or "gem push" to push my gem to RubyGems.org?
I have to specify "handle" when signing up in RubyGems. What is that?
Thanks.
jeweler was created before RubyGems became what it is, so it still reflects the split. I'm not sure when jeweler was last updated, either. (I think it also still recognizes building gems on Github, which is now disabled.)
I'm not sure I follow what you're saying. The specification in the Rakefile details what the spec that gets written should look like. The spec that gets written details what should be installed and how, I believe.
A manifest is a list of all the files that your gem should ship with. Not everyone uses one. See the hoe documentation for some pro-manifest discussion.
Many Ruby gems are only libraries. If you want yours to also have a program like jeweler or rake or rails that you can call, you have to write the callable program, put it in bin in your gem's layout and specify (in your gemspec) that it should be packaged and installed. See the Gem::Specification reference under files and executable.
Not sure. Consult both jeweler's docs and the docs for RubyGems.
You can give an email address or use a name (a 'handle', like I use Telemachus here), which is all they mean by 'handle'.
For the record, if you are just learning how to write gems, you do not need to upload your first attempts using RubyGems or anything like it. You can simply install the gem on your machine only.

Resources