How do I run Rake tasks within a Ruby script? - ruby

I have a Rakefile with a Rake task that I would normally call from the command line:
rake blog:post Title
I'd like to write a Ruby script that calls that Rake task multiple times, but the only solution I see is shelling out using `` (backticks) or system.
What's the right way to do this?

from timocracy.com:
require 'rake'
def capture_stdout
s = StringIO.new
oldstdout = $stdout
$stdout = s
yield
s.string
ensure
$stdout = oldstdout
end
Rake.application.rake_require 'metric_fetcher', ['../../lib/tasks']
results = capture_stdout {Rake.application['metric_fetcher'].invoke}

This works with Rake version 10.0.3:
require 'rake'
app = Rake.application
app.init
# do this as many times as needed
app.add_import 'some/other/file.rake'
# this loads the Rakefile and other imports
app.load_rakefile
app['sometask'].invoke
As knut said, use reenable if you want to invoke multiple times.

You can use invoke and reenable to execute the task a second time.
Your example call rake blog:post Title seems to have a parameter. This parameter can be used as a parameter in invoke:
Example:
require 'rake'
task 'mytask', :title do |tsk, args|
p "called #{tsk} (#{args[:title]})"
end
Rake.application['mytask'].invoke('one')
Rake.application['mytask'].reenable
Rake.application['mytask'].invoke('two')
Please replace mytask with blog:post and instead the task definition you can require your rakefile.
This solution will write the result to stdout - but you did not mention, that you want to suppress output.
Interesting experiment:
You can call the reenable also inside the task definition. This allows a task to reenable himself.
Example:
require 'rake'
task 'mytask', :title do |tsk, args|
p "called #{tsk} (#{args[:title]})"
tsk.reenable #<-- HERE
end
Rake.application['mytask'].invoke('one')
Rake.application['mytask'].invoke('two')
The result (tested with rake 10.4.2):
"called mytask (one)"
"called mytask (two)"

In a script with Rails loaded (e.g. rails runner script.rb)
def rake(*tasks)
tasks.each do |task|
Rake.application[task].tap(&:invoke).tap(&:reenable)
end
end
rake('db:migrate', 'cache:clear', 'cache:warmup')

Related

Invoke namespaced rake task in ruby code

Given some ruby file foo.rb:
require 'rake'
namespace :tmp do
desc "Foo bar baz.."
task :some_task do
puts "running some task..."
end
end
How can I invoke the namespaced task tmp:some_task task in ruby?
I've tried:
require_relative 'foo'
#=> true
Rake::Task[tmp:some_task].invoke
#=> NameError: undefined local variable or method `some_task' for main:Object
You are calling the task incorrectly - pass the name of the task as string:
Rake::Task['tmp:some_task'].invoke
The Accepted answer is correct. But, from rails, to invoke any rake task from Rails. you need to load them first.
require 'rake'
Rails.application.load_tasks
Rake::Task['my_task'].invoke

Rake tast with cli arguments in Sinatra

I have created a rake file in my Sinatra app to create indexes for a Mongodb collection and I am trying to pass the environment parameter in the rake task db:create_indexes.
Here is my db.rake file:
namespace :db do
task :create_indexes, :environment do |t, args|
puts "Environment : #{args}"
unless args[:environment]
puts "Must provide an environment"
exit
end
yaml = YAML.load_file("./config/mongoid.yml")
env_info = yaml[args[:environment]]
unless env_info
puts "Unknown environment"
exit
end
Mongoid.configure do |config|
config.from_hash(env_info)
end
Bin.mongoid:create_indexes
end
end
Also the Rakefile in the root of app contains:
require 'rake'
require 'rubygems'
require 'bundler/setup'
Dir.glob('lib/tasks/*.rake').each { |r| load r}
But whenver I try to run the rake task using the command rake db:create_indexes[development], I get the following error, no matches found: db:create_indexes[development]
Now I am clueless about how to solve this issue.
So the problem was not with the code but the shell I was using.
I use zsh shell in place of bash and it seems zsh require you to escape the brackets: rake my_task\['arg1'\].
Therefore the code works with rake db:create_indexes\['development'\].

Running minitest handler tests inside another ruby script

I'd like to run my minitest handler tests inside another ruby script (a bootstrapper script of sorts). I'd like to return the results of my tests to a variable that I can then parse to make sure everything passed before moving on. Is this possible? If so, what does the syntax for something like this look like?
You can shell out to run the test and capture the output.
puts "Running foo test:"
output = `ruby -Ilib:test test/test_foo.rb`
puts output
puts "Completed foo test."
Okay so I figured out how to do this using the rake::test library. This code will execute a minitest test, then slurp in the xml from the report and determine if everything passed or not.
require 'rake'
require 'rake/testtask'
require 'ci/reporter/rake/minitest'
require 'xmlsimple'
Rake::TestTask.new do |t|
t.verbose = true
t.test_files = FileList['system_tests/vm_tests.rb']
end
task :test => :"ci:setup:minitest"
Rake::Task[:test].execute
results = XmlSimple.xml_in('test/reports/TEST-VMtests.xml')
if results["failures"] > 0 or results["errors"] > 0
raise "The VM Tests have resulted in failures or errors"
end

How do I pass args from command line to rake then rspec

I'm trying to pass a couple of variables through the command line to rake to be used in rspec.
From what i gather i can do this to pass args into my rake task:
task :my_task, :arg1, :arg2 do |t, args|
puts "Args were: #{args}"
end
but I'm using rspec so my rake task looks like this:
RSpec::Core::RakeTask.new(:my_task), :arg1, :arg2 do |t, args|
puts args.arg1
puts args.arg2
end
which doesn't work.
also I havent figured out how to pass it to my rspec spec file
How do you call rake?
For your example you have to call:
rake mytask[val1,val2]
I'm not sure about your 2nd code example.
args should be a hash with keys :arg1 and :arg2.
So you may use
puts args[:arg1]
I'm in doubt if args.arg1 will work.
It's been a while since the original question, but I had to solve a similar problem. You don't have to change much of your initial idea:
RSpec::Core::RakeTask.new(:my_task, [:arg1, :arg2]) do |t, args|
puts args.arg1
puts args.arg2
end
Then call it e.g. via:
rake my_task["hello","world"]

Namespaces in Ruby Rake Tasks

Are the following equivalent?
namespace :resque do
task setup: :environment do
end
end
task "resque:setup" => :environment do
end
In short: yes. When running rake resque:setup both of these tasks will be invoked.
Rake will merge these tasks. You can test this by doing the following:
p Rake.application.tasks
Which in this case would return something like
[<Rake::Task resque:setup => [environment]>]
Which is simply an Array holding a single Rake::Task object. You can also check the scope or list of namespaces for a task by doing:
p Rake.application.tasks.first.scope
#=> ["resque"]
If you want to learn a little more on how the internals of Rake work, check out Rake::Task and Rake::TaskManager

Resources