Ruby: Accessing rake task from a gem without Rails

2019-01-15 06:39发布

问题:

I'm aware that Rake tasks can be defined in a number of places within a Ruby gem:

  • inside a Rakefile
  • inside tasks/*.rake
  • inside lib/tasks/*.rake

I've read that the first two should be used when the tasks are to be executed on the gem itself. It seems the third option should be chosen when tasks are desired to be publicly available.

There are many tutorials online demonstrating a variety of methods to load Rake tasks from a gem using Rails, namely by utilising Rails::RailTie.

However, I'd like to find a way of using a dependency gem's tasks within another gem without needing Rails.

Is there a simple solution to this? Would someone be kind enough to describe the proper approach, or outline what approaches would be viable?

UPDATE

I've tried creating a file bin/my-gem to make available on the system for executing Rake tasks from my-gem. I've put the following inside;

#!/usr/bin/env ruby
require 'rubygems'
require 'rake'
task=ARGV[0]
spec = Gem::Specification.find_by_name('dsi_core')
Dir["#{spec.gem_dir}/lib/tasks/*.rake"].each {|file| puts file and Rake::load_rakefile(file)}
Rake::Task.clear # Avoid tasks being loaded several times in dev mode
Rake::Task[task].reenable # Support re-execution of a task.
Rake::Task[task].invoke

Some of the content was based on this SO post.

Sadly I'm doing something wrong because upon installing the gem then running my-gem mytask with mytask defined in lib/test.rake then the following is output:

/var/lib/gems/1.8/gems/rake-0.9.2/lib/rake/task_manager.rb:49:in `[]': Don't know how to build task 'mytest' (RuntimeError)
    from /var/lib/gems/1.8/gems/rake-0.9.2/lib/rake/task.rb:298:in `[]'
    from /var/lib/gems/1.8/gems/my_gem-0.0.1/bin/my_gem:8
    from /usr/local/bin/my_gem:19:in `load'
    from /usr/local/bin/my_gem:19

回答1:

I found the body of the solution here. I've modified it to support specification of tasks with arguments and added support for cucumber.

So..

Within your gem create bin/my_gem

Paste the script at bottom of this post into it. See comments for example usage.

Your rake tasks must be in your Rakefile.

Alternatively, add your tasks e.g. to lib/tasks/*.rake then add the following into your Rakefile:

Dir.glob('lib/tasks/*.rake').each {|r| import r}

Here's the secret sauce:

#!/usr/bin/env ruby

# Run rake tasks and cucumber features
# from my_gem once it's installed.
#
# Example:
#
#   my_gem rake some-task
#   my_gem rake some-task[args]
#   my_gem cucumber feature1 feature2
#
# Note: cucumber features have '.feature' appended automatically,
#       no need for you to do it ;)
#
# Author:: N David Brown
gem_dir = File.expand_path("..",File.dirname(__FILE__))
$LOAD_PATH.unshift gem_dir# Look in gem directory for resources first.
exec_type = ARGV[0]
if exec_type == 'rake' then
    require 'rake'
    require 'pp'
    pwd=Dir.pwd
    Dir.chdir(gem_dir) # We'll load rakefile from the gem's dir.
    Rake.application.init
    Rake.application.load_rakefile
    Dir.chdir(pwd) # Revert to original pwd for any path args passed to task.
    Rake.application.invoke_task(ARGV[1])
elsif exec_type == 'cucumber' then
    require 'cucumber'
    features = ARGV[1,].map{|feature| "#{gem_dir}/features/#{feature}.feature"}.join(' ')
    runtime = Cucumber::Runtime.new 
    runtime.load_programming_language('rb') 
    pwd=Dir.pwd
    Dir.chdir(gem_dir) # We'll load features from the gem's dir.
    Cucumber::Cli::Main.new([features]).execute!(runtime)
    Dir.chdir(pwd) # Revert to original pwd for convenience.
end

Bingo! :-)



回答2:

This post gave me some hints, but perhaps for others that bump into this, I struggled with some dependency issues. I finally worked out how to do this with a bin/binstub and without requiring a rakefile. My solution is posted on another SO question.