Ruby scripts with access to Rails Models - ruby-on-rails

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.

Related

Where do I put seed data if I have already created my database in my Rails project?

I’m using Rials 4.2.5. I want to create some seed data for a new model, user_images, I just created in an existing project. However, I already have a db/seeds.rb file that has been run on my database. Where do I put the seed data for this new model? I assume i can’t use db/seeds.rb because it has already been run. It is not an option to blow away the database and start again.
Thanks, - Dave
You can use seeds.. I use, for example:
Person.find_or_create_by(name: 'Bob')
Lots of them, as required, then run as many times as I like.. I run seeds on each auto deployment for example, so I don't forget..
Link to command: http://apidock.com/rails/v4.2.1/ActiveRecord/Relation/find_or_create_by
create a custom rake task in lib/tasks. The file should end in .rake. Then you run it by the name. For example:
task :do_something => :environment do
p "do something"
end
You'd run this task by calling rake do_something in terminal.

How do I run rails g model / rake db:migrate within my rails application

I would like to run "rails g model" and / or "rake db:migrate" within my rails application.
When I do it , The "rails g model" works fine, but take long,
and "rake db:migrate" raised an error
SQLite3::BusyException: database is locked
The error occurs only when I use after_create and after_save, but works fine when I use before_create or around_create.
My model :
class Table < AbstractModel
after_create :create_table
protected
def create_table
system("rails g model #{self.name}")
system("rake db:migrate")
end
end
Rails generators are not intended to be run from within a Rails application. They're not even really necessary. They're just nice-to-haves. So instead of making a file named Table, you can run rails g model table <attrs> and it will generate the file table.rb in the models and generate your migration with whatever attrs you pass it.
Migrations also, should not be run within the application code. You pretty much have to manage them manually or with a deploy script.
As an aside, after_create is a callback for individual instances of Table. So when you call Table.create({<attrs>}) it would call create_table, which is not what what you want.
EDIT:
Using after_commit not after_create seemed to solve the OP's problem.
What are you trying to accomplish?. As said by evanbikes, the way you have set up is that every time you create an instance of table, you are going to generate a new model. You could run rake tasks on a schedule using cron jobs. If you are using heroku you can use heroic scheduler. Hope it helps.

How do I redirect the sql statements in Rails to other than log/development.log?

Something in Rails (ActiveRecord::Base.logger ?) puts all executed SQL into log/development.log.
I have a rails app, whose data is populated by several rake tasks. Often in development, I want to run the web app, and run several of these rake tasks simultaneously (they are long running tasks that talk to other systems and create data in a local database).
Annoyingly, they all log to the same file at the same time.
How/where should I change this? Can I do it from the command line? Where (if in a rake file) should I do it? Or should I create new environments for each rake task?
Is there documentation i should have read to answer this, where is it?
Thanks a bunch.
Depends on what you are really trying to log. In Rails 3.+ you can use the ActiveSupport::LogSubscriber mechanism to hook into Rails internals and log certain interests to other files.
If you really want to hijack the core Rails.logger instance for each of your rake tasks, than do just that.
# lib/tasks/foo.rake
desc "Prints Hello World"
task :helloworld do
Rails.logger = Logger.new("/path/to/hello-world.log")
# do something in your Rails stack that would write to "Rails.logger"
end
Not tested, but I think that might work.
That being said, I think subscribing to interesting events in ActiveRecord via ActiveSupport::LogSubscriber might be a cleaner approach
My current solution is
dec "update statistics"
task :update_stats => :environment do
root_path = Rails.configuration.root_path
env = Rails.configuration.environment
ActiveRecord::Base.logger = Logger.new(File.join(root_path, "log", "#{env}-stats.log"))
#code ...
end
It feels a bit hacky, but maybe that's just the way to do it.

Is seeding data with fixtures dangerous in Ruby on Rails

I have fixtures with initial data that needs to reside in my database (countries, regions, carriers, etc.). I have a task rake db:seed that will seed a database.
namespace :db do
desc "Load seed fixtures (from db/fixtures) into the current environment's database."
task :seed => :environment do
require 'active_record/fixtures'
Dir.glob(RAILS_ROOT + '/db/fixtures/yamls/*.yml').each do |file|
Fixtures.create_fixtures('db/fixtures/yamls', File.basename(file, '.*'))
end
end
end
I am a bit worried because this task wipes my database clean and loads the initial data. The fact that this is even possible to do more than once on production scares the crap out of me. Is this normal and do I just have to be cautious? Or do people usually protect a task like this in some way?
Seeding data with fixtures is an extremely bad idea.
Fixtures are not validated and since most Rails developers don't use database constraints this means you can easily get invalid or incomplete data inserted into your production database.
Fixtures also set strange primary key ids by default, which is not necessarily a problem but is annoying to work with.
There are a lot of solutions for this. My personal favorite is a rake task that runs a Ruby script that simply uses ActiveRecord to insert records. This is what Rails 3 will do with db:seed, but you can easily write this yourself.
I complement this with a method I add to ActiveRecord::Base called create_or_update. Using this I can run the seed script multiple times, updating old records instead of throwing an exception.
I wrote an article about these techniques a while back called Loading seed data.
For the first part of your question, yes I'd just put some precaution for running a task like this in production. I put a protection like this in my bootstrapping/seeding task:
task :exit_or_continue_in_production? do
if Rails.env.production?
puts "!!!WARNING!!! This task will DESTROY " +
"your production database and RESET all " +
"application settings"
puts "Continue? y/n"
continue = STDIN.gets.chomp
unless continue == 'y'
puts "Exiting..."
exit!
end
end
end
I have created this gist for some context.
For the second part of the question -- usually you really want two things: a) very easily seeding the database and setting up the application for development, and b) bootstrapping the application on production server (like: inserting admin user, creating folders application depends on, etc).
I'd use fixtures for seeding in development -- everyone from the team then sees the same data in the app and what's in app is consistent with what's in tests. (Usually I wrap rake app:bootstrap, rake app:seed rake gems:install, etc into rake app:install so everyone can work on the app by just cloning the repo and running this one task.)
I'd however never use fixtures for seeding/bootstrapping on production server. Rails' db/seed.rb is really fine for this task, but you can of course put the same logic in your own rake app:seed task, like others pointed out.
Rails 3 will solve this for you using a seed.rb file.
http://github.com/brynary/rails/commit/4932f7b38f72104819022abca0c952ba6f9888cb
We've built up a bunch of best practices that we use for seeding data. We rely heavily on seeding, and we have some unique requirements since we need to seed multi-tenant systems. Here's some best practices we've used:
Fixtures aren't the best solution, but you still should store your seed data in something other than Ruby. Ruby code for storing seed data tends to get repetitive, and storing data in a parseable file means you can write generic code to handle your seeds in a consistent fashion.
If you're going to potentially update seeds, use a marker column named something like code to match your seeds file to your actual data. Never rely on ids being consistent between environments.
Think about how you want to handle updating existing seed data. Is there any potential that users have modified this data? If so, should you maintain the user's information rather than overriding it with seed data?
If you're interested in some of the ways we do seeding, we've packaged them into a gem called SeedOMatic.
How about just deleting the task off your production server once you have seeded the database?
I just had an interesting idea...
what if you created \db\seeds\ and added migration-style files:
file: 200907301234_add_us_states.rb
class AddUsStates < ActiveRecord::Seeds
def up
add_to(:states, [
{:name => 'Wisconsin', :abbreviation => 'WI', :flower => 'someflower'},
{:name => 'Louisiana', :abbreviation => 'LA', :flower => 'cypress tree'}
]
end
end
def down
remove_from(:states).based_on(:name).with_values('Wisconsin', 'Louisiana', ...)
end
end
alternately:
def up
State.create!( :name => ... )
end
This would allow you to run migrations and seeds in an order that would allow them to coexist more peaceably.
thoughts?

How do I run Ruby tasks that use my Rails models?

I have a Rails app with some basic models. The website displays data retrieved from other sources. So I need to write a Ruby script that creates new instances in my database. I know I can do that with the test hooks, but I'm not sure that makes sense here.
I'm not sure what this task should look like, how I can invoke it, or where it should go in my source tree (lib\tasks?).
For example, here's my first try:
require 'active_record'
require '../app/models/mymodel.rb'
test = MyModel.new
test.name = 'test'
test.save
This fails because it can't get a connection to the database. This makes sense in a vague way to my newbie brain, since presumably Rails is doing all the magic work behind the scenes to set all that stuff up. So how do I set up my little script?
You can load the entire rails environment in any ruby script by simply requiring environment.rb:
require "#{ENV['RAILS_ROOT']}/config/environment"
This assumes the RAILS_ROOT environment variable is set, see my comment for other ways of doing this.
This has the added bonus of giving you all the nice classes and objects that you have in the rest of your rails code.
To kick off your processes it sounds like cron will do what you want, and I would also add a task to your capistrano recipe that would add your script to the crontab to periodically get the data from the external source and update your DB. This can easily be done with the cronedit gem.
The cron approach does have some drawbacks, mostly overhead and control, for other more sophisticated options see HowToRunBackgroundJobsInRails from the rails wiki.
I agree with the answer above but you have to include => :environment in your task or it will not load the Rails environment.
e.g.,
namespace :send do
namespace :trial do
namespace :expiry do
desc "Sends out emails to people who's accounts are about to expire"
task :warnings => :environment do
User.trial_about_to_expire.has_not_been_notified_of_trial_expiry.each do |user|
UserMailer.deliver_trial_expiring_warning(user)
user.notified_of_trial_expiry = true
user.save
end
end
end
end
end
I'd suggest creating custom rake tasks (lib/task/foo.rake). This give you easy access to most of the functionality of your rails app.
namespace :foo do
desc 'do something cool'
def something_cool
test = MyModel.new
test.name = 'test'
test.save
end
end
Then:
$ rake -T foo
rake foo:something_cool # do something cool
You can even run the tasks via a cronjob.
I wrote up a post about this a while back.
http://www.rawblock.com/2007/06/14/ruby-oracle-mac-os-x-pain-jruby-and-activerecord-jdbc-to-the-rescue/
You can open a connection in your scripts as such:
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:username => "root",
:host => "localhost",
:password => "******",
:database => "******"
)
I'm sure there is a more elegant way to do it, so that it grabs the info from your database.yml.
There are few steps to this and more details needed to really answer well.
You say your site retrieves data from other sources? How often? If it is semi-regularly you definitely want to look into background processing/messaging. If it is frequently you really want to avoid loading your rails environment every time your script runs since you will be paying too high a startup tax each time.
There are a multitude of options out there you will want to research. Reading about each of them, particularly reviews from people who post about why they made the choice they did, will give you a good feel for what questions you need to ask yourself before you make your choice. How big a job is loading the data? etc...
Off the top of my head these are some of the things you may want to look into
Script/Runner & Cron
Background/RB
Starling
Workling
MemcacheQ
Beanstalk
Background Job (Bj)
delayed_job (Dj)
Daemon Generator
Check out my answer in "A cron job for rails: best practices?".
It contains two examples for using cron to run Rake tasks and class methods (via script/runner). In both cases, Rails is loaded and you can use your models.
Nice Joyent write up of using rake to run rails tasks from a cron job - http://wiki.joyent.com/accelerators:kb:rails:cron
Easiest way to run ruby tasks 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!

Resources