Make custom commands for ruby on rails - ruby-on-rails

I want make new command line program for my ruby on rails application, but I can't found how I can make new command.
I mean, this functional analogue in RoR.
Help me please, and tell me please how I can fix my problem.
Thanks a lot.
P.S. sorry about my English.
ADD1: I want create backend script for parse xmls and save its in database. I think, will be very good if I can use AR classes from my RoR application.

Create you rake task file in lib/tasks/my_self.rake
and write something like:
namespace :my_commands do
task :this_command do
puts "this is My Command"
end
end
If you want access to your Applications Models you must initialize rails for you applications with your task including environment.rb:
# write task name with :environment if you use task with AR
namespace :my_commands do
task this_command: :environment do
puts "this is My Command"
user = User.first
puts user.name
end
end
Now you can run you task code in terminal:
$ rails my_commands:this_command

For command line prog just use rune as follows :
rails runner "model.method"
as in case of console and server you can also mention the environment for running.
For parsing xml there a lot of options like LibXML.

Related

Invoke ActionMailer from cron job in Rails 3?

Is it possible to invoke ActionMailer from a cron job? We're using Rails 3.
Ideally, a cron job triggers a script to read users from a database, passes these users and some variables to an ActionMailer class to fire off emails.
Is this possible in Rails 3.2.12?
Yes it is possible. You could use a task to invoke with the rake command. Your task could be something like this:
# lib/tasks/cron.rake
namespace :cron do
desc "Send account emails"
task deliver_emails: :environment do
accounts_for_delivery = Account.where(condition: true)
# ... whatever logic you need
accounts_for_delivery.each do |account|
Postman.personalized_email_for(account).deliver
end
end
end
And your mailer and the corresponding view could look like this:
# app/mailers/postman.rb
class Postman < ActionMailer::Base
def personalized_email_for(account)
#account = account
mail to: account.email
end
end
# app/views/postman/personalized_email_for.text.haml
= #account.inspect
Now you can set the crontab to run your rake task just like you perform rake tasks. I recommend you use the whenever gem, that really provides a nice way to define cronjobs for your application that looks like this:
# config/schedule.rb
every 6.hours do
rake 'cron:deliver_email'
end
So now the cronjob definitions are bound your application. It works well with Capistrano between deployments as well. You can also pass variables at your task or execute system commands.
If everything else fails you can just create a normal controller action and let the cronjob call it with curl.
Otherwise any script in your Rails apps script folder can be started with rails runner script/myscript.rb from the commandline and has full access to all Rails features.
You can use rails r (rails runner) to run a script in your rails app. It runs it, loading in the full context of your rails app before doing so, so all your models etc. are available. I use it a lot. For example,
rails r utilities/some_data_massaging_script.rb
From cron, you'd obviously need to give it the full path to your app.
The old-fashioned way was to have something like:
require "#{File.dirname(__FILE__)}/../config/environment.rb"
at the top of your script (adjusting the relative bit of the path depending on the subdirectory level of your script in your app of course) and then just run your script using ruby, but rails r makes that unnecessary.

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.

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.

Rails: How to write to a custom log file from within a rake task in production mode?

I'm trying to write to my log files while running a rake task. It works fine in development mode, but as soon as I switch to the production environment, nothing is written to the log files.
I read here
How do I use a custom log for my rake tasks in Ruby on Rails?
that this is the normal behavior and also found a #wontfix ticket in lighthouse.
My question: Is there a way to output, what's going on while my rake task is running? It performs some crawling and runs for hours. I would prefer if the output went in a specific log file like /log/crawler.log
Right now I'm just using this command to write to the log files:
ActiveRecord::Base.logger.info "Log Text"
Thanks!
The problem you are having is that rails ignores 'info' level logs in production mode.
I'd recommend reading this: http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/classes/Logger.html
and creating your own logger:
logger = Logger.new('logfile.log')
logger.info "Something happened"
You can make a new logger with Logger.new("file.log") and then call it's methods like this.
task :import_stuff => :environment do
require 'csv'
l = Logger.new("stuff.log")
csv_file = "#{RAILS_ROOT}/stuff.csv"
CSV.open(csv_file, 'r') do |row|
l.info row[1]
end
end
Maybe you need to write out the buffer where you need it:
logger.flush
or you can turn on auto flushing:
task :foo => :environment do
Rails.logger.auto_flushing = 1
Rails.logger.info "bar"
end

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