I am trying to create a rake file with tasks for consuming my API. I want to be able and pass a lot of arguments to the task, depending on the call to be used. In an effort to make the tasks easier to use, I want the namespace to be part of the configuration. Is this possible?
namespace :myAPI do
SERVER = 'local'
namespace :live do
SERVER = 'live'
end
namespace :beta do
SERVER = 'beta'
end
BASE_URI = {
live: "https://myapi.com/do/v1",
beta: "https://beta.myapi.com/do/v1",
local: "http://127.0.0.1:4500/do/v1"
}
desc 'Get currently logged users'
task :extract_logged_users => :environment do
get("BASE_URI[SERVER]/users/current")
end
}
And then I want to be able and run this against the live server for example:
rake myAPI:live:extract_logged_users
You can create the task in a mor dynamic way like this:
require 'rake'
BASE_URI = {
live: "https://myapi.com/do/v1",
beta: "https://beta.myapi.com/do/v1",
local: "http://127.0.0.1:4500/do/v1"
}.each do |server,url|
namespace :myAPI do
namespace server do |ns|
desc 'Get currently logged users'
task :extract_logged_users do
puts 'get("%s/users/current", %s)' % [server,url]
end
end
end
end
(I replaced your get command with puts to check what happens and added the url into the command).
Now you can call:
rake myAPI:live:extract_logged_users
rake myAPI:beta:extract_logged_users
rake myAPI:local:extract_logged_users
The output:
get("live/users/current", https://myapi.com/do/v1)
get("beta/users/current", https://beta.myapi.com/do/v1)
get("local/users/current", http://127.0.0.1:4500/do/v1)
Alternative coding:
namespace :myAPI do
BASE_URI = {
live: "https://myapi.com/do/v1",
beta: "https://beta.myapi.com/do/v1",
local: "http://127.0.0.1:4500/do/v1"
}
BASE_URI .keys.each do |key|
namespace key do |ns|
desc 'Get currently logged users'
task :extract_logged_users do
puts 'get("%s")' % BASE_URI[key]
end
end
end
end
Related
I have a simple rails application where I import data from csv into my rails app which is functioning properly, but I have no idea where to start with testing this rake task, as well as where in a modular rails app. Any help would be appreciated. Thanks!
Hint
My Rails structure is a little different from traditional rails structures, as I have written a Modular Rails App. My structure is in the picture below:
engines/csv_importer/lib/tasks/web_import.rake
The rake task that imports from csv..
require 'open-uri'
require 'csv'
namespace :web_import do
desc 'Import users from csv'
task users: :environment do
url = 'http://blablabla.com/content/people.csv'
# I forced encoding so avoid UndefinedConversionError "\xC3" from ASCII-8BIT to UTF-8
csv_string = open(url).read.force_encoding('UTF-8')
counter = 0
duplicate_counter = 0
user = []
CSV.parse(csv_string, headers: true, header_converters: :symbol) do |row|
next unless row[:name].present? && row[:email_address].present?
user = CsvImporter::User.create row.to_h
if user.persisted?
counter += 1
else
duplicate_counter += 1
end
end
p "Email duplicate record: #{user.email_address} - #{user.errors.full_messages.join(',')}" if user.errors.any?
p "Imported #{counter} users, #{duplicate_counter} duplicate rows ain't added in total"
end
end
Mounted csv_importer in my parent structure
This makes the csv_importer engine available in the root of the application.
Rails.application.routes.draw do
mount CsvImporter::Engine => '/', as: 'csv_importer'
end
To correctly migrate in the root of the application, I added initializer
/engines/csv_importer/lib/csv_importer/engine.rb
module CsvImporter
class Engine < ::Rails::Engine
isolate_namespace CsvImporter
# This enables me to be able to correctly migrate the database from the parent application.
initializer :append_migrations do |app|
unless app.root.to_s.match(root.to_s)
config.paths['db/migrate'].expanded.each do |p|
app.config.paths['db/migrate'] << p
end
end
end
end
end
So with this explanation am able to run rails app like every other rails application. I explained this so anyone who will help will understand what to help me with as regards writing test for the rake task inside the engine.
What I have done as regards writing TEST
task import: [:environment] do
desc 'Import CSV file'
task test: :environment do
# CSV.import 'people.csv'
Rake::Task['app:test:db'].invoke
end
end
How do someone write test for a rake task in a modular app? Thanks!
I haven't worked with engines, but is there a way to just put the CSV importing logic into it's own class?
namespace :web_import do
desc 'Import users from csv'
task users: :environment do
WebImport.new(url: 'http://blablabla.com/content/people.csv').call
end
end
class WebImport # (or whatever name you want)
def initialize(url) ... end
def call
counter, CSV parse, etc...
end
end
That way you can bump into the Rails console to do the WebImport and you can also do a test isolating WebImport. When you do Rake tasks and Jobs (Sidekiq etc), you want to make the Rake task act as as thin a wrapper as possible around the actual meat of the code (which is in this case CSV parsing). Separate the "trigger the csv parse" code from the "actually parse the csv" code into their own classes or files.
I have deployed a Rails app on an Ubuntu server with Capistrano. However I am trying to run a custom task I had created and is in 'lib/tasks'. I tried to make it work by executing
cap production db:views
As if it was a custom task, but obviously it did not worked
cap aborted!
Don't know how to build task 'db:views'
The file is sql_views.rake, this task is for create views in the database
namespace :db do
desc "Update and create SQL views"
task :views => :environment do
Dir["#{Rails.root}/db/sql_views/*.sql"].each do |file_name|
STDERR.puts "Applying the SQL view at #{file_name}"
source_file = File.new(file_name, 'r')
if source_file and (sql_content = source_file.read)
ActiveRecord::Base.transaction do
# Each statement ends with a semicolon followed by a newline.
sql_lines = sql_content.split(/;[ \t]*$/)
if sql_lines.respond_to?(:each)
sql_lines.each do |line|
ActiveRecord::Base.connection.execute "#{line};"
end
end
end # transaction
end
end # Dir.each
end # task
end
If you used cap install to generate your Capfile, the following lines should be present:
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
So it's looking in lib/capistrano/tasks for custom tasks.
Move lib/tasks/sql_views.rake to lib/capistrano/tasks/sql_views.rake
Or just import each rake task:
import 'lib/tasks/sql_views.rake'
In our rails 3.2.12 app, there is a rake task created under lib/tasks. The rake task needs to call a method find_config() which resides in another rails module authentify (module is not under /lib/). Can we include Authentify in rake task and make method find_config() available to call in the rake task?
Here is what we would like to do in the rake task:
include Authentify
config = Authentify::find_config()
Thanks for comments.
require 'modules/module_name'
include ModuleName
namespace :rake_name do
desc "description of rake task"
task example_task: :environment do
result = ModuleName::method_name()
end #end task
end
This works for me. Since your Module is not in /lib you might have to edit how it is required. But it should work. Hope it helps.
PLEASE PAY ATTENTION TO THIS AND SAVE SOME RANDOM HEADACHES!! .
Don't include your module before your namespace:
include YourModule
namespace :your_name do
desc 'Foo'
task foo: :environment do
end
end
or inside your namespace:
namespace :your_name do
include YourModule
desc 'Foo'
task foo: :environment do
end
end
as that will include your module for the whole app and it could bring you a lot of troubles (like me adding in the module some :attr_accessors and breaking factory-bot functioning or other things it has happened in the past for this same reason).
The "no issues" way is inside of your task's scope:
namespace :your_name do
desc 'Foo'
task foo: :environment do
include YourModule
end
end
And yes, if you have multiple tasks, you should include in each of them:
namespace :your_name do
desc 'Foo'
task foo: :environment do
include YourModule
end
desc 'Bar'
task bar: :environment do
include YourModule
end
end
or simply call your method directly if you're only calling a method once in the task:
namespace :your_name do
desc 'Foo'
task foo: :environment do
YourModule.your_method
end
desc 'Bar'
task bar: :environment do
YourModule.your_method
end
end
How to require a Rails service/module in a Rake task?
I had the same problem and manage to solve it by requiring the rails files inside the rake task.
I had a rake task named give_something defined in the file lib/tasks/a_task.rake.
Within that task I needed to call the function give_something from the module AService which lives in the file app/services/a_service.rb
The rake task was defined as follows:
namespace :a_namespace do
desc "give something to a list of users"
task give_something: :environment do
AService.give_something(something, users)
end
end
I was getting the error: uninitialized constant AService
To solve it I had to require the module not at the beginning of the file a_task.rake, but inside the rake task:
namespace :a_namespace do
desc "give something to a list of users"
task give_something: :environment do
require 'services/a_service' # <-- HERE!
AService.give_something(something, users)
end
end
In rails 5.x.x we can do as-
Module file exist her app/lib/module/sub_module.rb like-
module Module
module SubModule
def self.method(params1, params2)
// code goes here...
end
end
end
and my rake_task presented here /lib/tasks/my_tasks.rake as-
namespace :my_tasks do
desc "TODO"
task :task_name => :environment do
Module::SubModule.my_method(params1, params2)
end
end
Note:- the above task file presented in outer lib not in app/lib
Now run the task using following command-
rake my_tasks:task_name
from app directory not from rails console
That worked for me !!!
I have a file alert_import in lib/models/alert_import', I would like to use in my task sth like this:
task :send_automate_alerts => :environment do
# STDERR.puts "Path is #{$:}"
Rake.application.rake_require '../../lib/models/alert_import'
ai = AlertImport::Alert.new(2)
ai.send_email_with_notifcations
end
In this code I get error:
Can't find ../../lib/models/alert_import
in AlertImport I have:
module AlertImport
class Alert
def initialize(number_days)
#number_days = number_days
end
def get_all_alerts
alerts = { }
Organization.automate_import.each do |o|
last_import = o.import_histories.where(import_type: "automate").last
last_successful_import = ImportHistory.last_automate_successful_import(o)
if last_import
if last_import.created_at + #number_days.days >= Time.now
alerts[o.id] ="Error during last automate import Last successful import was #{ last_successful_import ? last_successful_import.created_at : "never"}" if last_import.status == "failure"
alerts[o.id] ="Error during last automate import - status pending Last successful import was #{ last_successful_import ? last_successful_import.created_at : "never"}" if last_import.status == "pending"
else
alerts[o.id] = "There were no new files uploaded within #{#number_days} days"
end
else
alerts[o.id] = "The import was never triggered at all"
end
end
alerts
end
def send_email_with_notifcations
alerts =get_all_alerts
unless alerts.empty?
AlertMailer.email_notifications(alerts).deliver
end
end
end
end
The correct solution is:
desc "Send alerts about automate imports"
task :send_automate_alerts => :environment do
require "#{Rails.root}/lib/models/alert_import"
ai = AlertImport::Alert.new(2)
ai.send_email_with_notifcations
end
In Rails 3.x, I've had success by first importing the file using require and then including the module to the namespace. Here's how it would look:
require 'models/alert_import'
namespace :alerts
include AlertImport
desc 'Send alerts about automate imports'
task send_automate_alerts: :environment do
ai = AlertImport::Alert.new(2)
ai.send_email_with_notifcations
end
end
I tried a few options, most notably trying rake require, but it looks like the documentation for rake_require is incorrect. It specifically will not include files that don't end in .rake
So in the end, I did it "from scratch" - something like this:
```
namespace :my_namespace do
task :my_task do
require File.join(Rails.root, 'app', 'services', 'my_module.rb')
class Wrapper
include MyModule
end
Wrapper.new.the_method_I_need(args)
end
end
Done.
Most probably your path wrong, you can do as follow
task :send_automate_alerts => :environment do
# STDERR.puts "Path is #{$:}"
Rake.application.rake_require "#{Rails.root}/lib/models/alert_import"
ai = AlertImport::Alert.new(2)
ai.send_email_with_notifcations
end
"#{Rails.root}" this will give you the current path of your project
Your path is wrong, you can try:
task :send_automate_alerts => :environment do
# STDERR.puts "Path is #{$:}"
Rake.application.rake_require "#{Rails.root}/lib/models/alert_import"
ai = AlertImport::Alert.new(2)
ai.send_email_with_notifcations
end
Regards!
check out there http://rake.rubyforge.org/classes/Rake/Application.html#M000099
define correct path
I tried putting my script in a class that inherited from my model, like so:
class ScriptName < MyModel
But when I ran rake my_script at the command-line, I got this error:
rake aborted!
uninitialized constant MyModel
What am I doing wrong?
Also, should I name my file my_script.rb or my_script.rake?
Just require the file. I do this in one of my rake tasks (which I name my_script.rake)
require "#{Rails.root.to_s}/app/models/my_model.rb"
Here's a full example
# lib/tasks/my_script.rake
require "#{Rails.root.to_s}/app/models/video.rb"
class Vid2 < Video
def self.say_hello
"Hello I am vid2"
end
end
namespace :stuff do
desc "hello"
task :hello => :environment do
puts "saying hello..."
puts Vid2.say_hello
puts "Finished!"
end
end
But a better design is to have the rake task simply call a helper method. The benefits are that it's easier to scan the available rake tasks, easier to debug, and the code the rake task runs becomes very testable. You could add a rake_helper_spec.rb file for example.
# /lib/rake_helper.rb
class Vid2 < Video
def self.say_hello
"Hello I am vid2"
end
end
# lib/tasks/myscript.rake
namespace :stuff do
desc "hello"
task :hello => :environment do
Vid2.say_hello
end
end
All I had to do to get this to work was put my requires above the task specification, and then just declare the :environment flag like so:
task :my_script => :environment do
#some code here
end
Just by doing that, gave me access to all my models. I didn't need to require 'active_record' or even require my model.
Just specified environment and all my models were accessible.
I was also having a problem with Nokogiri, all I did was removed it from the top of my file as a require and added it to my Gemfile.