Rake tast with cli arguments in Sinatra - ruby

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'\].

Related

Aruba: Command "seedly-calculator" not found in PATH-variable

So, I am trying to run the test but I am getting an error says.
Aruba::LaunchError:Command "seedly-calculator.rb" not found in PATH-variable
-seedly-calculator
-bin
-src
-seedly-calculator.rb
I have tried to change the path in rake file but it doesn't work.
My seedly-calculator.rb file is in the root directory.
require "rspec/core/rake_task"
namespace :spec do
desc "Run the functional suite against the CLI"
RSpec::Core::RakeTask.new(:functional, [] => [:set_path])
task :set_path do
project_bin_dir = File.join(File.dirname(File.expand_path(__FILE__)), '..', 'bin')
ENV['PATH'] = project_bin_dir + ':'+ ENV['PATH']
end
end
it shows error like:
Failure/Error: let(:command) { run "seedly-calculator.rb" }
Aruba::LaunchError:
Command "seedly-calculator.rb" not found in PATH-variable "/Users/bilaltariq/Desktop/seedly-calculator/functional_spec/bin:/Users/bilaltariq/Desktop/seedly-calculator/functional_spec/exe:/Users/bilaltariq/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/bin:/Users/bilaltariq/Desktop/seedly-calculator/functional_spec/../bin:/Users/bilaltariq/.rbenv/versions/2.6.2/bin:/usr/local/Cellar/rbenv/1.1.1/libexec:/Users/bilaltariq/.rbenv/shims:/Users/bilaltariq/.asdf/shims:/Users/bilaltariq/.asdf/bin:/usr/local/bin:/Users/bilaltariq/.bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin".
I expect it to hit the file so i can write some test.
am i doing something wrong?
require 'spec_helper'
RSpec.describe 'Command Validation', type: :aruba do
let(:command) { run "seedly-calculator.rb" }
it "wrong/missing arguments" do
command.write("lookup\n")
stop_all_commands
expect(command.output).to end_with("Missing bank_name argument.\n")
end
end
seedly-calculator.rb:
#!/usr/bin/env ruby
# Complete bin/setup so that after it is
# run, ruby seedly-calculator.rb can be used to launch
# it.
# frozen_string_literal: true
require_relative './src/runner'
if !ARGV.length.zero?
input = ARGV
Runner.new.send('process_input', input)
else
puts "Arguments required!."
end
Update
To run a ruby script using run you need to make sure your ruby script is executable and contains a shebang so your system knows to run it with ruby. Here's example from this starter example
#!/usr/bin/env ruby
file = ARGV[0]
if file.nil? || file.empty?
abort "aruba-test-cli [file]: Filename is missing"
elsif !File.exist? file
abort "aruba-test-cli [file]: File does not exist"
end
puts File.read(file).chomp
So in your case you'll need to add this to the first line of your seedly-calculator.rb file
#!/usr/bin/env ruby
Then run this from command line to make it executable.
chmod +x #!/usr/bin/env ruby
I made a simple example forked off the one I reffed above. See this commit
Rspec convention is that it should match the same file structure of your project. It is not a good idea to set PATH manually.
Rake tasks are normally put in a tasks folder so you should have in project root a tasks folder
my_project/tasks/something.rake
Then you should have a spec folder that matches
my_project/spec/tasks/something_spec.rb
Then you should be able to get rid of task :set_path do end block and just run the spec without that.
You should also have a Gemfile to load your gems, run bundle install then invoke your test with
bundle exec rspec spec/tasks/sometask_spec.rb

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

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

Can I conditionally skip loading "further" ruby code in the same file?

Can I conditionally skip loading "further" ruby code in the same file,
if a library (loaded via require) is not found ?
begin
require 'aws-sdk'
rescue LoadError
puts "aws-sdk gem not found"
return #does not work. nor does next
end
# code after here should not be executed as `aws-sdk` gem was not found
puts "=== should not get executed"
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
In the above code, can I ask Ruby not to load the file further after
hitting a rescue LoadError.
Like an early return but for loading a file and not for a function.
Need it because i have i have a rake task which needs aws-sdk rubygem but i use it only
on my local machine. If aws-sdk is not found it does not make sense for me to load code afterwards in the same file. I guess i can split the code into smaller files and warp it in
a require call
if Rails.env.development?
require 'import_to_heroku'
end
But do not want to warp or modify my existing code
Also, i can wrap the whole code in an conditional but that is inelegant.
A begin-rescue block is also a form of explicit control flow.
I do not want to wrap or touch the original code is any manner
Maybe an api such as
require_or_skip_further_loading 'aws-ruby`
So i want my code to be functionally equivalent to
begin
require 'aws-sdk'
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
rescue LoadError
puts "aws-sdk gem not found"
end
Or via an if conditional
library_found = false
begin
require 'aws-sdk'
library_found = true
rescue LoadError
puts "aws-sdk gem not found"
return #does not work
end
if library_found
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
end
Want program execution to continue after LoadError is raised. ie. gracefully handle LoadError and do not load code written after LoadError in the same file. cannot raise exit or abort on LoadError And particularly the code after LoadError should not be executed (or loaded) by the ruby interpreter
Had originally asked How to skip require in ruby?
but i did not ask the question properly. hope this is better worded
I haven't checked the source, but I suppose that, when you run ruby my_file.rb at the console, or require/load it from Ruby code, the file is read entirely into memory before being evaluated. I'm afraid there is no such thing as skipping a part of a file.
I had an idea with catch/throw.
requiring file (for example a Rake task ?) treq1.rb :
catch :aws_sdk do
require_relative 'original'
end
puts '... continued'
The original file that you don't want to modify, original.rb :
puts 'in original, not to be modified'
begin
require 'aws-sdk'
rescue LoadError
puts "aws-sdk gem not found"
throw :aws_sdk
end
puts ">>> to execute only if 'aws-sdk' is found"
# namespace :db do ... etc
#end
Execution :
$ ruby -w treq1.rb
in original, not to be modified
aws-sdk gem not found
treq1.rb:2:in `require_relative': method `backtrace' called on unexpected T_NODE object (0x007fd32b88e900 flags=0x381c klass=0x0) (NotImplementedError)
from treq1.rb:2:in `block in <main>'
from treq1.rb:1:in `catch'
from treq1.rb:1:in `<main>'
Googling with the error : http://www.ruby-forum.com/topic/4406870
Recent post, no answer. It works in a single file, if you want to wrap your code.
Let's try another solution. Supposing that you can change the Rake task, treq2.rb :
begin
require_relative 'original'
rescue LocalJumpError
puts 'rescued LocalJumpError'
end
puts '... continued'
In original.rb, replace throw :aws_sdk by return :
$ ruby -w treq2.rb
in original, not to be modified
aws-sdk gem not found
rescued LocalJumpError
... continued
This way it works.
HTH
The "top-level return" feature has been added.
It is now possible to use the return keyword at the top level, as in your "via an if conditional" example. Further discussion here.

How do I run Rake tasks within a Ruby script?

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')

Resources