-
-
Notifications
You must be signed in to change notification settings - Fork 91
Hooks
Hooks allow you to register commands and script to run when doing performs certain actions. You can register the following events:
-
post_config
-- Runs after the main configuration is read, before any local.doingrc
configurations -
post_local_config
-- Runs after local configurations are read and merged into the configuration object -
post_read
-- Runs after the contents of the doing file are parsed -
pre_entry_add
-- Runs when an entry is prepared but before it's added -
post_entry_added
-- Runs after an entry has been added to the index, allows modification -
post_entry_updated
-- Runs when an existing entry is modified -
post_entry_removed
-- Runs after an entry has been deleted -
pre_export
-- Runs when an export is ready but before output, allows modification -
pre_write
-- Runs prior to writing the index to a file, allows modification -
post_write
-- Runs after the contents of the doing file are changed. Does not run unless a modification is made
To register a hook, simply place a Ruby file in your plugins folder. This folder is located at ~/.config/doing/plugins
by default, but you can specify another location in your config using the plugin_path
key.
The ruby file should contain a call to register the hook. Pass a block to perform when the associated event is triggered. A single file can contain as many hooks as you like.
The register method takes an optional priority:
argument containing an integer. If multiple hooks are registered, they will be executed in order of priority, lowest to highest.
Doing::Hooks.register :post_read, priority: 20 do { |wwid| ... }
The following runs a shell script called after_doing.sh
whenever a change is made to the doing file (:post_write
event):
# frozen_string_literal: true
Doing::Hooks.register :post_write do |filename|
job1 = fork do
exec "/bin/bash /Users/ttscoff/scripts/after_doing.sh &> /dev/null"
end
Process.detach(job1)
Doing.logger.debug('Hooks:', res)
end
Note that when running external scripts from hooks, you should not do so using a method that will end the current process (such as
exec
). The preferred method of executign external scripts is to usefork
andProcess.detach
as shown in the:post_write
hook. This will execute the script in the background and immediately return control back to Doing (and thus free up your command line even if the external script is long-running).
Events that receive that same block arguments can be combined as array when registering. In a case like :post_entry_updated, which receives wwid, entry, old_entry
and post_entry_added, which receives wwid, entry
, you can include the extra block parameter and it will just be nil for the event that doesn't need it.
Doing::Hooks.register [:post_entry_updated, :post_entry_added] do |wwid, entry, old_entry|
# Actions, can test for old_entry.nil?
end
That's all there is to it. As an example, I use the :post_write
hook to execute a shell script which updates my iTerm status bar and my Touch Bar (BetterTouchTool widget) with my current task whenever the file changes. Any time I add or complete an entry, my status bars update.
The post_config hook receives the Doing::WWID object. This contains the @config hash and has access to all of the WWID methods. Changes made to the object within the block will carry through to the current doing operation.
If you modify the configuration variable, the changes will be incorporated into the current operation but will not be saved. If you want to save the modified configuration, use wwid.write_config to save it to a file. This will overwrite your current configuration with whatever you've done to the wwid.configuration object, so be careful. You can provide a filename argument to wwid.write to write to a different file than the primary config, e.g. (wwid.write_config(File.expand_path('~/configuration.yml'))
).
Doing::Hooks.register :post_config do |wwid|
wwid.config['new config key'] = 'My new value'
end
The post_local_config hook runs after local .doingrc
files are merged into the configuration. Keys from these files are not written out to any configurations by default, so performing any destructive file write operations in this hook is discouraged.
Doing::Hooks.register :post_config do |wwid|
wwid.config['new config key'] = 'My new value'
end
The post_read hook receives the current Doing::WWID object. This includes the @content object. wwid.content
acts like an Array of items. Each item has values for date(Date), title(String), section(String), and note(Note < Array). Additionally, there's a wwid.content.sections
attribute containing an array of section titles.
The items found in wwid.content have a variety of methods for testing whether they match search strings or tag filters (see the #tags?
and #search
methods), as well as for adding tags and modifying them. Modifications made to items by your hook will be passed on and saved when the file writes out at the end of a command. Not all commands write to disk, though; commands like show
and view
never save changes.
Doing::Hooks.register :post_read do |wwid|
Doing.logger.info('Hook:', wwid.content.keys)
end
Called before every item is added, and receives the WWID object and the new entry, which can be modified before it's added.
Doing::Hooks.register :post_entry_add do |wwid, entry|
if entry.tags?('hooked')
entry.tag('addhook')
end
end
Or make entry of a note optional when adding new items without one:
Hooks.register :pre_entry_add do |wwid, entry|
if entry.note.empty?
res = Doing::Prompt.yn('Want to add a note?', default_response: false)
if res
note = Doing::Prompt.enter_text('Enter note')
entry.note.add(note) unless note.empty?
end
end
end
Called after adding a new entry, receives the WWID object and a copy of the added entry.
Hooks.register :post_entry_added do |wwid, entry|
# Do something with the new entry, such as setting an environment variable
# that you can use in your prompt. Changes to the entry variable will not be
# saved.
end
Called after an existing entry is modified, receives the WWID object, the updated entry (which can be modified), and a clone of the entry prior to the update
Hooks.register :post_entry_updated do |wwid, entry, old_entry|
# entry (Item) can be modified and changes will be saved
end
Called after removing an entry, receives the WWID object and a copy of the removed entry
Doing::Hooks.register :post_entry_removed { |wwid, removed| ... }
Called before any output operation, receives the WWID object, the output format, and the array of entries (which can be modified, but modifications will only affect the output, not the doing file content)
Doing::Hooks.register :pre_export { |wwid, output_format, items| ... }
The pre_write hook receives the WWID object and the filename that is going to be written. Modifications made to the WWID.content object at this point will be written out to filename
(and thus permanently saved).
To see a diff of items being added and removed, use wwid.changes
, which outputs a hash containing { added: [Items], removed: [Items] }
.
Doing::Hooks.register :pre_write do |wwid, filename|
return unless filename == Doing.setting('doing_file')
wwid.content.each do |section, content|
content[:items].each do |i|
# If any item has @test AND @todo tags, add @hooktest
if i.tags?(['test', 'todo'], :and)
i.tag('hooktest')
end
end
end
end
The post_write hook receives the filename that was saved to.
If you want to see what changed, you can use WWID.new.get_diff(filename)
. Note that this does a line-by-line diff between the current Doing file and the most recent backup, so it can be slow and will introduce a couple of seconds wait after any command that updates the doing file. :get_diff
returns an Items object (which is an array of Items, each with date, title, note, and section, plus its own ). The more efficient way to get a list of changes is to use wwid.changes
in the :pre_write
hook.
Doing::Hooks.register :post_write do |filename|
job1 = fork do
exec "/bin/bash /Users/ttscoff/scripts/after_doing.sh &> /dev/null"
end
Process.detach(job1)
# This is what I use to update things like my
# BetterTouchTool widgets and my iTerm status bar.
end