How to parse rake arguments with OptionParser - ruby-on-rails

Reffering that answer I was trying to use OptionParser to parse rake arguments. I simplified example from there and I had to add two ARGV.shift to make it work.
require 'optparse'
namespace :user do |args|
# Fix I hate to have here
puts "ARGV: #{ARGV}"
ARGV.shift
ARGV.shift
puts "ARGV: #{ARGV}"
desc 'Creates user account with given credentials: rake user:create'
# environment is required to have access to Rails models
task :create => :environment do
options = {}
OptionParser.new(args) do |opts|
opts.banner = "Usage: rake user:create [options]"
opts.on("-u", "--user {username}","Username") { |user| options[:user] = user }
end.parse!
puts "user: #{options[:user]}"
exit 0
end
end
This is the output:
$ rake user:create -- -u foo
ARGV: ["user:create", "--", "-u", "foo"]
ARGV: ["-u", "foo"]
user: foo
I assume ARGV.shift is not the way it should be done. I would like to know why it doesn't work without it and how to fix it in a proper way.

You can use the method OptionParser#order! which returns ARGV without the wrong arguments:
options = {}
o = OptionParser.new
o.banner = "Usage: rake user:create [options]"
o.on("-u NAME", "--user NAME") { |username|
options[:user] = username
}
args = o.order!(ARGV) {}
o.parse!(args)
puts "user: #{options[:user]}"
You can pass args like that: $ rake foo:bar -- '--user=john'

I know this does not strictly answer your question, but did you consider using task arguments?
That would free you having to fiddle with OptionParser and ARGV:
namespace :user do |args|
desc 'Creates user account with given credentials: rake user:create'
task :create, [:username] => :environment do |t, args|
# when called with rake user:create[foo],
# args is now {username: 'foo'} and you can access it with args[:username]
end
end
For more info, see this answer here on SO.

Try this:
require 'optparse'
namespace :programs do |args|
desc "Download whatever"
task :download => [:environment] do
# USAGE: rake programs:download -- rm
#-- Setting options $ rake programs:download -- --rm
options = {}
option_parser = OptionParser.new
option_parser.banner = "Usage: rake programs:download -- rm"
option_parser.on("-r", "--rm","Remove s3 files downloaded") do |value|
options[:remove] = value
end
args = option_parser.order!(ARGV) {}
option_parser.parse!(args)
#-- end
# your code here
end
end

You have to put a '=' between -u and foo:
$ rake user:create -- -u=foo
Instead of:
$ rake user:create -- -u foo

Related

I don't understand how to add arguments to a rake task. (Excerpt from the documentation)

I'm trying to create a custom rake task that takes in two arguments and uses them in my code.
I'm looking at the rails documentation and I see this excerpt for running a rails task with an argument:
task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
# You can use args from here
end
The rake task can then be invoked like this:
bin/rake "task_name[value 1]"
However, this is way too vague for me. The rails documentation fails to give a concrete example of a rake task with an argument.
For example, I'm looking at this code and I'm thinking what does bin/rake "task_name[value 1]" do? What is [:pre1, :pre2]?
Additionally, I've found some other fantastic links that do things a little bit differently. Here are the links.
Thoughtbot version
In the thoughtbot version that have this example
task :send, [:username] => [:environment] do |t, args|
Tweet.send(args[:username])
end
What is the [:username => [:environment]? its different than the official rails docs.
Here is another:
4 ways to write rake tasks with arguments
I've also looked at the officail optparser documentation and that too has a different way of making it work.
All I want is for this example code that I have to work on my .rake file:
require 'optparse'
task :add do
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: rake add"
opts.on("-o", "--one ARGV", Integer) { |one| options[:one] = one }
opts.on("-t", "--two ARGV", Integer) { |two| options[:two] = two }
end.parse!
puts options[:one].to_i + options[:two].to_i
end
The code fails because of invalid option: -o. I just want to make this work so I can move on. Does anyone have any thoughts?
Here is one of my rake tasks with arguments:
namespace :admin do
task :create_user, [:user_email, :user_password, :is_superadmin] => :environment do |t, args|
email = args[:email]
password = args[:password]
is_superadmin = args[:is_superadmin]
... lots of fun code ...
end
end
and I invoke this task like this:
rake admin:create_user['admin#example.com','password',true]
EDIT
To pass flags in you can do something like this:
task :test_task do |t, args|
options = {a: nil, b: nil}
OptionParser.new do |opts|
opts.banner = "Usage: admin:test_task [options]"
opts.on("--a", "-A", "Adds a") do |a|
options[:a] = true
end
opts.on("--b", "-B", "Adds b") do |b|
options[:b] = true
end
end.parse!
puts options.inspect
end
And examples of invoking it:
rake admin:test_task -A -B
rake admin:test_task -A
rake admin:test_task -B

How to pass an option to a Rake task?

I would to know if it was possible to pass an option from the commande line to the task without having the Rakefile taking option for itself.
Here is my task:
task :task do
args = ARGV.drop(1)
system("ruby test/batch_of_test.rb #{args.join(' ')}")
args.each { |arg| task arg.to_sym do ; end }
end
Here is the command I want to be functional:
rake task --name test_first_one
The result I want is the same as this:
ruby test/batch_of_test.rb --name test_first_one
For this code, I get this error:
invalid option: --name
How can --name (or another option) can be passed to my task ?
As far as I know you cannot add flags to Rake tasks. I think the closest syntax available is using ENV variables.
rake task NAME=test_first_one
Then in your task:
name = ENV['NAME']
like this:
task :task, [args] => :environment do |t, args|
system("ruby test/batch_of_test.rb #{args.join(' ')}")
args.each { |arg| task arg.to_sym do ; end }
end
and you call it like this.
rake task[args]
UPDATE
task :task, [:options] => :environment do |t, args|
arg = args[:options].pop
options = args[:options]
end

IDE Cloud9 - cannot open %=ENV[C9_USER]%: No such file

I run task for importing dates to DB Mysql. After running task rake db:restore displayed error:
cannot open %=ENV[C9_USER]%: No such file
What wrong?
task:
require 'yaml'
namespace :db do
def backup_prep
#directory = File.join(Rails.root, 'db', 'backup')
#db = YAML::load( File.open( File.join(Rails.root, 'config', 'database.yml') ) )[ Rails.env ]
#db_params = "-u #{#db['username']} #{#db['database']}"
#db_params = "-p#{#db['password']} #{#db_params}" unless #db['password'].blank?
end
desc 'Backup database by mysqldump'
task :backup => :environment do
backup_prep
FileUtils.mkdir #directory unless File.exists?(#directory)
file = File.join( #directory, "#{RAILS_ENV}_#{DateTime.now.to_s}.sql" )
command = "mysqldump #{#db_params} | gzip > #{file}.gz" #--opt --skip-add-locks
puts "dumping to #{file}..."
# p command
exec command
end
desc "restore most recent mysqldump (from db/backup/*.sql.*) into the current environment's database."
task :restore => :environment do
unless ENV['RAILS_ENV']=='development'
puts "Are you sure you want to import into #{ENV['RAILS_ENV']}?! [y/N]"
return unless STDIN.gets =~ /^y/i
end
backup_prep
wildcard = File.join( #directory, ENV['FILE'] || "#{ENV['FROM']}*.sql*" )
puts file = `ls -t #{wildcard} | head -1`.chomp # default to file, or most recent ENV['FROM'] or just plain most recent
if file =~ /\.gz(ip)?$/
command = "gunzip < #{file} | mysql #{#db_params}"
else
command = "mysql #{#db_params} < #{file}"
end
p command
puts "please wait, this may take a minute or two..."
exec command
end
end
dump db store in path workspace/db/backup/db.sql

rake tasks dry up namespace with namespace

I setup a series of custom rake tasks for a project that I am building that are name spaced to placewise. These tasks can be viewed by running rake tasks from the console.
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
All of these tasks are stored in lib/tasks/placewise and built like so:
namespace :placewise do
namespace :db do
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = environments(args.env)
msg("Dropping the #{env} database")
shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
msg("Creating the #{env} database")
shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
msg("Running the #{env} database migrations")
shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
end
end
A new task, for example may start with the base setup as follows:
namespace :placewise do
namespace :example do
desc "example"
task :example do
end
end
end
As you can see the namespace :placewise do will be replicated each time. I want to keep all of our custom rake tasks in the same group, however, I am curious if there is a way to avoid having to add that namespace to each .rake file?
Cheers.
Unfortunately I was advised against this strategy and in the process of discovery I found out my helper methods were not setup correctly. So here goes.
I created a new modules folder in lib/modules with the new helper_functions.rb file inside that directory. Here are my helpers:
module HelperFunctions
# ------------------------------------------------------------------------------------
# Permitted environments
# ------------------------------------------------------------------------------------
def environments(arg)
arg = arg || "development"
environments = ["development", "test", "production"]
if environments.include?(arg)
puts
msg("Environment parameter is valid")
return arg
else
error("Invalid environment parameter")
exit
end
end
# ------------------------------------------------------------------------------------
# Console message handler
# ------------------------------------------------------------------------------------
def msg(txt, periods: "yes", new_line: "yes")
txt = txt + "..." if periods == "yes"
puts "===> " + txt
puts if new_line == "yes"
end
def error(reason)
puts "**** ERROR! ABORTING: " + reason + "!"
end
# ------------------------------------------------------------------------------------
# Execute Shell Commands
# ------------------------------------------------------------------------------------
def shell(cmd, step: nil)
msg("Starting step #{step}", new_line: "no") if step.present?
if ENV['TRY']
puts "-->> " + cmd
else
sh %{#{cmd}}
end
msg("#{step} completed!", periods: "no")
end
end
Then in the Rakefile add:
# Shared Ruby functions used in rake tasks
require File.expand_path('../lib/modules/helper_functions', __FILE__)
include HelperFunctions
Rails.application.load_tasks
# Do not add other tasks to this file, make files in the primary lib/tasks dir ending in .rake
# All placewise tasks should be under the lib/tasks/placewise folder and end in .rake
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
And finally my .rake tasks look like:
namespace :placewise do
# ------------------------------------------------------------------------------------
namespace :db do
# ------------------------------------------------------------------------------------
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = HelperFunctions::environments(args.env)
HelperFunctions::msg("Dropping the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
HelperFunctions::msg("Creating the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
HelperFunctions::msg("Running the #{env} database migrations")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
# ------------------------------------------------------------------------------------
end
# ------------------------------------------------------------------------------------
end

How can I set the Devise secret key inside a rake task

I want to run a rake task, but it's complaining about not having the devise secret key. I was hoping defining the task with task :mytask => :environment would have loaded that up for me but I would need to specify it when calling the rake task.
I keep my secret key in .env-production and normally source the file then export DEVISE_SECRET_KEY. But I don't want to have to type source .env-production && export DEVISE_SECRET_KEY && RAILS_ENV=production rake mytask just to run a rake task.
I tried to enhance the :environment task like so:
# lib/tasks/environment.rake
Rake::Task["environment"].enhance do
if Rails.env.production?
fn = ".env-production"
else
fn = ".env"
end
puts "Trying to read devise secret key from #{fn}"
match = File.read(fn).match /DEVISE_SECRET_KEY="(.*)"/
if match
Devise.secret_key = match[1]
ENV['DEVISE_SECRET_KEY'] = match[1]
puts "Found devise secret key"
else
puts "Couldn't find secret key"
end
end
But it still complains about not knowing the key... is there any way to make this work?
Man, the answer was simple.
I just changed it to this:
task :load_devise_key do
if Rails.env.production?
fn = ".env-production"
else
fn = ".env"
end
puts "Trying to read devise secret key from #{fn}"
match = File.read(fn).match /DEVISE_SECRET_KEY='(.*)'/
puts File.read(fn)
if match
Devise.secret_key = match[1]
ENV['DEVISE_SECRET_KEY'] = match[1]
puts "Found devise secret key"
else
puts "Couldn't find secret key"
end
end
task :environment => :load_devise_key
Seems to have done the trick!

Resources