Rake task to add default data - ruby-on-rails

I have seen some apps that have a few rake tasks included to load data. I am not talking about seed data, I know about db/seeds.rb, instead I am referring to data such as default users and basic records that help me fill my application with something to look at. I don't want to use db:fixtures:load because I don't have any control over this...
I would like to have rake tasks like this:
rake myapp:data:delete
rake myapp:data:load
rake myapp:data:reload
Where the 'delete' rake task would delete all data that I specify in the rake task, the 'load' app will load the default data from the task into the app and the 'reload' task will delete all data, then load it in the app. How do I do something like this?
If you could give me an example where I have a model named 'Contact' and a few fields - basically how to add or delete data from those fields in a rake task, I would REALLY appreciate it!
Just to give you an idea, I would mainly use these rake tasks when I move from one computer to another to do development. I don't want to manually go enter default records (such as my user to login with) so I could just do rake myapp:data:reload - this would be after doing rake db:schema:load
Thank you,
BN

Create a file lib/tasks/data.rake and write the following code:
require File.join(File.dirname(__FILE__), '../../config/environment')
require 'database_cleaner'
namespace :myapp do
namespace :data do
task :delete do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
end
task :load do
require 'db/data.rb'
end
task :reload do
Rake::Task['myapp:data:delete'].invoke
Rake::Task['myapp:data:load'].invoke
end
end
end
So now you have defined your rake tasks. I'm using the gem database_cleaner, so you'll need to install it:
sudo gem install database_cleaner
Now, the rake myapp:data:load is basically loading the data from a file called db/data.rb. You could name it anything you wanted as long as you used the file name in the rake task or you could use more than one file if you wanted... So create the file db/data.rb and put all the code that you need...
User.create(...)

Related

Do something in a rake task before Rails models are loaded

I have a rake task that works with models. But I need to tell the models that they are being used from Rake instead of within the app. My best idea is to set an environment variable on the command line before running the rake task, but that requires everyone on the team to remember to use the environment variable. Specifically, I have an if statement to skip loading Delayed::Job's handle_asynchronously while running the rake task.
class Thing < ActiveRecord::Base
searchable do
string :title
text :title, :content
end
handle_asynchronously unless ENV['MIGRATINGDATA']
end
When we run our migration task we do this:
MIGRATINGDATA=true bundle exec rake project:migrate_data
I'd like to get rid of the need for the special addition to the command line. Our migration code loads a fake Sunspot instance to turn off indexing while migrating the data.
Just set something at the top of your Rakefile:
ENV['HELLO_RAKE'] = true
require_relative 'config/application'
Rails.application.load_tasks
Instead of using ENV you could, if you wanted, set a constant, e.g. HELLO_RAKE = true and then check defined?(HELLO_RAKE).
An alternative is to just check if the running program is rake:
handle_asynchronously unless File.basename($0) == "rake"
A downside to both of these approaches is that they will be in effect any time you're using Rake, which will include other Rake tasks not related to migrations.
If your models care if you are using them from a rake task or not, you are doing something wrong. Instead you can add parameters to certain methods for example. In your specific use case, you can run jobs immediately instead. Put this at the start of your rake task:
Delayed::Worker.delay_jobs = false

How to access Rails model in a rake file outside of rake tasks?

In order to access Rails models in a rake task you give :environment as a dependency. But what if you want to have dynamic descriptions of your task and they depend on some database date. For example:
end_date = Foo.end_date # the model foo provides some end date
desc "Do something after #{end_date}"
task bar: :environment do
...
end
I tried Rake::Task[:environment].invoke, but I get Don't know how to build task 'environment'.
I've just discovered that you can load up the rails environment a bit more manually. I just stick this at the top of the file containing the rake task:
# Pre-load the rails environment so we can dynamically create
# these tasks. Your path may vary.
require File.expand_path('../../../config/environment', __FILE__)
# Eager load the models so I have access to them, this may be
# different in your environment.
Zeitwerk::Loader.eager_load_all

Rails 3.2: Adding seed tasks from a mountable engine

I have a rails application using rake db:seed too fill in some of the basics in my DB to start working. This works through a little infrastructure and a fixtures.rb someone else wrote to load a YAML into the DB.
What I'm trying to do is take this infrastructure and move it into my own gem so that I can use it again elsewhere, this requires me both to have some of my own gem's models be inserted through the gem's seed task (which I want to have run from calling db:seed or something similar in the main app) and that the main app push some of the gem's models using it's own seed. The second part of this I already have working, it was a simple fix in the fixtures.rb file I was given.
The things I want to do now:
Move fixtures.rb into the gem: I still don't have any of the source running this in the gem. Now to do this I can probably require the file from the [MyGem::Engine.root, 'lib', ...].join then call a method there with a path to load YAML files from into the DB, which I don't see why it shouldn't work.
Get rake db:seed to run a task defined in my gem. I've added .rake files under lib/tasks of my gem (it's an engine) and I can't seem to call them from the main app rakefile though.
To make myself clear, what I want to do is through the gem (not the main app - or with 1 line of code in the main app) add a dependency onto the main apps seed task, so that when someone runs rake db:seed in the main app the gem will run additional seeding without the main app developer even having to know about them.
The dirty solution that I want to avoid is loading the .rake files from the gem inside the main app, or loading a seeds.rb in the gem from the one in the main app.
So what I'm asking is basically how to make the rake db:seed task do things defined within my gemified engine just by having the gem required in the gemfile?
So shortly after asking this I figured it out.
First step was taken from here: How to add a gems tasks to the main app
Then inside a task file
#lib/task/some_task.rake
Rake::Task['db:seed'].enhance ['my_seed_task']
#lib/tasks/my_seed_task.rake
task 'my_seed_task' do
...
end
And now when in the main app I run rake db:seed it runs whatever my_seed_task defines as a perquisite.
Try instead to extend Rails::Generators::Base.
This is the mechanism as given in the docs:
"Each public method in the generator is executed when a generator is invoked"
so
class DbGenerator < Rails::Generators::Base
source_root File.expand_path('../../../../db', __FILE__)
def copy_seeds_file
copy_file 'seeds.rb', 'db/seeds.rb'
end
def copy_seeds_dir
Dir.foreach seeds_dir do |file|
copy_file "seeds/#{file}", "db/seeds/#{file}" unless file.match /^\./
end
end
def seeds_dir
File.expand_path 'seeds', self.class.source_root
end
end
This approach will allow all gem-based seed data to copy into the app dir for the db:seed run

How to get name of current rake task in my Rails model?

I have some problems with one of gem supporting ActiveModel caching. When I'm using observer for cached model, during application initialization it tries to describe table to get all fields names.
The same thing is done when rake task is running, including db:migration. In that case there is some circular reference error. I'd like to detect current rake task, to skip gem initialization, but I don't know how to find out was code invoked through rake task. How to check it?
I dont get exactly what you are trying to do, but here is an example of getting the task name.
task :testing do |task_name|
puts task_name
end
This question has been asked a few places, and I didn't think any of the answers were very good... I think the answer is to check Rake.application.top_level_tasks, which is a list of tasks that will be run. Rake doesn't necessarily run just one task.
If you run your task via rake task or bundle exec rake task you can check it in your initializer simply by:
if $0.end_with?('rake')
# rake stuff
else
# non-rake stuff
end
You can use $PROGRAM_NAME instead of $0 if you like.

Access a class method from a model in the Rakefile / Ruby on Rails 3

I have a model, let's call it Foobar. I want to be able to run a cron job to update an attribute of all objects that are instances of Foobar. So, in pseudocode, it might be something like this:
Foobar.all.each do |foobar|
foobar.update_attributes({:my_attribute => 'updated'});
end
Now, let's say I wrap that in a class method called Foobar.run_update().
Calling Foobar.run_update() would work fine from the controller, or even from a view. But, what I want to do is run run_update() from the Rakefile so that I can tie it into a cron run. But, the Foobar class is not available to Rake when it is called from crontab.
How can I resolve that? How can I access the class methods of Foobars from Rake, when Rake is called from cron?
Thank you very much for your help.
By rake, if you mean a rake task then adding => :environment loads the rails environment for the task and you be able to call the Foobar.run_update method there. Like,
namespace :foobar do
task :update => :environment do
Foobar.run_update
end
end
And you should just be able to call rake foobar:update from the console and have it scheduled as a cronjob.
You can load up the Rails environment by requiring config/environment.rb:
ENV["RAILS_ENV"] ||= "production"
require '/where/your/rails/project/is/config/environment.rb'
In my Sinatra apps I typically have the file models/init.rb which requires Sequel, sets up my DB connection, and then uses require_relative to require all my model files. My main application then does require_relative "models/init".
With this setup any other script (including IRB) all I have to do is require the models/init.rb file myself, and I have full access to the same models and DB connection that the application has.

Resources