DEV Community

loading...

Rake::Task .enhance() Method Explained

Molly Struve (she/her)
International Speaker πŸ—£ Runner πŸƒβ€β™€οΈ Always Ambitious. Never Satisfied. I ride πŸ¦„'s IRL
・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) ===================
Enter fullscreen mode Exit fullscreen mode

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"])
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now go and enhance away!!!

Alt Text

Discussion (4)

Collapse
cescquintero profile image
Francisco Quintero πŸ‡¨πŸ‡΄

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

Collapse
annarankin profile image
Anna Rankin

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

Collapse
michaeltharrington profile image
Michael Tharrington (he/him) • Edited

Meow that's a fitting GIF right there.

Collapse
ben profile image
Ben Halpern

Great post