Last few days I’ve been writing ebuilds for some gems. There are a lot of problems with packed gems and many of them was discussed before. I want to talk about only one of them - evil Rakefiles.
Here’s example of evil Rakefile:
require 'rake' require 'spec' require 'cucumber' require 'cucumber/rake/task' require 'spec/rake/spectask' spec_files = Rake::FileList["spec/**/*_spec.rb"] desc "Run specs" Spec::Rake::SpecTask.new do |t| t.spec_files = spec_files t.spec_opts = ["-c"] end Cucumber::Rake::Task.new(:features) do |t| t.cucumber_opts = "features --format progress" end task :default => [:spec, :features]
You see those requires at the top? It means that you can not even list available tasks if you don’t have
Here’s the approach I propose. Write a stubs for tasks so that people could list tasks and run tasks without forcing them to install unnecessary stuff.
require 'rake' desc "Run specs" task :spec do unless Rake.application.lookup('real_spec') require 'spec' require 'spec/rake/spectask' spec_files = Rake::FileList["spec/**/*_spec.rb"] Spec::Rake::SpecTask.new(:real_spec) do |t| t.spec_files = spec_files t.spec_opts = ["-c"] end end Rake.application['real_spec'].invoke end desc "Run features" task :features do unless Rake.application.lookup('real_features') require 'cucumber' require 'cucumber/rake/task' Cucumber::Rake::Task.new(:real_features) do |t| t.cucumber_opts = "features --format progress" end end Rake.application['real_features'].invoke end task :default => [:spec, :features]
This Rakfile postpones requiring stuff until it’s really needed and so creating needed tasks and calling them. Yes this is a bit longer than previous variant but it also much nicer. Note, this is rather small Rakefile and it already depends on two gems I’ve seen Rakefiles that pull half a dozen gems for tests and about the same for docs. And each of those gems has their own dependencies. So I have to install a whole bunch of gems to just see the list of available tasks or run a task that is not related to tests or docs.