How to add data to rails app in production? - ruby-on-rails

I'm about to add in a few users manually (because I want to make them admins eg user.admin = true. Previously, in development I've done this using seeds but I believe (correct me if I'm wrong) that you aren't supposed to use seeds in this way in production. What else should I do?

There are a number of options, each with their own pros and cons.
Set up the users in the Rails console
Advantage: simple, can customize as much as needed
Disadvantage: not reproducible
Add users in a migration
Advantage: if this is a new table or relevant data exists in other tables, you can set up the new data right away as part of a migration
Disadvantage: not a standard use of migration scripts
Set up users with a rake task
Advantage: code to reproduce your steps will be checked into your repo
Disadvantage: if you only want to do this once, a rake task is overkill
The best solution might be to add a script in the scripts/ directory of your app detailing the steps, have this code reviewed by your team, and then run it with rails runner.

Do you have access to the production command line? If you're using Heroku you can do this:
$ heroku run rails console
> user = User.new
> user.name = "The name"
// repeat for all other fields
> user.admin = true
> user.save
Or if you already have them in the database as non-admins
$ heroku run rails console
> user = User.find_by(name: "Their Name")
> user.update_attribute(:admin, true)

Related

Rails Seed Data with Association Heroku Error

I'm trying to load some seed data into an app with heroku. I'm more looking just to insert a bunch of seed (sample) data into my app so my client can see it with a lot of objects. (Imagine a demo e-commerce app - I want to be able to show a couple dozen sample products without manually entering them in)
My seed data works fine in development but in production, the one HMBT association causes the below error:
WARNING: Rails was not able to disable referential integrity.
This is most likely caused due to missing permissions.
Rails needs superuser privileges to disable referential integrity.
Heroku documentation said this this so I tried a conditional to remove the referential integrity on production in my seeds file(see below), but now seeds won't run. It just does this:
Running rake db:seed on ⬢ app... up, run.1225 (Free)
ActiveRecord::SchemaMigration Load (1.7ms) SELECT "schema_migrations".* FROM "schema_migrations"
Here's my seeds file below:
if Rails.env.development?
ActiveRecord::Base.connection.disable_referential_integrity do
end
1.upto(14) do |n|
pic_one = File.open(File.join(Rails.root,'app/assets/images/file1.jpg'))
pic_two = File.open(File.join(Rails.root,'app/assets/images/carpet_2/file2.jpg'))
image = PicAttachment.create!([
{picture: pic_one, w_dream_id: "#{n}"},
{picture: pic_two, w_dream_id: "#{n}"},
])
rug = Product.create!(
pic_attachments: image
end
if Rails.env.development?
end
end
Does anyone where I'm going wrong?
The link you posted states that referential integrity cannot be removed in Heroku. It suggests considering using another test data scaffolding tool (like FactoryGirl or Fabrication Gem)
Anyway, your code does nothing if the environment is not development. All code is inside the if Rails.env.development?. The first end corresponds to the do. The indentation is wrong. Your code is in fact:
if Rails.env.development?
ActiveRecord::Base.connection.disable_referential_integrity do
end
1.upto(14) do |n|
pic_one = File.open(File.join(Rails.root,'app/assets/images/file1.jpg'))
pic_two = File.open(File.join(Rails.root,'app/assets/images/carpet_2/file2.jpg'))
image = PicAttachment.create!([
{picture: pic_one, w_dream_id: "#{n}"},
{picture: pic_two, w_dream_id: "#{n}"},
])
rug = Product.create!(
pic_attachments: image
if Rails.env.development?
end
end
Ultimately I took this answer, which was already in my code to ensure associations work in development.
For development, this is needed:
ActiveRecord::Base.connection.disable_referential_integrity do
For production:
the disable_referential_integrity isn't allowed in heroku and the problem is the associated model (Pic_Attachment) gets created before the model object it belongs to, hence an error gets thrown because it needs an object to belong. What worked for me was to delete the disable_referential_integrity from the seeds file AND comment out the belongs_to line in the associated model (PicAttachment), then commit/push your changes and it works. (Add these lines back in afterwards so your development works)
I hope this helps someone. Took me a few days of work here.

Automatically setting who was responsible for a change in the console in paper_trail in Heroku

paper_trail is working great for us. But when we login to Heroku's console with heroku run console and then we make a change, the originator of the change is not set :(. So paper_trail has a mechanism for this:
In a console session you can manually set who is responsible like this:
>> PaperTrail.whodunnit = 'Andy Stewart'
>> widget.update_attributes :name => 'Wibble'
>> widget.versions.last.whodunnit # Andy Stewart
You can avoid having to do this manually by setting your initializer to pick up the username of the current user from the OS, like this:
<bunch of code>
My question is how we can automate this with Heroku? Is there maybe something like this I can do?
heroku run console -e "PaperTrail.whodunnit = '123:console'"
Then each of us could simply create a console.sh file with the above using our individual user ids. Or is there another mechanism to automate this?
You can avoid having to do this manually by setting your initializer
to pick up the username of the current user from the OS
That's basically the answer. Place the PaperTrail.whodunnit code in an initializer in config/initializers. These run when you start up the console. That would be the best place since it'll take effect when any of the devs login to the heroku console without having to change the command they run to do that:
# config/initializers/default_whodunnit.rb
if defined?(Rails::Console)
PaperTrail.whodunnit = "whatever"
end
Update:
Heroku is a git server, just like Github, but you push to heroku to initiate a deploy. MrYoshiji's answer provides an option to give your devs a chance to state their usernames and have it set as the whodunnit. Just make his answer's code sample the contents of your default_whodunnit.rb initializer. This way you'll check in the whodunnit file, push to Heroku, and now any time a dev logs in to the heroku console they'll get asked for their username and that will set the whodunnit
You could do this in your application.rb file:
if Rails.const_defined? 'Console'
STDOUT.print "Enter your name for PaperTrail.whodunnit : "
username = STDIN.gets.split("\n").first
puts "Hello #{username}!"
PaperTrail.whodunnit = username
end
Only when you will launch a console, the user will be asked to fill a username which will be used as the author of the changes.

Ruby scripts with access to Rails Models

Where and how do I run a simple script that uses my rails environment. Specifically I have one column that holds multiple pieces of information, I've added columns now for each piece of information and need to run a ruby script that can run to call a method on each row of the database to extrapolate data and save it to the new column.
Using a migration sounds like the right way to go if I am understanding your use case.
However, if you really do want to write a standalone script that needs access to your Rails application's models, you can require the environment.rb file from inside your standalone script.
Example:
#!/bin/env ruby
ENV['RAILS_ENV'] = "production" # Set to your desired Rails environment name
require '/path/to/railsapp/config/environment.rb'
# After this point you have access to your models and other classes from your Rails application
model_instance = MyModel.find(7)
model_instance.some_attribute = "new value"
model_instance.save
I have to agree with David here. Use a migration for this. I'm not sure what you want to do, but running it from inside your environment is much, much more efficient then loading up the app environment manually. And since your initial post suggests you're only doing this once, a migration is the way to go:
rails g migration MigrateData
.. generates:
class MigrateData < ActiveRecord::Migration
def self.up
# Your migration code here
end
def self.down
# Rollback scenario
end
end
Of course, you will always want to perform this locally first, using some test data.
Agree with everyone, for this specific case it sounds like migration will be way to go, however, to do this regularly, or write some other task/script that interacts rails app environment make rails generate a rake task for you! This gets saved with your rails app, and can be run again and again :)
Easiest way to generate a rake task that interact with rails app/models is to make Rails generate Rake tasks for you!! :)
Here's an example
run rails g task my_namespace my_task
This will generate a file called lib/tasks/my_namespace.rake which looks like:
namespace :my_namespace do
desc "TODO: Describe your task here"
task :my_task1 => :environment do
#write any ruby code here and also work with your models
puts User.find(1).name
end
end
Run this task with rake my_namespace:my_task
Watch your ruby code task that interacts with rails modal run!
Seeding data:
http://railscasts.com/episodes/179-seed-data
Adding data with migrations
http://railscasts.com/episodes/23-counter-cache-column
Working with Rake Tasks
http://railscasts.com/episodes/66-custom-rake-tasks
I prefer to use migrations for adding some data in your case.
If it's a one-time thing, use a migration.
If this is something that needs to be done multiple times, use a rake task for it.

Ruby on Rails 2.3.5: Populating my prod and devel database with data (migration or fixture?)

I need to populate my production database app with data in particular tables. This is before anyone ever even touches the application. This data would also be required in development mode as it's required for testing against. Fixtures are normally the way to go for testing data, but what's the "best practice" for Ruby on Rails to ship this data to the live database also upon db creation?
ultimately this is a two part question I suppose.
1) What's the best way to load test data into my database for development, this will be roughly 1,000 items. Is it through a migration or through fixtures? The reason this is a different answer than the question below is that in development, there's certain fields in the tables that I'd like to make random. In production, these fields would all start with the same value of 0.
2) What's the best way to bootstrap a production db with live data I need in it, is this also through a migration or fixture?
I think the answer is to seed as described here: http://lptf.blogspot.com/2009/09/seed-data-in-rails-234.html but I need a way to seed for development and seed for production. Also, why bother using Fixtures if seeding is available? When does one seed and when does one use fixtures?
Usually fixtures are used to provide your tests with data, not to populate data into your database. You can - and some people have, like the links you point to - use fixtures for this purpose.
Fixtures are OK, but using Ruby gives us some advantages: for example, being able to read from a CSV file and populate records based on that data set. Or reading from a YAML fixture file if you really want to: since your starting with a programming language your options are wide open from there.
My current team tried to use db/seed.rb, and checking RAILS_ENV to load only certain data in certain places.
The annoying thing about db:seed is that it's meant to be a one shot thing: so if you have additional items to add in the middle of development - or when your app has hit production - ... well, you need to take that into consideration (ActiveRecord's find_or_create_by...() method might be your friend here).
We tried the Bootstrapper plugin, which puts a nice DSL over the RAILS_ENV checking, and lets your run only the environment you want. It's pretty nice.
Our needs actually went beyond that - we found we needed database style migrations for our seed data. Right now we are putting normal Ruby scripts into a folder (db/bootstrapdata/) and running these scripts with Arild Shirazi's required gem to load (and thus run) the scripts in this directory.
Now this only gives you part of the database style migrations. It's not hard to go from this to creating something where these data migrations can only be run once (like database migrations).
Your needs might stop at bootstrapper: we have pretty unique needs (developing the system when we only know half the spec, larg-ish Rails team, big data migration from the previous generation of software. Your needs might be simpler).
If you did want to use fixtures the advantage over seed is that you can easily export also.
A quick guess at how the rake task may looks is as follows
desc 'Export the data objects to Fixtures from data in an existing
database. Defaults to development database. Set RAILS_ENV to override.'
task :export => :environment do
sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
export_tables = [
"roles",
"roles_users",
"roles_utilities",
"user_filters",
"users",
"utilities"
]
time_now = Time.now.strftime("%Y_%h_%d_%H%M")
folder = "#{RAILS_ROOT}/db/fixtures/#{time_now}/"
FileUtils.mkdir_p folder
puts "Exporting data to #{folder}"
ActiveRecord::Base.establish_connection(:development)
export_tables.each do |table_name|
i = "000"
File.open("#{folder}/#{table_name}.yml", 'w') do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
file.write data.inject({}) { |hash, record|
hash["#{table_name}_#{i.succ!}"] = record
hash }.to_yaml
end
end
end
desc "Import the models that have YAML files in
db/fixture/defaults or from a specified path."
task :import do
location = 'db/fixtures/default'
puts ""
puts "enter import path [#{location}]"
location_in = STDIN.gets.chomp
location = location_in unless location_in.blank?
ENV['FIXTURES_PATH'] = location
puts "Importing data from #{ENV['FIXTURES_PATH']}"
Rake::Task["db:fixtures:load"].invoke
end

How (and whether) to populate rails application with initial data

I've got a rails application where users have to log in. Therefore in order for the application to be usable, there must be one initial user in the system for the first person to log in with (they can then create subsequent users). Up to now I've used a migration to add a special user to the database.
After asking this question, it seems that I should be using db:schema:load, rather than running the migrations, to set up fresh databases on new development machines. Unfortunately, this doesn't seem to include the migrations which insert data, only those which set up tables, keys etc.
My question is, what's the best way to handle this situation:
Is there a way to get d:s:l to include data-insertion migrations?
Should I not be using migrations at all to insert data this way?
Should I not be pre-populating the database with data at all? Should I update the application code so that it handles the case where there are no users gracefully, and lets an initial user account be created live from within the application?
Any other options? :)
Try a rake task. For example:
Create the file /lib/tasks/bootstrap.rake
In the file, add a task to create your default user:
namespace :bootstrap do
desc "Add the default user"
task :default_user => :environment do
User.create( :name => 'default', :password => 'password' )
end
desc "Create the default comment"
task :default_comment => :environment do
Comment.create( :title => 'Title', :body => 'First post!' )
end
desc "Run all bootstrapping tasks"
task :all => [:default_user, :default_comment]
end
Then, when you're setting up your app for the first time, you can do rake db:migrate OR rake db:schema:load, and then do rake bootstrap:all.
Use db/seed.rb found in every Rails application.
While some answers given above from 2008 can work well, they are pretty outdated and they are not really Rails convention anymore.
Populating initial data into database should be done with db/seed.rb file.
It's just works like a Ruby file.
In order to create and save an object, you can do something like :
User.create(:username => "moot", :description => "king of /b/")
Once you have this file ready, you can do following
rake db:migrate
rake db:seed
Or in one step
rake db:setup
Your database should be populated with whichever objects you wanted to create in seed.rb
I recommend that you don't insert any new data in migrations. Instead, only modify existing data in migrations.
For inserting initial data, I recommend you use YML. In every Rails project I setup, I create a fixtures directory under the DB directory. Then I create YML files for the initial data just like YML files are used for the test data. Then I add a new task to load the data from the YML files.
lib/tasks/db.rake:
namespace :db do
desc "This loads the development data."
task :seed => :environment do
require 'active_record/fixtures'
Dir.glob(RAILS_ROOT + '/db/fixtures/*.yml').each do |file|
base_name = File.basename(file, '.*')
say "Loading #{base_name}..."
Fixtures.create_fixtures('db/fixtures', base_name)
end
end
desc "This drops the db, builds the db, and seeds the data."
task :reseed => [:environment, 'db:reset', 'db:seed']
end
db/fixtures/users.yml:
test:
customer_id: 1
name: "Test Guy"
email: "test#example.com"
hashed_password: "656fc0b1c1d1681840816c68e1640f640c6ded12"
salt: "188227600.754087929365988"
This is my new favorite solution, using the populator and faker gems:
http://railscasts.com/episodes/126-populating-a-database
Try the seed-fu plugin, which is quite a simple plugin that allows you to seed data (and change that seed data in the future), will also let you seed environment specific data and data for all environments.
I guess the best option is number 3, mainly because that way there will be no default user which is a great way to render otherwise good security useless.
I thought I'd summarise some of the great answers I've had to this question, together with my own thoughts now I've read them all :)
There are two distinct issues here:
Should I pre-populate the database with my special 'admin' user? Or should the application provide a way to set up when it's first used?
How does one pre-populate the database with data? Note that this is a valid question regardless of the answer to part 1: there are other usage scenarios for pre-population than an admin user.
For (1), it seems that setting up the first user from within the application itself is quite a bit of extra work, for functionality which is, by definition, hardly ever used. It may be slightly more secure, however, as it forces the user to set a password of their choice. The best solution is in between these two extremes: have a script (or rake task, or whatever) to set up the initial user. The script can then be set up to auto-populate with a default password during development, and to require a password to be entered during production installation/deployment (if you want to discourage a default password for the administrator).
For (2), it appears that there are a number of good, valid solutions. A rake task seems a good way, and there are some plugins to make this even easier. Just look through some of the other answers to see the details of those :)
Consider using the rails console. Good for one-off admin tasks where it's not worth the effort to set up a script or migration.
On your production machine:
script/console production
... then ...
User.create(:name => "Whoever", :password => "whichever")
If you're generating this initial user more than once, then you could also add a script in RAILS_ROOT/script/, and run it from the command line on your production machine, or via a capistrano task.
That Rake task can be provided by the db-populate plugin:
http://github.com/joshknowles/db-populate/tree/master
Great blog post on this:
http://railspikes.com/2008/2/1/loading-seed-data
I was using Jay's suggestions of a special set of fixtures, but quickly found myself creating data that wouldn't be possible using the models directly (unversioned entries when I was using acts_as_versioned)
I'd keep it in a migration. While it's recommended to use the schema for initial setups, the reason for that is that it's faster, thus avoiding problems. A single extra migration for the data should be fine.
You could also add the data into the schema file, as it's the same format as migrations. You'd just lose the auto-generation feature.
For users and groups, the question of pre-existing users should be defined with respect to the needs of the application rather than the contingencies of programming. Perhaps your app requires an administrator; then prepopulate. Or perhaps not - then add code to gracefully ask for a user setup at application launch time.
On the more general question, it is clear that many Rails Apps can benefit from pre-populated date. For example, a US address holding application may as well contain all the States and their abbreviations. For these cases, migrations are your friend, I believe.
Some of the answers are outdated. Since Rails 2.3.4, there is a simple feature called Seed available in db/seed.rb :
#db/seed.rb
User.create( :name => 'default', :password => 'password' )
Comment.create( :title => 'Title', :body => 'First post!' )
It provides a new rake task that you can use after your migrations to load data :
rake db:seed
Seed.rb is a classic Ruby file, feel free to use any classic datastructure (array, hashes, etc.) and iterators to add your data :
["bryan", "bill", "tom"].each do |name|
User.create(:name => name, :password => "password")
end
If you want to add data with UTF-8 characters (very common in French, Spanish, German, etc.), don't forget to add at the beginning of the file :
# ruby encoding: utf-8
This Railscast is a good introduction : http://railscasts.com/episodes/179-seed-data

Resources