Rails - execute task before server starts - ruby-on-rails

I want my app to execute a task (download CSV files from another website & parse them) just before the server starts.
In which file exactly should I put the code for this?(maybe db/seeds.rb?)
Also, any idea on how to test this with RSpec?

The initializers folder is the proper place to set this tasks.
Just create a file with .rb extension, and everything else will be performed on start.
http://guides.rubyonrails.org/configuring.html#initializers

From the Rails Guides:
In the rare event that your application needs to run some code before Rails itself is loaded, put it above the call to require 'rails/all' in config/application.rb.

Inside initializers folder, can create any .rb file and can write any logical code in that file. It will be executed on start.
But it may slow down server start, because in your case csv download and parsing may take time to execute.
http://guides.rubyonrails.org/configuring.html#running-code-before-rails

One solution, this may be not right
in lib/tasks/say_hello_rake.rake
namespace :sample do
task :your_task_name do
puts 'Hello my task ... '
end
end
before rails s
rake sample:your_task_name

Related

Why are rake tasks stored in lib/tasks/?

My understanding of the lib/ directory in rails is that it stores non-domain specific code as a best practice.
However, my Rake scripts are very specific to my domain. They do things like create new models.
So is there a better place than lib/tasks/ to store domain-specific rake scripts, or am I missing something here?
I like this idea, and I agree - lib at one point was very much a junk drawer, and as a Rails community we've moved some of the junk away, but yes Rake tasks are usually very specific application logic.
In your Rakefile all you have to do is load your new Rakefiles (exercise for the reader: iterate the files in the folder instead of specifying it explicitly.
Example:
require File.expand_path('../config/application', __FILE__)
Rails.application.load_tasks
load('app/tasks/my_task.rake') # <--- my custom task!!!
The above is correct, but you should add the following to prevent the zeitwerk from creating the Tasks constant. (ignore if not using zeitwerk loader)
Rails.autoloaders.main.ignore('app/tasks');
You may check this in the console by calling Tasks, it should not be defined.
Another option might be to add rake tasks inside /tasks instead of app/tasks

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

Ruby on rails, run a method on server start 2.3

I want to run a method, on the startup of the rails server. It's a model method.
I tried using config/initializers/myfile.rb, but the method was invoked during migrations, so it SELECTed from a nonexistant table.
Tried environment.rb also, but the class does not exist yet (and will probably have the same problem with migrations)
I don't know where to put that method, so it'll run only on server startup and not during migrations.
There are some things you could do to actually improve this a bit. The issue is that you are running this code when rake loads your environment, but you really only want to run this when the environment is loaded by an instance of your web server. One way to get around this is to set a value when rake loads your environment, and when that value is set, to not execute your initializer code. You can do this as follows:
task :environment => :disable_initializer
task :disable_initializer do
ENV['DISABLE_INITIALIZER_FROM_RAKE'] = 'true'
end
#In your initializer:
ENV['DISABLE_INITIALIZER_FROM_RAKE'] || MyModel.method_call
There is no way to avoid this from my understanding. You can put the initializer code that relies on the new table in a rescue block to quiet things down so others can run migrations.
Try putting your method call in boot.rb, in the run method after the Rails::initializer call. I don't have rails in front of me right now because I'm at work but I think that the whole environment should be loaded by that point and you can run methods on the framework.
I found this to work quite well:
if File.basename($0) == "rails" && ARGV == []
It also detects "rails generate .."

Execute commands from external file in Rails

Is there an easy way to have a rails action load up an external file of commands and then execute them?
For example, I'm trying to write a bunch of rails create methods to pre-populate a bunch of tables in a database.
Ideally, I'd like the action to check for the existence of the file, if it exists, run all of the commands, and then delete the file so it doesn't get executed again.
So, the external file would basically look like this:
MyTable.create :name => "New 1"
MyTable.create :name => "New 2"
Is this easy to accomplish in rails?
Some elaboration:
The idea would be that if a certain set up tables need to be touched up after a release, and that you can't do it through a migration script (i.e. you're initializing the database from the schema.rb file), you could:
Create a file called "update_data.rb" for example
Place it in an admin directory
Target some action in the browser (i.e. /admin/update_data)
Rails would then read in the file, executing the commands line-by-line, and then
Delete the file when finished so that the actions weren't accidentally executed again
Does that help? It would be a file for one-time actions that need to be executed after a release. If there is a better method, I'm certainly all ears!
Another option would be rake. You can create a new file in lib/tasks - we'll call yours bootstrap.rake
namespace :db do
desc 'Load an initial set of data'
task :bootstrap => :environment do
if your_file_exists
puts 'Loading data...'
this_is_where_the_magic_happens
end
end
end
Then from the console you can run rake db:bootstrap and schedule it with crontab if you like.
For step 4:
load("update_data.rb")
I believe this would load and execute your script.
Sounds like a job for script/runner.
http://www.ameravant.com/posts/recurring-tasks-in-ruby-on-rails-using-runner-and-cron-jobs

Adding sample data to database using rake for a rails engine

I am trying out Rails engines by creating a classifieds engine where users can view/post/reply to classifieds.
The main application contains code for user authentication and profiles while there is an engine which I have created which will deal with the classifieds functionality.
Now I want to add some sample data to the database for the classifieds engine. So I created a rake file called 'sample_classifieds_data.rake' in 'vendor/plugins/classifieds/lib/tasks' and I added the yml files in 'vendor/plugins/classifieds/lib/tasks/sample_classifieds_data'
The code of the rake file and a sample yml file can be found here: http://gist.github.com/216776
Now the problem is that when I run the rake task, no error is being thrown but the values are not getting populated in the database.
Any ideas? BTW, it is development environment and the database is the development database.
I ran a similar rake task to populate sample users in the database which worked. the location of that rake file 'sample_data.rake' was located in 'lib/tasks'.
In rails edge, you can use the rake db:seed feature to add datas to your base. See the commit.
The use is pretty simple.
Create a db/seeds.rb file.
And put whatever code you want to seed your database in it.
For example :
Category.create!(:name => 'My Category')
Country.create!(:name => 'Cassoulet Land')
And when you want to seed your database, you could do a rake db:seed
If, for any reason, you do not wish to use edge (which would be comprehensible in a production environment), you can use the Seed Fu plugin, which quite does the trick for you.
Your task looks good. About the only thing would cause your task to fail silently is that the file you're passing to Fixture.new does not point to a yml or csv file.
Double check by modifying the put statement to print the full path of the file it imported, and compare what it prints against your directory structure.
For example, things will fail silently if your fixture files start with a capital letter? Categories.yml instead of categories.yml
The db:seed task was added in Rails 2.3.4. So no need to run edge.
http://weblog.rubyonrails.org/2009/9/4/ruby-on-rails-2-3-4

Resources