Search code examples
ruby-on-railsrubyruby-on-rails-4rake

How to design a rake task that calls multiple class methods?


I have a rake task called pull_orders which calls methods of a RemoteDbConnector class to do things like establish a connection to an external db, generate raw SQL queries, execute queries and store records in the local db.

While trying to test it, I stumbled upon this answer which got me thinking whether my design is flawed.

Should rake tasks really be one liners? If so, where should I put all these method calls, given that they need to be called in a particular sequence?

My task looks like this:

namespace :db do
  desc 'finds and populates data from remote db'
  task pull_orders: :environment do
    ...
    columns = ...
    table = ...
    options = ...
    column_mappings = ...
    RemoteDbConnector.generate_query(...)
    RemoteDbConnector.execute_query(...)
    RemoteDbConnector.map_column_names(...)
    Order.create(...) #creates records based on hash generated by RemoteDbConnector
    ...

  end
end

Solution

  • Reasonable people will probably not enforce a strict rule of no more than one line per rake task, but it is definitely nicer to move large blocks of code out of .rake files into classes:

    • Short rake tasks make the structure of the .rake file clearer.
    • Large blocks of code should usually be broken up into multiple small methods with names that explain what they do. You could just do that in the .rake file, but then you'd have a mixture of rake tasks and methods, which would not be as easy to read as a uniform set of rake tasks.
    • Normal classes are easier to test.

    So,

    • Extract classes out of your big rake tasks. You can initially define such a class in the same .rake file as the task you extracted it from as an intermediate step.
    • Move the classes from .rake files to your project's lib directory, not in lib/tasks (which is for .rake files) but in the root of lib or in a subdirectory of lib corresponding to their namespace if they have one.
    • require the classes in the rake tasks that use them. (Don't configure your app to autoload lib; that would load unwanted code and its dependencies in your production application.)
    • Test the classes as you would any classes.