When to Use Rake Tasks in Rails - ruby-on-rails

I'm confused over whether it is the Rails way to place some code in a Rake Task or in a Model.
I have some code that is only run as a background job to send an email to a User if one of their friends had any activity for a day
I'm using the Whenever gem to schedule the execution of the method/task which allows me to run the code from either the Model or a Rake Task
Can someone help me with this newbie question of if the Rails way would be to put the background job-only code in the Model or Rake Task?

This as nothing to do with rails way but with object oriented programming. I would say you should place the behavior at a class so you can use it form every place in your app. If you place it at the rake task you would have behavior there. And this kind of rake task is to do something but who should know about how to do it should be the model or some other class. For example if you want to use resque in the future the changes to your code will be easier to do if you have it inside of a class and not locked inside a rake task.

Related

Calling rake task on web request

In my Rails app, the user can upload a file and then what I need to do is: when the file is uploaded I want to start a rake task, which parses the file and feeds all the tables in the database, and depending on how large is the file, it can take some time. I assume sth like >> system "rake task_name" in my controller will do the work.
However is it the best practice? Is it safe? Because in that way any user would be starting a rake process. In this Railcast they recommend running rake in background. What would be an alternative or the common practice?
I am using Rails 3.2, so I couldn't use Active Job. I used Sidekiq and worked fine.
Check a very helpful tutorial here

What's the best method for creating "test" database content in >= Rails 3.2.0?

I want to be able to create a few dozen users, articles (or whatever resources are unique to the app), etc to see how the app looks and responds when full. This is just for testing/dev purposes, so I want to be able to roll it back, destroy it, or whatever easily. Perhaps I'm overthinking it, who knows.
I've seen people recommend just using a standard migration, which is one idea, but I want to do this OPTIONALLY, I don't want everyone on the project to get the sample content as they update the app.
Other people have mentioned Factory Girl, but that looks like it might be either overkill or a side-use of a gem really designed for testing, etc. It wasn't perfectly clear.
So what do you all do in this case?
I recommend a rake task. You can stick it in lib/tasks and so everyone in the project gets it, but not everyone needs to run it, and only when it's run will it do anything. This a great tutorial on writing rake tasks, just remember to read the part under the Rails heading in order to learn how to bring in your models.
After that, your rake tasks is basically just ruby code. I'd suggest using the dynamic find_or_create_by methods in order to explicitly create the models you want, and if it's run multiple times, they won't be created multiple times. You can also choose to destroy all records in a particular model before creating them.
I wouldn't recommend using Factory Girl because you probably want explicit control over how your models are created.
Here's an example rake task to show how easy it is:
#lib/tasks/my_task.rake
task :fake_data => :environment do
MyModel.find_or_create_by_name("Test")
end
Then in your console:
rake fake_data
Or:
rake fake_data RAILS_ENV=test
Ta da!
Take a look at Rails seed data features
http://railscasts.com/episodes/179-seed-data

Rails: whenever + delayed_job to optimize rake tasks?

I use whenever to call rake tasks throughout the day, but each task launches a new Rails environment. How can I run tasks throughout the day without relaunching Rails for each job?
Here's what I came up with, would love to get some feedback on this...?
Refactor each rake task to instead be a method within the appropriate Model.
Use the delayed_job gem to assign low priority and ensure these methods run asynchronously.
Instruct whenever to call each Model.method instead of calling the rake task
Does this solution make sense? Will it help avoid launching a new Rails environment for each job? .. or is there a better way to do this?
--
Running Rails 3
You could certainly look into enqueuing delayed_jobs via cron, then having one long running delayed_job worker.
Then you could use whenever to help you create the delayed_job enqueuing methods. It's probably easiest to have whenever's cron output call a small wrapper script which loads active_record and delayed_job directly rather than your whole rails stack. http://snippets.aktagon.com/snippets/257-How-to-use-ActiveRecord-without-Rails
You might also like to look into clockwork.rb, which is a long-running process that would do the same thing you're using cron for (enqueuing delayed_jobs): http://rubydoc.info/gems/clockwork/0.2.3/frames
You could also just try using a requeuing strategy in your delayed_jobs: https://gist.github.com/704047
Lots of good solutions to this problem, the one I eventually ended up integrating is as such:
Moved my rake code to the appropriate models
Added controller/routing code for calling models methods from the browser
Configured cronjobs using the whenever gem to run command 'curl mywebsite.com/model#method'
I tried giving delayed_job a go but didn't like the idea of running another Rails instance. My methods are not too server intensive, and the above solution allows me to utilize the already-running Rails environment.
Comment this line from schedule.rb
require File.expand_path(File.dirname(__FILE__) + "/environment")
Instead load only required ruby files like models in your case.

Rails - Is it okay (or possible) to run rake inside a controller?

I am using the sitemap_generator gem to build an xml sitemap. From the readme:
... run rake sitemap:refresh as needed to create/rebuild your Sitemap files
I'd prefer to do this any time the create action is ran in my Content controller. Is there a best practice for doing something like this?
It is possible, yes. But I would not recommend it. Rake tasks tend to take at least a few seconds to run which will occupy a server request and prolong the response to the client.
If you want to refresh the sitemap after every create, then I would recommend one of two solutions. Either analyse what the rake task sitemap:refresh does and use the code directly from your controller. But I would only do that as long as it doesn't take too much time to run and since I do not know much about sitemap_generator, I can't tell.
The other option would be to run the rake task from a delayed_job which I found to be the preferred alternative. That way, you can trigger the job from your create action but you don't have to wait for it to finish.

Rake task for scraping with rails

I'm starting to write scrapers to get data from different websites. I built the first scraper in a rake file and am now starting to write a second rake file to get data from a second site. For now, I am writing a scraper specific to each site I'm interested in (not trying to build a generic scraper).
I have 3 questions:
Is writing rake tasks a good choice for me? Are there alternatives I should consider?
How can I add functions/methods to my rake files? (sorry, very silly questions, but I can't figure out how to structure my code... so for now it's just 500 lines of uninterrupted code in a long method) for instance, I'd like a "get_description(section)" method that returns the description from the page. The method could be different depending on which site I'm scraping.
How can I test my task with RSpec? I'd like to give a link and make sure the output of my tasks matches what I expect to get
Thanks for your help!
As a general principle, rake tasks should be very minimal. Refer the actual behavior to real classes. These classes can then be easily tested.
Example:
task :scrape do
Scraper.scrape!
end
class Scraper
def self.scrape!
# do something
end
end
describe Scraper do
# your tests
end
You could, as #brad indicated, use thor, which has a regular class structure by itself, so in theory it should be easier to test the tasks themselves. I haven't done that though.
You can define methods in rake, but I don't know where they end up. You shouldn't do that, so don't bother. Keep task bodies minimal, write normal code to do the dirty work.
Sure rake is fine if you want to use it, you can also check out thor which uses more standard ruby-like syntax rather than the dsl rake provides you.
Rake is just another ruby library so you can include whatever you like in there. As such you can write your own library and load it in your rake file. Check out how Bundler does it for instance. They've just defined their own classes, then created tasks inside of it. It uses thor by the way, which, from what I can gather somehow proxies those tasks on to rake, haven't really looked through it thoroughly though so i could be wrong.
If you're defining things in your own library, just use rspec as you normally would for any other project, then hook that library into rake or thor with whatever means and you're off to the races

Resources