Ruby On Rails: way to create different seeds file for environments - ruby-on-rails

How can one make the task rake db:seed to use different seeds.rb file on production and development?
edit: any better strategy will be welcome

You can have a rake task behave differently based on the current environment, and you can change the environment a task runs in by passing RAILS_ENV=production to the command. Using these two together you could produce something like so:
Create the following files with your environment specific seeds:
db/seeds/development.rb
db/seeds/test.rb
db/seeds/production.rb
Place this line in your base seeds file to run the desired file
load(Rails.root.join( 'db', 'seeds', "#{Rails.env.downcase}.rb"))
Call the seeds task:
rake db:seed RAILS_ENV=production

I like to implement all seeds inside one seed.rb file and then just separate the environments inside.
if Rails.env.production?
State.create(state: "California", state_abbr: "CA")
State.create(state: "North Dakota", state_abbr: "ND")
end
if Rails.env.development?
for 1..25
Orders.create(order_num: Faker::Number:number(8), order_date: Faker::Business.credit_card_expiry_date)
end
end
That way you do not need to cast the RAILS_ENV property on your rake task, or manage multiple files. You also can include Rails.env.test?, but I personally let RSPEC take care of the testing data.

Related

Rails + Capistrano, seed production database with file uploads?

for a rails app, I'm using seeds.rb to populate the database with records and associated image-uploads. The seeds.rb gets all records data from a given YAML-file and grabs image-files from a folder to upload them. This works well in development-environment:
Folder Structure:
rails_app/
db/seeds.rb
...
data/
images1/
image1.jpg
image2.jpg
images2/
...
data.yml
data.yml:
item1:
description: Some description
filepath: images1/image1.jpg
item2:
description: ...
seeds.rb:
items = YAML.load_file(File.join(Rails.root, '..', 'data', 'data.yml'))
items.each do |item, details|
# create items with file-uploads, etc.
...
end
As all database-content is ready for production like this, we want to seed the production database via rake db:seed and access my local YAML-file and image-folder to create the records with their associated file-uploads.
To deploy, I'm using Capistrano and already found a task to seed data to production...
# Add this in config/deploy.rb
# and run 'cap production deploy:seed' to seed your database
desc 'Runs rake db:seed'
task :seed => [:set_rails_env] do
on primary fetch(:migration_role) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:seed"
end
end
end
end
...Unfortunately, this task only works with the seeds.rb on the production server and thus can not find the YAML or images on my local machine.
How can I write a task for Capistrano to access my local YAML and files and db:seed them to the database?
(Appearantly it's not a common practice to seed the production database, but it worked well to get a YAML from the client-side with all files and already use this "proper" data for development/design)
Thanks!
At a high level, you'll want to create a task that is a prerequisite of deploy:seed. That will ensure your task is run first, just before the seed script is executed.
In terms of that task you create, you want it to upload certain files to the same server and relative to the same directory as where the seed task will be run. Looking at the seed task you pasted in your post, we can see the directory is release_path and the server is primary fetch(:migration_role).
Therefore, I suggest writing a task like this:
task :upload_seed_data do
on primary fetch(:migration_role) do
execute :mkdir, "-p", release_path.join("../data")
upload! "../data/data.yml", release_path.join("../data/data.yml")
# ... and so on for all files you want to upload
end
end
# Register the prerequisite
before "deploy:seed", :upload_seed_data

Rake task during application initialization rails

I want to execute a rake task when the server of my application starts.
In config/application.rb i put the following:
if !Rails.env.production?
Rake::Task[ "init:db_records" ].invoke
end
The rake task is well defined, and runs without a problem if i invode it from terminal
rake init:db_records
But when placed in config/application.rb (or even in any initializers/*) i got the following error.
Don't know how to build task 'init:db_records'
What is the way to execute a rake task when the server starts ?
Thanks!
Rails already has a mechanism for setting up a development database -- rake db:seed. It does not run automatically when you start the app, but it does run as part of rake db:setup.
Unless you have a good reason, it's usually best to stick the conventions that Rails provides.
For those who encounter the same problem in the future.
I achieved this by creating a new file in the initializers directory, where i put the code of the rake task.
The advantage of this at this point, is that the application is already loaded, so you have access to ActiveRecord functions...
Putting the code directly in config/application.rb didn't work, since my models were not loaded yet.
Hope it will help!
Your Rake tasks are (likely) defined in a Rakefile. The initializer has no idea that file even exists, so it doesn't know about the tasks within.
The easiest way to circumvent this is by doing something like this:
Dir.chdir(Rails.root) do
`rake init:db_records`
end
That is, change the working directory to the root rails directory, then running the command.

Capistrano 3, using upload! in a task in lib/capistrano/tasks

I'm using Capistrano 3 and I want to create my own task. So I created a file my_new_thing.rake in lib/capistrano/tasks and I can see the task when I run cap -T. But... some of the methods aren't available. When I try to use upload! I get
cap aborted!
NoMethodError: undefined method `upload!' for main:Object
But if I move the same task into config/deploy.rb then then upload! method is available.
So what's going on? How do I create new Capistrano tasks put them in separate file and have them work?
I had the same problem, I created my own recipe in a separate file which I loaded in deploy but couldn't get upload! to work.
What fixed it for me was adding a role filter inside the task making my final recipe look something like this:
namespace :figaro do
desc "Transfer Figaro's application.yml to shared/config"
task :upload do
on roles(:all) do
upload! "config/application.yml", "#{shared_path}/config/application.yml"
end
end
end
before "deploy:check", "figaro:upload"
I hope that helps!
You can create a folder config/recipes for your capistrano recipes if you want to keep them in separate files.
Use the .rb extension since this isnt a regular rake task.
In config/deploy.rb add this line
load File.expand_path('../recipes/my_new_thing.rb', __FILE__)
If you want to use the rake tasks then you will need to create a task in the deploy file which calls that rake tasks which is not that smart of a move. So as #Sharagoz suggested the best route will be to create your own recipe file and include that in.

How to use rake with rake tasks outside rails?

I think Rails is very heavy and I'm taking pieces out of my projects and making them standalone. My library of tasks, I would like it to work outside Rails. So there is no application and no config/application.rb, only the lib/ folder that defines tasks. How should I structure my rakefile to include all the tasks defined in lib/tasks/*rake? My non-working attempt is below.
#!/usr/bin/env/rake
d = Dir["#{File.dirname(__FILE__)}/src/tasks/*.rake" ]
d.each do |file|
require "tasks/"+ File.basename(file, File.extname(file))
end
The invocation is something like bundle exec rake -T -Isrc
Put this in your rake file
Dir["#{File.dirname(__FILE__)}/src/tasks/*.rake" ].each{ |rake_file| load rake_file }

Is there any way to have multiple seeds.rb files? Any kind of 'versioning' for seed data?

We need to add more seed data for some newly added tables to "version 100" of our rails project.
However, if we simply add it to the seeds.rb and re-run the rake db:seed command, it will of course Re-add the original seed data, duplicating it.
So if you've already added seed data to seeds.rb for, say, TableOne ...
How can we incrementally add seed data for TableTwo and TableThree at later stages of development?
I'd hoped I could simply create a NEW seeds_two.rb file and run rake db:seeds_two but that gave an error Don't know how to build task 'db:seeds_two'
So it looks like ONLY "seeds.rb" can be used.
How do people maintain incremental additions to seed data?
You can re-use the seed task, but make it idempotent.
To make the seed idempotent, simply check for the existence of the condition before executing a command. An example: do you want to create a new admin user?
User.find_or_create_by_username(:username => "admin")
instead of
User.create(:username => "admin")
However, seed should be used to populate your database when the project is created. If you want to perform complex data seeding durin the lifecycle of the app, simply create a new rake task, execute it then remove it.
For those who have concerns about this question
We can have multiple seed files in db/seeds/ folder, and, we can write a rake task to run the separate files as we desire to run
# lib/tasks/custom_seed.rake
namespace :db do
namespace :seed do
Dir[File.join(Rails.root, 'db', 'seeds', '*.rb')].each do |filename|
task_name = File.basename(filename, '.rb').intern
# Now we will create multiple tasks by each file name inside db/seeds directory.
task task_name => :environment do
load(filename)
end
end
# This is for if you want to run all seeds inside db/seeds directory
task :all => :environment do
Dir[File.join(Rails.root, 'db', 'seeds', '*.rb')].sort.each do |filename|
load(filename)
end
end
end
end
Then, in order to run specific seed file, you can just run
rake db:seed:seed_file_name
To run all the seeds file with order in that db/seeds folder, run below command
rake db:seed:all

Resources