How do I programmatically extract a rake task's description? - ruby

I'm attempting to capture the equivalent of rake -D programmatically. I can load the Rakefile I'm targeting and see a list of tasks, but I can not figure out how to get the descriptions.
This will let me see the tasks that I am interested in:
Dir.chdir #myTarget
rake = Rake::Application.new
Rake.application = rake
rake.init
rake.load_rakefile
tasks = Rake.application.tasks
puts tasks.inspect
The above outputs something similar to:
[<Rake::Task default => [test]>, <Rake::Task foodcritic => []>, <Rake::Task integration => [kitchen:all]>]
My question is how to access the desc comments that are visible if I run rake -D. Here's what I'm after:
rake foodcritic
Run Foodcritic lint checks
rake integration
Alias for kitchen:all
rake kitchen:all
Run all test instances
Here's the final solution. The key was that I was missing metadata from the taskmanager:
Dir.chdir #myTarget
rake = Rake::Application.new
Rake::TaskManager.record_task_metadata = true
Rake.application = rake
rake.init
rake.load_rakefile
Rake.application.tasks.each do |t, n|
puts t
puts t.full_comment
puts "\n"
end

Use the methods comment or full_comment for that. More docs on the Rake::Task class here.

Related

Can Rake run each test in a separate Ruby instance?

I have a very simple Rakefile to test a small Ruby gem. It looks like this:
Rake::TestTask.new
task :default => :test
It invokes two tests that define constants with the same name. This results in errors being output by the second test like this:
warning: already initialized constant xxxxx
The reason for this is because Rake executes all of the tests within a single Ruby instance:
/usr/bin/ruby -I"lib" -I"/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib" "/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib/rake/rake_test_loader.rb" "test/test*.rb"
How should I specify that each test should be run in a separate Ruby instance ?
I have achieved this as shown below but I wonder if there is a better way because this solution doesn't scale well for lots of tests.
Rake::TestTask.new(:one) { |t| t.test_files = %w(test/test_one.rb) }
Rake::TestTask.new(:two) { |t| t.test_files = %w(test/test_two.rb) }
task :default => [:one, :two]
Instead of using Rake::TestTask, you could define a test task in your Rakefile that loops through each test file and runs them with sh like this:
task :test do
libs = ['lib',
'/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib',
'/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib/rake/rake_test_loader.rb']
test_files = FileList['test/**/test*.rb']
test_files.each do |test_file|
includes = libs.map { |l| "-I#{l}"}.join ' '
sh "ruby #{includes} #{test_file}"
end
end

How to create an RSpec Rake task using RSpec::Core::RakeTask?

How do I initialize an RSpec Rake task using RSpec::Core::RakeTask?
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new do |t|
# what do I put in here?
end
The Initialize function documented at
http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method isn't very well-documented; it just says:
- (RakeTask) initialize(*args, &task_block)
A new instance of RakeTask
What should I put for *args and &task_block?
I'm following in the footsteps of someone who had already started to build some ruby automation for a PHP project using RSpec in combination with Rake. I'm used to using RSpec without Rake, so I'm unfamiliar with the syntax.
Thanks,
-Kevin
Here is an example of my Rakefile:
require 'rspec/core/rake_task'
task :default => [:spec]
desc "Run the specs."
RSpec::Core::RakeTask.new do |t|
t.pattern = "spec.rb"
end
desc "Run the specs whenever a relevant file changes."
task :watch do
system "watchr watch.rb"
end
This allows to run specs defined in the spec.rb file from Rake
This is what my rakefile looks like
gem 'rspec', '~>3'
require 'rspec/core/rake_task'
task :default => :spec
desc "run tests for this app"
RSpec::Core::RakeTask.new do |task|
test_dir = Rake.application.original_dir
task.pattern = "#{test_dir}/*_spec.rb"
task.rspec_opts = [ "-I#{test_dir}", "-I#{test_dir}/source", '-f documentation', '-r ./rspec_config']
task.verbose = false
end
You can 'rake' from your tests directory and it will run all tests with a name [something]_spec.rb - and it should work across different test directories (e.g. different projects); if you have source in a separate directory (e.g. in the code above, a subdirectory called '/source' it will pick them up. Obviously, you can change that source directory to what you want.
Here's the rspec_config file I use - you can add your own settings in here:
RSpec.configure do |c|
c.fail_fast = true
c.color = true
end

How to access namespace from within a rake task?

My rake task (rake v 0.9.2.2) includes a bit to print out the name of the task. I'd like it to also print its namespace. Kind of like the following:
namespace :yelp do
desc "..."
task :scrape => :environment do
puts "rake #{task.namespace}:#{task.name}"
end
end
The problem is that namespace is a private method.
rake aborted!
private method `namespace' called for <Rake::Task => []>:Rake::Task
Anyone have a solution?
task.name includes the namespace. Use this tip to get task.name to print under rake 0.9.2.2.

How do I log queries in a rake task using datamapper and padrino?

I have a padrino install that is using datamapper and logging queries to a file. This is working fine when browsing my application. But queries are not logged if executed inside a rake file. Why?
This is how the task is defined:
# lib/tasks/example.rake
task :example => :environment do
players = Player.all #Player is a datamapper object
puts players.first.to_s
end
I also added this line to /config/boot.rb
Padrino::Logger::Config[:development] = { :log_level => :devel, :stream => :to_file }
And this line is called in /config/database.rb
DataMapper.logger = logger
And this is how I'm executing the script
$ padrino rake example
Invoke rake with:
PADRINO_LOG_LEVEL=development padrino rake my:task

Alias of task name in Rake

When I need to alias some task's name, how should I do it?
For example, how do I turn the task name:
rake db:table
rake db:create
rake db:schema
rake db:migration
to:
rake db:t
rake db:c
rake db:s
rake db:m
Editing after getting the answer:
def alias_task(tasks)
tasks.each do |new_name, old_name|
task new_name, [*Rake.application[old_name].arg_names] => [old_name]
end
end
alias_task [
[:ds, :db_schema],
[:dc, :db_create],
[:dr, :db_remove]
]
Why do you need an alias? You may introduce a new task without any code, but with a prerequisite to the original task.
namespace :db do
task :table do
puts "table"
end
#kind of alias
task :t => :table
end
This can be combined with parameters:
require 'rake'
desc 'My original task'
task :original_task, [:par1, :par2] do |t, args|
puts "#{t}: #{args.inspect}"
end
#Alias task.
#Parameters are send to prerequisites, if the keys are identic.
task :alias_task, [:par1, :par2] => :original_task
To avoid to search for the parameters names you may read the parameters with arg_names:
#You can get the parameters of the original
task :alias_task2, *Rake.application[:original_task].arg_names, :needs => :original_task
Combine it to a define_alias_task-method:
def define_alias_task(alias_task, original)
desc "Alias #{original}"
task alias_task, *Rake.application[original].arg_names, :needs => original
end
define_alias_task(:alias_task3, :original_task)
Tested with ruby 1.9.1 and rake-0.8.7.
Hmmm, well, I see that's more or less exactly the same solution RyanTM already posted some hours ago.
Here is some code someone wrote to do it: https://gist.github.com/232966
def alias_task(name, old_name)
t = Rake::Task[old_name]
desc t.full_comment if t.full_comment
task name, *t.arg_names do |_, args|
# values_at is broken on Rake::TaskArguments
args = t.arg_names.map { |a| args[a] }
t.invoke(args)
end
end

Resources