Cannot load namespaced model when invoking rake task - ruby-on-rails

I got a rake task which invokes other rake tasks, so my development data can be easily reset.
the first rake task (lib/tasks/populate.rake)
# Rake task to populate development database with test data
# Run it with "rake db:populate"
namespace :db do
desc 'Erase and fill database'
task populate: :environment do
...
Rake::Task['test_data:create_company_plans'].invoke
Rake::Task['test_data:create_companies'].invoke
Rake::Task['test_data:create_users'].invoke
...
end
end
the second rake task (lib/tasks/populate_sub_scripts/create_company_plans.rake)
namespace :test_data do
desc 'Create Company Plans'
task create_company_plans: :environment do
Company::ProfilePlan.create!(name: 'Basic', trial_period_days: 30, price_monthly_cents: 4000)
Company::ProfilePlan.create!(name: 'Professional', trial_period_days: 30, price_monthly_cents: 27_500)
Company::ProfilePlan.create!(name: 'Enterprise', trial_period_days: 30, price_monthly_cents: 78_500)
end
end
when I run bin/rake db:populate then i get this error
rake aborted! LoadError: Unable to autoload constant
Company::ProfilePlan, expected
/home/.../app/models/company/profile_plan.rb to define it
but when I run the second rake task independently it works well.
The model (path: /home/.../app/models/company/profile_plan.rb)
class Company::ProfilePlan < ActiveRecord::Base
# == Constants ============================================================
# == Attributes ===========================================================
# == Extensions ===========================================================
monetize :price_monthly_cents
# == Relationships ========================================================
has_many :profile_subscriptions
# == Validations ==========================================================
# == Scopes ===============================================================
# == Callbacks ============================================================
# == Class Methods ========================================================
# == Instance Methods =====================================================
end
Rails 5.0.1
Ruby 2.4.0
The App was just upgraded from 4.2 to 5
It works when I require the whole path:
require "#{Rails.root}/app/models/company/profile_plan.rb"
But this seems strange to me, because in the error message rails has the correct path to the Model. Does someone know why I have to require the file when invoked from another rake task?
Thank you very much

Well, it seems that rake doesn't eager load, so when you call the create_company_plans.rake alone it loads the referred objects, however when you invoke it from another rake, it doesn't know you will need them and so they are not loaded.
You can take a look at this other QA which was similar to yours.
I think maybe you don't need to require the whole path, just:
require 'models/company/profile_plan'

From what I understand, you can probably overcome the problem by reenable ing and then revoke ing the task as given below. Pardon me if this doesn't work.
['test_data:create_company_plans', 'test_data:create_companies'].each do |task|
Rake::Task[task].reenable
Rake::Task[task].invoke
end
There is more info on this stackoverflow question how-to-run-rake-tasks-from-within-rake-tasks .

Related

Rails dynamically create model and use it

I am creating a model whose name is the input argument in a rake task. After the rake task, I wish to use the model to insert data.
So for example, I call my rake task with input Apple and the model Apple is created. Then I wish to do Apple.insert_all([{name: x},{name: y}...]) in another rake task but I get NameError: uninitialized constant Apple
Here's a better picture of the flow of what I'm doing
Rake::Task["create:fruit"].invoke("Apple") # create model here
Rake::Task["create:insert"].invoke("Apple") # insert data here but getting error
This is how I process the input in the second rake task:
task :insert, [:name] do |t, args|
fruit = args.name
fruit.classify.constantize.insert_all(xxx)
end
Any suggestions for how to go about this?
I created a new project and tried your code. I think the problem is in this line
fruit.classify.constantize.insert_all(xxx)
The code bellow works and create new records. I use a simple rake command to run it.
create.rake file
namespace :create do
desc "TODO"
task :insert, [:name] do |t, args|
klass = Object.const_get(args.name)
klass.create([{name: 'x'},{name: 'y'}])
p klass.count # testing new records have been saved
end
end
Rakefile file
require File.expand_path('../config/application', __FILE__)
Rails.application.load_tasks
task :default do
Rake::Task["create:insert"].invoke("Apple")
end

How to fix "uninitialized constant" in a Rake task

I have a problem when I do:
namespace :xaaron do
task :get_roles do
roles = Xaaron::Role.all
puts roles
end
task :get_role, [:name] do |t, args|
role = Xaaron::Role.find(args[:name].parameterize)
puts role
end
end
The first task will work fine. I can even add binding.pry and run Xaaron::Role and get information about Roles back. But the second task fails with:
NameError: uninitialized constant Xaaron::Role
I run each task in my main app because these tasks are inside an engine, using:
bin/rake xaaron:get_roles` and `bin/rake xaaron:get_role
I can run bin/rails c in the main application that uses the engine and run Xaaron::Role and get information about Roles table.
Why is the second one failing but the first one is not? Is there scoping with arguments?
I'm not sure why either works, but if this is Rails and those are Rails models, your tasks should depend on the environment:
task :get_roles => [ :environment ] do
By depending on the :environment task, it first loads Rails.
Also see: What's the 'environment' task in Rake?.
You can also run a Rake task as
bundle exec rake environment xaaron:get_role
This will load the Rails environment first.
I kept getting uninitialized constant errors for a Rake task, even after depending on :environment and running with bundle exec.
The issue was that I was making a Rake::TestTask and, even though the Rake task had access to all constants, the test files themselves did not have access to constants.
The solution was to add this line to the top of my test file:
require_relative '../config/environment'
This is the Rake task:
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/test_*.rb"]
end
To add, as of Ruby 1.9 and above, you can use this hash syntax:
namespace :xaaron do
desc "Rake task to get roles"
task get_roles: :environment do
roles = Xaaron::Role.all
puts roles
end
#####
end
And then you can run the command below to run the Rake task:
rake xaaron:get_roles
or
bundle exec rake xaaron:get_roles

Rake task error: uninitialized constant == Model / Controller classes not loaded

In Rails 3.2.16 execution of this rake task breaks with uninitialized constant error, complaining that the Model class MyModel is not known.
desc "some rake task"
task :do_it => :environment do
# do something depending on a model:
MyModel.each do |m|
# do something with model
end
# ...
end
Looks like the root-cause of this is that I had config.threadsafe! enabled in the config files.
To fix this, do this:
# only enable threadsafe! mode if we are not running rake tasks
config.threadsafe! unless File.split($0).last == 'rake' # unless it's a rake task

Determining whether gem was called as Rake task

I'm writing a gem that includes some rake tasks. I have some code in the before_configuration method that I want to run when my gem is loaded by the app at runtime, but NOT when a task is run with rake. How can I determine that?
lib/mygem/tasks.rake:
namespace :mygem do
task :dosomething do
puts "DONE"
end
end
lib/mygem/railtie.rb:
require "rails"
module Mygem
class Railtie < ::Rails::Railtie
config.before_configuration do
#is_rake_task = ?
if !is_rake_task
# Do something
end
end
end
end
Rake is only defined in rake context.
So, you could simply go something like that :
if defined? Rake
# rake specific stuff
else
# non rake stuff
end
Edit :
While this will work perfectly with rails s, there's a problem with zeus on development environment : zeus will require rake.
If this is a problem, if think you can take advantage of Rake.application, which sets an instance variable when a rake task is executed.
I've tested this in a zeus s context :
> Rake.instance_variable_defined? :#application
false
And in a rake <task> context :
> Rake.instance_variable_defined? :#application
true
So, a complete solution would be :
if defined?( Rake ) and Rake.instance_variable_defined?( :#application )
# rake specific stuff
else
# non rake stuff
end

Rails Rake Task - Access to model class

I would like to define a Ruby (1.9.2)-on-Rails(3.0.5) rake task which adds a user to the User table. The file looks like this:
#lib/tasks/defaultuser.rake
require 'rake'
namespace :defaultuser do
task :adduser do
u=User.new
u.email="bob#example.com"
u.password="password"
u.save
u.errors.each{|e| p e}
end
end
I would then invoke the task as
> rake defaultuser:adduser
I tested the code in the :adduser task in the Rails console, and it works fine.
I tested the rake task, running only
print "defaultuser:adduser"
in the body of the task, and it worked fine.
However, when I combined them, it complained, saying
rake aborted!
uninitialized constant User
I tried a
require File.expand_path('../../../app/models/user.rb', __FILE__)
at above the namespace definition in the rake file, but that didn't work. I got
rake aborted!
ActiveRecord::ConnectionNotEstablished
What do I need to do so that I have the same access to the User model class in the Rake task that I have in the Rails console?
You're close :)
#lib/tasks/defaultuser.rake
require 'rake'
namespace :defaultuser do
task :adduser => :environment do
...
end
Note the use of :environment, which sets up the necessary Rails environment prior to calling the rake task. After that, your User object will be in scope.

Resources