Questions about building a new gem using Wycats template - ruby

I'm writing a new gem I'm basing off of Yehuda's new gem template and I'm slightly confused. Having a Gemfile with dependencies, and also specifying a gemspec with dependencies seems redundant to me. Can someone explain why this is desirable and if it's truly necessary?
Note, this is the first gem I've ever written so I'm new to all of this.

The .gemspec dependencies tell rubygems what it needs to grab to resolve the dependencies when a user installs your gem. The Gemfile is to manage the dependencies while you develop the gem. Rubygems and Bundler aren't connected, at least not yet.

The gemspec is required to build the gem. The Gemfile is more of a convenience, so that people who are working on your gem can easily pull in all the dependencies via Bundler. In the case that you're developing multiple related gems at once, you may also want to put git sources in the Gemfile, so all of the HEAD versions can be tested against.

Related

How to create interdependent gems with bundler, and use repositories as the source?

How is it possible to create interdependent gems, and still use Bundler?
Right now, if the Gemfile for library-a reads like this:
gem 'library-b'
gem 'library-c'
And the Gemfile for library-b reads like this:
gem 'library-a'
gem 'library-c'
And the Gemfile for library-c reads:
gem 'library-a', github: 'library-root/library-a', branch: 'master'
gem 'library-b', github: 'library-root/library-b', branch: 'master'
When you try to run bundle update and/or bundle install for library-c, this is the output:
Your Gemfile requires gems that depend depend on each other,
creating an infinite loop. Please remove either gem 'library-a'
or gem 'library-b' and try again.
A variation of the same message will happen for library-a or library-b.
But if you use the following for library-c, and use the same style for library-b and library-a, this will work:
gem.add_dependency 'library-a'
gem.add_dependency 'library-b'
However now, you will need to cut and push gems to RubyGems to test each revision, especially when using Travis CI. Whereas if you'd used Gemfile definitions, even if you also used gemspec definitions, the code being run or tested would have been pulled from a git repository, and could be updated up-to-the-minute without cutting and pushing gems.
I've posted two issues about this.
https://github.com/bundler/bundler/issues/3594
https://github.com/bundler/bundler/issues/3597
I need to have interdependent gems, and use git repositories as the source of the gem code.
For those who say just don't do it ... "it's Circular Dependency", etc. I understand, but let's think about this. Is a gem really that complicated? It's just files with a certain structure. True, it has version information, and "dependencies" associated -- but those dependencies just need to be present. If a gem of the appropriate version exists already, it ought to just break out of a "circular dependency" and say, "hey, the gem's already here, let's just move forward" then.
Bundler is a package management system; the above is not Circular Dependency of objects it is Interdependency of files ... if it were Objects inheriting from each other, definitely... I surrender. But this is a point where one package assumes another exists in the filesystem.
You could change the word depends to expects the presence of not requires specifically sequential instantiation of.
Again gemspec works. If it were Circular Dependency that would be impossible. But gemspec is clearer on its purpose as part of a package management system. It lets files be files.
gemspec behavior? Both gems need each other? Cool! Download both gems. Easy, done.
Gemfile behavior? Both gems need each other? Wait, what? Implode.
I want gemspec "dependency" behavior, with Gemfile source specificity power. Otherwise I'd just keep using gemspec only.
Bundler is warning you that your gems have a circular dependancy. Namely library-a depends on library-b and the other way around. This is a situation that should be avoided.
If 2 libraries can't exist without each other it's best to just make them one.

Is it still useful to declare development dependencies in a gemspec when using bundler?

I am working on a new Ruby Gem. I am familiar with using Bundler to manage gems:
source "https://rubygems.org"
gemspec
gem 'rspec-rails'
I am familiar with specifying dependencies in a gemspec file:
Gem::Specification.new do |s|
# ...
s.add_dependency "rails", "~> 4.1.5"
end
The Gemfile that was generated mentions that I should move my dependency declarations from my Gemfile to my gemspec when I'm ready to release.
# Declare any dependencies that are still in development here instead of in
# your gemspec. These might include edge Rails or gems from your path or
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.
Why would I want to do this? Why should my gemspec care what gems I'm using in development? What purpose does development_dependency serve that Bundler doesn't already do for me?
To best answer your questions, we should first untangle the concepts of Bundler and Rubygems. I think a great explanation can be found here.
Why Would I Want to [move dependencies from Gemfile to .gemspec]?
Gemfile allows you to specify not only dependencies, but where the dependencies come from. This is useful when you are also working on the dependencies themselves and need to point to a Git repo (or something).
Once work on these dependencies is complete, Rubygem conventions dictate that you move the dependency declaration on these released gems into your .gemspec file. Adding a line gemspec tells Bundler to read from this conventional Rubygems location. If you are working on a gem, and you are not actively developing the gem's dependencies, then all dependencies should be declared in your .gemspec
Why should my gemspec care what gems I'm using in development?
From the docs for add_development_dependency:
Development dependencies aren't installed by default and aren't activated when a gem is required.
A popular example for this Rspec. You should generally declare Rspec as a development dependency for yourself, but not force everyone else to download it when they grab your gem.
Note that the comment you cited does not say "development dependencies", it says "dependencies that are in development". Dependencies that are in development commonly include cutting-edge versions of gems that you are installing directly from a Git repo. RubyGems cannot install gems from a Git repo; however, Bundler can. If you are installing cutting-edge versions of gems installed from a source that RubyGems cannot handle (such as a VCS repo), you should list them in your Gemfile instead of the .gemspec file.

Find out binary dependencies of gem

I want to build rpm with my Ruby app and its gems. As far as some gems depends on binaries I want to find out them automatically. For example nokogiri gem depends on libxml. Can I fetch all this dependencies for all gems in Gemfile automatically to pack them into rpm?
Now I do it mannualy and this is boring!
No, unfortunately, this is not possible. RubyGems only records Gem dependencies. It obviously cannot record any dependencies it doesn't know about, how would that even work?
There is a requirements attribute in the gemspec, but that is only intended to be human-readable, not machine-readable.

Where do I have to do my Bundler setup?

If I'm developing a gem using Bundler and RSpec for testing. Where do I do my Bundler.setup? Let's assume my gem is called fancy-gem and my directory setup is similar to the following:
Gemfile
Gemfile.lock
lib/
fancy-gem.rb
Rakefile
README
spec/
...
Should I execute Bundler.setup in my 'fancy-gem.rb' or does this cause problems with other gems which might use bundler? I'm thinking, when I'm not doing this, then there is no way to guarantee that the right version of the third party libraries I'm requiring is loaded.
I already asked, if I need to add Bundler itself to the Gemfile. The answer was no, but now I'm not so sure, because if I do execute Bundler.setup somewhere then Bundler actually is a dependency of my Gem and should be installed along with my Gem when it is downloaded from rubygems.org
In my opinion you should neither depend on bundler, nor use it in your gem. The way I'd do it is simply require your gem's dependencies in lib/fancy-gem.rb (almost every gem only has a handful of runtime dependencies, so this should not be too much of a hassle) and I'd call Bundler.setup only in the development files (like spec_helper.rb or Rakefile). This way you don't screw with applications that use your gem and still get all the convenience of automatic dependency management when developing your gem.

unpacking/freezing gems into a non-rails ruby app

I'm writing a non-Rails ruby application (gasp!) and would like to be able to include all the gem dependencies which the application requires in a vendor subdirectory. This would be similar to how http://gemsonrails.rubyforge.org/ works for Rails apps.
The goal here is to avoid the situation my team currently experiences when a new dependency is added. Every developer on my team has to install the gem manually, and then someone has to manually update each test and staging and production machine. If we can freeze the dependencies into the distributed application itself then a simple svn update (or git pull for those hipsters in the crowd) would be all that is needed.
UPDATE (New Solution):
Try Yehuda Katz's new bundler gem. gem install bundler then create a Gemfile with all your dependencies. See the documentation for more info.
Old Recommendation:
One easy way is to just manually unpack the gems into your vendor directory and add the lib path of the unpacked gems to the front of the $LOAD_PATH.
To unpack a gem:
$ cd vendor/gems
$ gem unpack active_support
Unpacked gem: '/path/to/myproject/vendor/gems/activesupport-2.3.2'
Just make sure you unpack all the necessary gems and their dependencies (using the correct versions).
To add all gems under vendor/gems to your $LOAD_PATH, try adding something like this to your application's initialization:
Dir.glob(File.join("vendor", "gems", "*", "lib")).each do |lib|
$LOAD_PATH.unshift(File.expand_path(lib))
end
Update: Sarah (in the comments) convinced me it might also be necessary to override the GEM_PATH. Here's one way to do that:
require 'rubygems'
gem_paths = [File.expand_path(File.join("vendor", "gems")), Gem.default_dir]
Gem.clear_paths
Gem.send :set_paths, gem_paths.join(":")
Another option is to look into Rip (Ruby’s Intelligent Packaging) for managing your dependencies. Rip looks really sweet, but it's still new.

Resources