The case of the disappearing ActiveRecord attribute - ruby-on-rails

Following the instructions in https://stackoverflow.com/a/24496452/102675 I wound up with the following:
namespace :db do
desc 'Drop, create, migrate, seed and populate sample data'
task seed_sample_data: [:drop, :create, :migrate, :seed, :populate_sample_data] do
puts 'Sample Data Populated. Ready to go!'
end
desc 'Populate the database with sample data'
task populate_sample_data: :environment do
puts Inspector.column_names.include?('how_to_fix')
# create my sample data
end
end
As you would expect, I get true if I run bundle exec rake db:populate_sample_data
BUT if I run bundle exec rake db:seed_sample_data I get all the migration output and then false. In other words I can't see the Inspector attribute how_to_fix even though it definitely exists as proved by the other rake run. Where did my attribute go?

My guess is that this is a "caching" problem. Can you try the following?
task populate_sample_data: :environment do
Inspector.reset_column_information
# ...
end
P.S. We used to have a similar problem working with different databases having the exact same schema (only except some columns here and there)

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 do I verify if a series of rake tasks ran correctly? Or, what alternatives are there to rake

Somebody has asked a similar question here:
https://github.com/jimweirich/rake/issues/257
The answer from the maintainer was:
I am going to reject this since it allows you to use tasks in non-rake-like ways.
So what are the correct way of using rake if a task depends of other tasks.
task 'succeed' => ['db:drop','stats'] do something end
displays results of stats even if Postgres threw an error and db:drop failded because of active connections.
If rake is not suitable for system maintenace, what tools should I use?
I need to be able to run a backup of a database, then do some tests, then drop the database and finally restore from backup.
to hel you understand my problem look at folowing fragment
namespace :experiment do
desc "TODO"
task 'succeed' => ['stopme', 'stats'] do
puts 'this and stats task should not run'
end
desc "TODO"
task stopme: :environment do
Rake::Task['db:drop'].invoke
end
end
You can invoke tasks manually like that:
task :stats => :environment do
Rake::Task['db:drop'].invoke rescue nil
# do something
end

How can you make a rake task that makes changes in multiple environments?

I have a rake task that I use to populate my development database. When it is done I would like it to also reset the test database, but I can't figure out the syntax. I need something like this:
namespace :db do
task populate: :environment do
Rake::Task["db:reset"].execute
Rake::Task["db:reset"].execute RAILS_ENV=test
# Add lots of data to the :environment database
end
end
This lets me run rake db:populate to populate my development database using the latest schema as well as reset the test database.
The task db:test:clone_structure will reset the test database schema to match the development database schema
namespace :db do
task populate: :environment do
Rake::Task["db:reset"].execute
Rake::Task["db:test:clone_structure"].execute
# Add lots of data to the :environment database
end
end

Rails recommended way to add sample data

I have a Rake script similar to below,but I am wondering if there is a more efficient way to do this, without having to drop the database, run all the migrations, reseed the database and then add the sample data?
namespace :db do
desc 'Fill database with sample data'
task populate: :environment do
purge_database
create_researchers
create_organisations
add_survey_groups_to_organisations
add_members_to_survey_groups
create_survey_responses_for_members
end
end
def purge_database
puts 'about to drop and recreate database'
system('rake db:drop')
puts 'database dropped'
system('rake db:create')
system('rake db:migrate')
system('rake db:seed')
puts 'Database recreated...'
end
def create_researchers
10.times do
researcher = User.new
researcher.email = Faker::Internet.email
researcher.save!
end
end
You should not fill your database with sample data via db:seed. That's not the purpose of the seeds file.
db:seed is for initial data that your app needs in order to function. It's not for testing and/or development purposes.
What I do is to have one task that populates sample data and another task that drops the database, creates it, migrates, seeds and populates. The cool thing is that it's composed of other tasks, so you don't have to duplicate code anywhere:
# lib/tasks/sample_data.rake
namespace :db do
desc 'Drop, create, migrate, seed and populate sample data'
task prepare: [:drop, :create, "schema:load", :seed, :populate_sample_data] do
puts 'Ready to go!'
end
desc 'Populates the database with sample data'
task populate_sample_data: :environment do
10.times { User.create!(email: Faker::Internet.email) }
end
end
I would suggest making rake db:seed self sufficient. By which I mean, you should be able to run it multiple times without it doing any damage, while ensuring that whatever sample data you need loaded gets loaded.
So, for your researches, the db:seed task should do something like this:
User.destroy_all
10.times do
researcher = User.new
researcher.email = Faker::Internet.email
researcher.save!
end
You can run this over and over and over and are ensured you will always end up with 10 random users.
I see this is for development. In that case, I wouldn't put it in db:seed as that might get run in production. But you can put it in a similar rake task that you can re-run as often as needed.

Overriding rails' default rake tasks

I have a Rails 2.2 project in which I want to override the functionality of the rake db:test:prepare task. I thought this would work, but it doesn't:
#lib/tasks/db.rake
namespace :db do
namespace :test do
desc "Overridden version of rails' standard db:test:prepare task since the schema dump used in that can't handle DB enums"
task :prepare => [:environment] do
puts "doing db:structure:dump"
Rake::Task['db:structure:dump'].invoke
puts "doing db:test:clone_structure"
Rake::Task['db:test:clone_structure'].invoke
end
end
end
I get the standard task's behaviour. If I change the name of the task to :prepare2 and then do rake db:test:prepare2, then it works fine. The natural conclusion I draw from this is that my rake tasks are being defined before the built-in Rails ones, so mine is overridden by the standard :prepare task.
Can anyone see how I can fix this? I'd rather override it than have to use a new task. Thanks, max
If you define a rake task that already exists, its execution gets appended to the original task's execution; both tasks will be executed.
If you want to redefine a task you need to clear the original task first:
Rake::Task["db:test:prepare"].clear
It's also useful to note that once a task has been executed in rake, it won't execute again even if you call it again. This is by design but you can call .reset on a task to allow it to be run again.
You have to remove the default task before adding your own:
Rake.application.instance_variable_get('#tasks').delete('db:test:prepare')
namespace 'db' do
namespace 'test' do
task 'prepare' do
# ...
end
end
end
A fairly popular idiom is to create a convenience method called remove_task like so:
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
end
def remove_task(task_name)
Rake.application.remove_task(task_name)
end
(Source: drnic/newgem)
Create a new project.rake file at lib/tasks/, and paster below code into it.
namespace :mv do
desc "Display hint and info for your rails 4 project"
task info: :environment do
puts 'Run rake test to test'
end
end
task(:default).clear.enhance ['mv:info']
inspired by Krasimir Angelov's blog

Resources