Why can't I require a gem from within the .gemspec? - ruby

I can't require third party code in my .gemspec file. I would like to require a semver library to manage my version number. Why can't I require gems inside my .gemspec?
I have a workaround for my particular problem. What I am interested in the specific reason I can't require code from inside a gemspec. I feel like I am missing something about how Rubygems work. Thanks!
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'semver'
Gem::Specification.new do |spec|
spec.add_dependency "semver2", "~> 3.4.2"
spec.version = SemVer.find.to_s[1..-1]
end
Running bundle install results in the following error:
$ bundle
[!] There was an error parsing `Gemfile`: There was a LoadError while loading example.gemspec:
cannot load such file -- semver from
/opt/pose/auto/stack-overflow/example/example.gemspec:3:in `<main>'
Obviously there is some point before which you cannot depend on 3rd party gems to be on your LOAD_PATH. I am asking for an explanation of where that point is. My guess is because my Gemfile references my gemspec and Bundler had not loaded all the dependencies yet, but that is a guess.

A .gemspec is just a Ruby script like any other Ruby script. Basically the only thing that is "special" is that it is run in an environment where certain libraries have already been required, such as rubygems/specification.
requireing libraries works just like in any other Ruby script.

Related

Created a simple gem, but irb can't see it

I created a simple gem by doing:
testgem.gemspec
Gem::Specification.new do |s|
s.name = 'testgem'
s.version = '0.0.1'
s.summary = "code generation"
s.authors = ['asdf']
end
created the following file layout:
testgem.gemspec
Gemfile
lib/
lib/testgem.rb
lib/testgem/other.rb
ran the following:
gem build testgem.gemspec
WARNING: no description specified
WARNING: no email specified
WARNING: no homepage specified
Successfully built RubyGem
Name: testgem
Version: 0.0.1
File: testgem-0.0.1.gem
installed it:
gem install ./testgem-0.0.1.gem
Successfully installed testgem-0.0.1
1 gem installed
tested to see if irb can see it:
irb
require 'testgem'
oadError: cannot load such file -- testgem
I'm using Ruby 1.9.3-p194
It seems to be installed if I do:
gem list
..
testgem (0.0.1)
..
What could be the issue?
You need to add your lib to the search path. Just add
$:.push File.expand_path("../lib", __FILE__)
on top of your .gemspec
When you use require in Ruby, you are trying to load a file, not a gem. The file could of course be contained in a gem, and by convention a gem will have a file with the same name as the gem itself so that if you wanted to use the gem foo you would use require 'foo'. This isn’t always the case though, for example to use the bcrypt-ruby gem you need to require 'bcrypt'.
Additionally a gem doesn’t necessarily have to contain any filles at all. An example of this is the rails gem, which doesn’t contain any files itself (at least in version 3.2), but has dependencies to the various Rails components, so that you can install them all in one step.
In your case, although you have a lib/testgem.rb file in your gems project directory, you are not including it in your gem. You need to specify which files should be included, the default is to not include anything.
To simply include all the files in your project directory, you can add something like:
s.files = Dir.glob '**/*'
to your gemspec.
You need to require 'rubygems' first.

Strange require statement errors in Ruby?

I've got a wrapper for my Gem, socks, inside socks.rb. The entire file is made up of require statements, and a module declaration:
# lib/socks.rb
require 'socks/version'
require 'socks/base_controller'
require 'socks/templates'
require 'socks/tasks'
require 'socks/rake_tasks'
module Socks
end
However, require 'socks/tasks' and socks/rake_tasks is giving me a LoadError: no such file to load -- socks/tasks / rake_tasks error.
Is this a problem with the alignment of the require statements, or just the code?
Code is on Github: https://github.com/Beakr/socks
EDIT: require './socks/tasks' is now working, however require './socks/rake_tasks' is not.
Ruby load files using its $LOAD_PATH. This global array is changed by e.g. rubygems and bundler to allow to find libraries in various locations. In your sock.gemspec you have defined
gem.require_paths = ["lib"]
which means that rubygems will add the lib directory of your gem to ruby's $LOAD_PATH. But it odes so only if you have installed the gem and the gemspec is thus evaluated. If you don't want to install your gem, you can test your gem using
bundle exec irb
in your gem directory, or alternatively by first adapting your $LOAD_PATH in your irb session like so:
$LOAD_PATH.push "/path/to/your/gem/lib"
require 'socks'

How to install, require, and use a gem in ruby

I'm trying to use rake in my ruby script...(Ruby 1.8.6, JRuby 1.6.5)
Downloaded rake using gem install --remote rake, looks ok on install...
Fetching: rake-0.9.2.2.gem (100%)
Successfully installed rake-0.9.2.2
1 gem installed
I've got a simple ruby script which works fine, but when I import rake to using any of the following requires, it starts complaining....
require 'rake'
LoadError: no such file to load -- rake
or
require '/lib/rake'
LoadError: no such file to load -- lib/rake
After some searching, I found that adding require 'rubygems' just before rakefixes the issue....
require 'rubygems'
require 'rake'
Even though it's working, I've got some questions...
The gem spec on rake shows the require_path as lib, so why
doesn't require '/lib/rake' work? Am I misunderstanding the significance of require_path?
Why is it necessary to place require 'rubygems' before require
'rake'
Yes, you are misunderstanding the significance. The require_paths in the specification is an array of subdirectories of that gem's installation directory that should be searched for files belonging to the gem.
To find out where rake really is, try this:
$ gem which rake
You'll see that it is actually installed somewhere completely unrelated to /lib; on my system, it's under /var/lib/gems. Ruby gems, in general, live in their own directory structure; the only file in the standard Ruby include path ($:) is rubygems itself, which you used to have to explicitly require in order to make any actual gems visible. (Since Ruby 1.9, that has not been necessary.)
Gems are more complex than just libraries to load; you can have multiple versions of the same gem installed, and specify which one you want at a time, and do other things that wouldn't work if the gems were just dumped into the standard Ruby include path.
The require_path in the gemspec tells ruby where the files of this gem are located within the gem. It makes you able to type require 'rake', and ruby then knows it needs to look for /lib/rake within the gem installation folder.
In Ruby 1.8, rubygems (the mechanism responsible for making gems available to your app) is not loaded by default, and the default ruby isn't aware of any gem on your system. You need to load rubygems before being able to require any other gem. This is not the case anymore with Ruby 1.9.

Bundler: why does it read the gemspec on require "bundler/setup"?

The title is the question, and here's the context that prompts it.
The Gemfile:
source "http://rubygems.org"
# Specify your gem's dependencies in the gemspec
gemspec
Here is the top of the rackup file:
require 'rubygems'
require "bundler/setup"
On running the rackup file an error is thrown:
<module:Rack>': GemName is not a class (TypeError)
Why? Because I'm writing a piece of Rack middleware, and the standard layout is:
lib/
rack/
gem_name.rb
gem_name/
version.rb
gem_name.rb will contain:
module Rack
class GemName
version.rb will contain:
module Rack
module GemName
VERSION = "0.0.1"
Finally, the gem_name.gemspec will contain:
require "rack/flash-in-the-pan/version"
#...
s.version = Rack::GemName::VERSION
Naming a module and a class by the same name isn't a problem as long as you don't require both files at the same time. Normally, this wouldn't happen, as you either need the version for building the gem, or you need to run the gem library, only one or other gets required.
But, this time I decided to use Bundler to manage the gem's dependencies. When requiring the gem library via bundler it obviously runs the gemspec too. I can "fix" it easily enough, I define the version number by hand in the gemspec.
So back to my question - why does Bundler need to look in the gemspec at the library's runtime?
bundler (1.0.21)
Any insight is much appreciated.
Whenever you run bundler, it has to parse the Gemfile to actually figure out what gems need to be loaded, what has to be added to $LOAD_PATH and so on. As part of that, it has to parse gemspec.
The Gemfile.lock contains info on all of the gems as well as the dependencies to save startup time, but it doesn't alleviate the need for it to parse the Gemfile.
There are various ways you could work around it. Two simple ones would be to use File.read and some regex to pull out the version. Or require the gem_name.rb and gem_name/version.rb files.

Writing a Jeweler Rakefile that adds dependencies depending on RUBY_ENGINE (ruby or jruby)

I have a Rakefile that includes this:
Jeweler::Tasks.new do |gem|
# ...
gem.add_dependency('json')
end
The gemspec that this generates builds a gem that can't be installed on jruby because the 'json' gem is native.
For jruby, this would have to be:
Jeweler::Tasks.new do |gem|
# ...
gem.add_dependency('json-jruby')
end
How do I conditionally add the dependency for 'json-jruby' when RUBY_ENGINE == 'java'?
It seems like my only option is to manually edit the gemspec file that jeweler generates to add the RUBY_ENGINE check. But I'd rather avoid this, since it kind of defeats the purpose of using jeweler in the first place.
Any ideas?
After some more digging around, I've come to the conclusion that the problem here isn't so much the limitation in Jeweler as it is a problem with how the json-jruby gem is named.
In a perfect world, the json-jruby gem should be named just json. That way when the gem file is built under JRuby (i.e. jgem build json-jruby.gem), the resulting gem file would be named json-1.4.3-universal-java-1.6.gem (RubyGems names the file based on the 'platform' property in the gemspec). When installed with the appropriate platform in the gem name, everything works transparently. There's no need to explicitly require json-jruby. RubyGems uses the appropriate version automatically.
I've contacted the maintainer of the json gem to see if he'd be willing to get json-1.4.3-universal-java-1.6.gem added to his gemcutter repo, but in the meantime you can download the drop-in jruby json gem here: http://roughest.net/json-1.4.3-universal-java-1.6.gem

Resources