Rails, Create a Task in multiple environment? - ruby-on-rails

I actually have few custom rake tasks.
What I want to do is to create a task that will execute itself on two environments when you simply call it.
I mean, when I run this :
rake initialize_global_settings
I want this to be executed on development and test environment.
Actually I'm constrained doing this :
rake initialize_global_settings (This will be executed in development environment by default, I don't really know why)
and then I do this :
rake initialize_global_settings RAILS_ENV=test
Is it possible to make a task doing both ?
Here's my task :
task :initialize_global_settings => :environment do
puts "Generating all global settings parameters..."
parameters = ["few", "parameters", "here"]
parameters.each do |param|
glob_set = GlobalSetting.new(:field_name => param,
:field_value => "")
if glob_set.save
puts "#{param} created"
else
puts "#{param} already exist"
end
end
puts "done."
end

I found a solution doing this :
task :initialize_global_settings => :environment do
puts "Generating all global settings parameters..."
parameters = ["few", "parameters", "here"]
environments = ['development', 'test']
environments.each do |environment|
Rails.env = environment
puts "\nRunning Task in "+environment+" environment \n\n"
parameters.each do |param|
glob_set = GlobalSetting.new(:field_name => param,
:field_value => "")
if glob_set.save
puts "#{param} created"
else
puts "#{param} already exist"
end
end
puts "\nParameters have been set"
end
end
It works but I've got a conflict between same variables set in test and development environment and I don't know why.

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 run custom rake task via capistrano 3?

which way, i can run rake commands via capistrano on remote server.
for example i have a lib/task/reparse.rake with some methods
desc "it's take csv file, makes some changes and fill db with this info"
task :example1 => :environment do
require 'csv'
rows_to_insert = []
# some actions
# ...
end
on local server all is fine - i just run rake reparse:example1
and it's work(fill db correctly).
so question is - how can i run this command on real hosting, after deploy?
i'am using rails 4.1 + capistrano 3.
P.S. examples from site not work for me
How do I run a rake task from Capistrano?
if i try cap production rake:invoke task=reparse:land
it fails with:
cap aborted!
Don't know how to build task 'rake:invoke'
some fixes
namespace :somenamespace do
task :runrake do
on roles(:all), in: :sequence, wait: 5 do
within release_path do
execute :rake, ENV['task'], "RAILS_ENV=production"
end
end
end
end
with such way it begin to execute via
cap production somenamespace:runrake task=custom_task_file:custom_method1
Based on the capistrano/rails gem: https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake
namespace :somenamespace do
task :runrake do
on roles(:all) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, ask :task
end
end
end
end
end
You can create a corresponding capistrano task to run a specific rake task like that:
namespace :guests do
desc 'Remove guest users older than 7 days'
task :clean do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, 'guests:delete_old_guest_users'
end
end
end
end
end
So I have been working on this. it seams to work well. However you need a formater to really take advantage of the code.
If you don't want to use a formatter just set the log level to to debug mode.
SSHKit.config.output_verbosity = Logger::DEBUG
Cap Stuff
namespace :invoke do
desc 'Run a bash task on a remote server. cap environment invoke:bash[\'ls -la\'] '
task :bash, :execute do |_task, args|
on primary :app do
within deploy_to do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
execute args[:execute]
end
end
end
end
desc 'Run a rake task on a remote server. cap environment invoke:rake[\'db:migrate\'] '
task :rake, :task do |_task, args|
on primary :app do
within current_path do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
rake args[:task]
end
end
end
end
end
This is the formatter I built to work with the code above. It is based off the :textsimple built into the sshkit but it is not a bad way to invoke custom tasks. Oh this many not works with the newest version of sshkit gem. I know it works with 1.7.1. I say this because the master branch has changed the SSHKit::Command methods that are available.
module SSHKit
module Formatter
class SuperSimple < SSHKit::Formatter::Abstract
def write(obj)
case obj
when SSHKit::Command then write_command(obj)
when SSHKit::LogMessage then write_log_message(obj)
end
end
alias :<< :write
private
def write_command(command)
unless command.started? && SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Running #{String(command)} #{command.host.user ? "as #{command.host.user}#" : "on "}#{command.host}\n"
if SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Command: #{command.to_command}" + "\n"
end
end
unless command.stdout.empty?
command.stdout.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
unless command.stderr.empty?
command.stderr.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
end
def write_log_message(log_message)
original_output << log_message.to_s + "\n"
end
end
end
end
you need to load a custom rake task in Capistrano config:
# config/deploy.rb || config/deploy/production.rb
load 'lib/task/reparse.rake'
check for new task in console cap -T
Try capistrano-rake
Without messing with custom capistrano tasks or going into much detail, all you need to do is simply install the gem and you can start executing rake tasks on remote servers like this:
$ cap production invoke:rake TASK=some:rake_task

How can I read a CSV file as a rake task and instantiate a model class? - getting ActiveRecord connection error

I am writing a Ruby on Rails application which has a Rake task that can parse a CSV file.
Here is the code:
desc "Import Channels into DB\n Usage: rake channel_import"
task :import_channels, :path_to_channel_list do |t, args|
require "#{Rails.root}/app/models/channel"
require "csv"
filePath = args.path_to_channel_list
puts "Filepath received = #{filePath}"
csv = CSV.read("#{filePath}", :encoding => 'windows-1251:utf-8')
csv.each_with_index do |row, i|
if [0].include?(i)
puts "Skipping over row #{i}"
next
end
if(row.nil?)
puts "row[#{i}] was nil"
else
channelName = nil
classif = nil
owner = nil
channelName = row[0].force_encoding('UTF-8')
classif = row[1].force_encoding('UTF-8')
owner = row[2].force_encoding('UTF-8')
if (channelName.nil?)
puts "Channel name for row #{i} was nil"
#add entry to Log file or errors database
next #skip this row of the csv file and go to next row
else
channel_hash = Hash.new("name" =>"#{channelName}", "classification" => "#{classif}", "owner" => "#{owner}" )
end
puts "\nChannel Name = #{channelName}\nClassification = #{classif}\n Ownership = #{owner}"
#If channel name exists in the Database, update it
xisting_channel = nil
xisting_channel = Channel.find_by channel_name: '#{channelName}'
if(xisting_channel.nil?)
#create a new channel
#new_channel = Channel.create(channel_hash)
puts "Inserted....#{#new_channel.inspect}"
else
#update existing channel
Channel.update(xisting_channel.id, :classification => "#{classif}", :ownership => "#{owner}" )
puts "Updated...."
puts "channel_hash = #{channel_hash.inspect} "
end#end if/else
end#end if/else
end #end CSV.each
end
When I run this code I get the following error message:
MRMIOMP0903:am AM$ rake import_channels[/XXXXXXXX/Channellist.csv]
Filepath received = /XXXXXXX/Channellist.csv
Skipping over row 0
Channel Name = APTN HD+
Classification = Specialty
Ownership = Aboriginal Peoples Television Network
rake aborted!
ActiveRecord::ConnectionNotEstablished
I tried to create a Channel object using IRB and it worked just fine. The DB is created and I'm seeing it using MySQL WorkBench. All tables are there, however, I am unable to create the Channel object from the Rake task.
My hypothesis is, perhaps outside of a certain folder/hierarchy the app cannot access the ActiveRecord::Base class or something like this.
Any suggestions on how to make this work?
UPDATE:
BAsed on the answer by Phillip Hallstrom
I changed the top line to load the environment
task :import_channels => :environment do|t, args|
Then tried rake import_channels and got this error:
rake aborted!
undefined method `safe_constantize' for #<Hash:0x007fbeacd89920>
You need to load the environment prior to running your Rake task. I'm not familiar with your multiple task name options there, but for a simple example, you'd want this:
task : import_channels => :environment do

How do I pass an argument from a child task to a parent task in Rake

I tried invoke, but I don't think it's going to help. Here is a short demo of what I am try to achieve.
task :first_task => :dep do
puts "something else"
end
task :dep do
puts "something"
end
If I run
rake first_task
from the command-line, the dependency gets satisfied and then the first_task gets executed.
something
something else
But what if I have a value, say dir = "testdir" in :dep that I want to pass as an argument to :first_task? Hope that makes sense.
I thought google would come up with something, but nope. Thanks a lot :)
Not sure if you can set or pass a parameter, but you can use instance variables:
task :first_task => :dep do
puts "something else"
puts #dir
end
task :dep do
puts "something"
#dir = "Foo"
end
Or just local variables:
dir = "default_dir"
task :first_task => :dep do
puts "something else"
puts dir
end
task :dep do
puts "something"
dir = "Foo"
end
For some kinds of task, it may make more sense to set environment variables:
task :first_task => :dep do
puts "something else"
puts ENV['dir']
end
task :dep do
puts "something"
ENV['dir'] = "Foo"
end
It doesn't fit completely to your question, but is may show another approach for parameters with rake and forwarding them to child tasks. Rake tasks may get parameters.
An example:
task :first_task, [:par1, :par2] do |t, args|
puts "#{t}: #{args.inspect}"
end
You can call this task with rake first_task[1,2] The result will be:
first_task: {:par1=>1, :par2=>2}
This parameters are forwarded to child tasks, if they share the same key. Example:
task :first_task, [:par1, :par2] => :second_task do |t, args|
puts "#{t}: #{args.inspect}"
end
task :second_task, :par2 do |t, args|
puts "#{t}: #{args.inspect}"
end
The result:
second_task: {:par2=>2}
first_task: {:par1=>1, :par2=>2}

How to pass arguments into a Rake task with environment in Rails? [duplicate]

This question already has answers here:
How to pass command line arguments to a rake task
(20 answers)
Closed 5 years ago.
I am able to pass in arguments as follows:
desc "Testing args"
task: :hello, :user, :message do |t, args|
args.with_defaults(:message => "Thanks for logging on")
puts "Hello #{args[:user]}. #{:message}"
end
I am also able to load the current environment for a Rails application
desc "Testing environment"
task: :hello => :environment do
puts "Hello #{User.first.name}."
end
What I would like to do is be able to have variables and environment
desc "Testing environment and variables"
task: :hello => :environment, :message do |t, args|
args.with_defaults(:message => "Thanks for logging on")
puts "Hello #{User.first.name}. #{:message}"
end
But that is not a valid task call. Does anyone know how I can achieve this?
Just to follow up on this old topic; here's what I think a current Rakefile (since a long ago) should do there. It's an upgraded and bugfixed version of the current winning answer (hgimenez):
desc "Testing environment and variables"
task :hello, [:message] => :environment do |t, args|
args.with_defaults(:message => "Thanks for logging on")
puts "Hello #{User.first.name}. #{args.message}" # Q&A above had a typo here : #{:message}
end
This is how you invoke it (http://guides.rubyonrails.org/v4.2/command_line.html#rake):
rake "hello[World]"
For multiple arguments, just add their keywords in the array of the task declaration (task :hello, [:a,:b,:c]...), and pass them comma separated:
rake "hello[Earth,Mars,Sun,Pluto]"
Note: the number of arguments is not checked, so the odd planet is left out:)
TLDR;
task :t, [args] => [deps]
Original Answer
When you pass in arguments to rake tasks, you can require the environment using the :needs option. For example:
desc "Testing environment and variables"
task :hello, :message, :needs => :environment do |t, args|
args.with_defaults(:message => "Thanks for logging on")
puts "Hello #{User.first.name}. #{args.message}"
end
Updated per #Peiniau's comment below
As for Rails > 3.1
task :t, arg, :needs => [deps] # deprecated
Please use
task :t, [args] => [deps]
Just for completeness, here the example from the docs mentioned above:
task :name, [:first_name, :last_name] => [:pre_name] do |t, args|
args.with_defaults(:first_name => "John", :last_name => "Dough")
puts "First name is #{args.first_name}"
puts "Last name is #{args.last_name}"
end
Notes:
You may omit the #with_defaults call, obviously.
You have to use an Array for your arguments, even if there is only one.
The prerequisites do not need to be an Array.
args is an instance of Rake::TaskArguments.
t is an instance of Rake::Task.
An alternate way to go about this: use OS environment variables. Benefits of this approach:
All dependent rake tasks get the options.
The syntax is a lot simpler, not depending on the rake DSL which is hard to figure out and changes over time.
I have a rake task which requires three command-line options. Here's how I invoke it:
$ rake eaternet:import country=us region=or agency=multco
That's very clean, simple, and just bash syntax, which I like. Here's my rake task. Also very clean and no magic:
task import: [:environment] do
agency = agency_to_import
puts "Importing data for #{agency}..."
agency.import_businesses
end
def agency_to_import
country_code = ENV['country'] or raise "No country specified"
region_slug = ENV['region'] or raise "No region specified"
agency_slug = ENV['agency'] or raise "No agency specified"
Agency.from_slugs(country_code, region_slug, agency_slug)
end
This particular example doesn't show the use of dependencies. But if the :import task did depend on others, they'd also have access to these options. But using the normal rake options method, they wouldn't.
While these solutions work, in my opinion this is overly complicated.
Also, if you do it this way in zsh, you'll get errors if the brackets in your array aren't escaped with '\'.
I recommend using the ARGV array, which works fine, is much simpler, and is less prone to error. E.g:
namespace :my_example do
desc "Something"
task :my_task => :environment do
puts ARGV.inspect
end
end
then
rake my_example:my_task 1 2 3
#=> ["my_example:my_task", "1", "2", "3"]
The only thing you need to keep in mind is that ARGV[0] is the process name, so use only ARGV[1..-1].
I realize that strictly speaking this does not answer the question, as it does not make use of :environment as part of the solution. But OP did not state why he included that stipulation so it might still apply to his use case.

Resources