loading...

Rake::Task .enhance() Method Explained

molly_struve profile image Molly Struve (she/her) Updated on ・2 min read

I recently fixed a bug in our Forem software that led to some serious Ruby learnings that I have to share.

The Bug

The problem we were having was that a Postgres view we use to expose data to our team, called hypershield, was not getting refreshed correctly. Ideally, we want this view refreshed AFTER we run migrations so that it is up to date with the database. However, it came to my attention when looking at our logs that the hypershield view was being refreshed BEFORE we ran migrations.

[hypershield] Refreshing schemas
[hypershield] Success!
== 20200726215928 ChangeTagIdsToBigints: migrating ============================
== 20200726215928 ChangeTagIdsToBigints: migrated (4.9509s) ===================

This led to our hypershield views being out of date with our actual database.

.enhance()

To ensure that the hypershield view is refreshed when we migrate our database we use the Rake::Task method .enhance().

Rake::Task["db:prepare"].enhance(["hypershield:refresh"])

In the past when I used the method enhance it always ran the additional rake task AFTER the task I was "enhancing". This got me really confused as to why the behavior was suddenly different, so I went digging.

During my digging, I came across the Rake::Task docs. Here, I opened up the source code for the method.

def enhance(deps=nil, &block)
  @prerequisites |= deps if deps
  @actions << block if block_given?
  self
end

The first thing that struck me was that the behavior was different if you passed in an argument versus a block.

When passed an argument, that argument became one of the @prerequisites for the task, meaning it was run BEFORE. When passed a block, the block was added to a list of @actions. According to the docs, @actions are "attached to a task", meaning they run AFTER the task.

The Fix

If I want to refresh our hypershield view after we run migrations I need to pass that refresh task to enhance in a block and not as an argument. The final fix looks like this:

Rake::Task["db:prepare"].enhance do
  Rake::Task["hypershield:refresh"].execute
end

Now, the hypershield view is updated AFTER migrations are run. This ensures the view is always up to date with our database. NOTE the .execute that we have to use to invoke our task. This is not needed when you pass the task as an argument, but it is needed when you are using a block.

TL;DR

Passing a task as an argument to enhance causes it to run BEFORE the task you are "enhancing".

Rake::Task["task_A"].enhance(["task_B"])
# Runs task_B
# Runs task_A

Passing a task to enhance in a block causes it to run AFTER the task you are "enhancing".

Rake::Task["task_A"].enhance do
  Rake::Task["task_B"].execute
end
# Runs task_A
# Runs task_B

Now go and enhance away!!!

Alt Text

Posted on by:

molly_struve profile

Molly Struve (she/her)

@molly_struve

International Speaker πŸ—£ Runner πŸƒβ€β™€οΈ Always Ambitious. Never Satisfied. I ride πŸ¦„'s IRL

Discussion

markdown guide
 

TIL, enhance existed. πŸ˜πŸ‘πŸ½

 
 
 

Thank you Molly! Super useful - sneaky differences in behavior! πŸ™