abt-cli 0.0.16 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/abt/ari.rb +20 -0
- data/lib/abt/ari_list.rb +13 -0
- data/lib/abt/base_command.rb +63 -0
- data/lib/abt/cli.rb +59 -47
- data/lib/abt/cli/arguments_parser.rb +9 -24
- data/lib/abt/cli/global_commands/commands.rb +23 -0
- data/lib/abt/cli/global_commands/examples.rb +23 -0
- data/lib/abt/cli/global_commands/help.rb +23 -0
- data/lib/abt/cli/global_commands/readme.rb +23 -0
- data/lib/abt/cli/global_commands/share.rb +36 -0
- data/lib/abt/cli/global_commands/version.rb +23 -0
- data/lib/abt/cli/prompt.rb +5 -4
- data/lib/abt/docs.rb +32 -15
- data/lib/abt/docs/cli.rb +5 -5
- data/lib/abt/docs/markdown.rb +8 -7
- data/lib/abt/git_config.rb +20 -36
- data/lib/abt/providers/asana/base_command.rb +15 -35
- data/lib/abt/providers/asana/commands/add.rb +9 -7
- data/lib/abt/providers/asana/commands/branch_name.rb +9 -4
- data/lib/abt/providers/asana/commands/clear.rb +2 -0
- data/lib/abt/providers/asana/commands/current.rb +19 -34
- data/lib/abt/providers/asana/commands/finalize.rb +5 -9
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +9 -4
- data/lib/abt/providers/asana/commands/init.rb +6 -6
- data/lib/abt/providers/asana/commands/pick.rb +16 -11
- data/lib/abt/providers/asana/commands/projects.rb +1 -1
- data/lib/abt/providers/asana/commands/share.rb +5 -7
- data/lib/abt/providers/asana/commands/start.rb +14 -12
- data/lib/abt/providers/asana/commands/tasks.rb +4 -3
- data/lib/abt/providers/asana/configuration.rb +20 -26
- data/lib/abt/providers/asana/path.rb +36 -0
- data/lib/abt/providers/devops/api.rb +12 -0
- data/lib/abt/providers/devops/base_command.rb +15 -40
- data/lib/abt/providers/devops/commands/boards.rb +2 -2
- data/lib/abt/providers/devops/commands/branch_name.rb +7 -3
- data/lib/abt/providers/devops/commands/clear.rb +2 -0
- data/lib/abt/providers/devops/commands/current.rb +14 -38
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +9 -1
- data/lib/abt/providers/devops/commands/init.rb +15 -15
- data/lib/abt/providers/devops/commands/pick.rb +5 -12
- data/lib/abt/providers/devops/commands/share.rb +6 -5
- data/lib/abt/providers/devops/commands/work-items.rb +1 -1
- data/lib/abt/providers/devops/configuration.rb +17 -46
- data/lib/abt/providers/devops/path.rb +50 -0
- data/lib/abt/providers/git/commands/branch.rb +22 -16
- data/lib/abt/providers/harvest/base_command.rb +16 -34
- data/lib/abt/providers/harvest/commands/clear.rb +2 -0
- data/lib/abt/providers/harvest/commands/current.rb +24 -31
- data/lib/abt/providers/harvest/commands/init.rb +5 -6
- data/lib/abt/providers/harvest/commands/pick.rb +3 -4
- data/lib/abt/providers/harvest/commands/projects.rb +1 -1
- data/lib/abt/providers/harvest/commands/share.rb +5 -7
- data/lib/abt/providers/harvest/commands/start.rb +1 -1
- data/lib/abt/providers/harvest/commands/stop.rb +7 -7
- data/lib/abt/providers/harvest/commands/tasks.rb +4 -1
- data/lib/abt/providers/harvest/commands/track.rb +26 -19
- data/lib/abt/providers/harvest/configuration.rb +20 -29
- data/lib/abt/providers/harvest/path.rb +36 -0
- data/lib/abt/version.rb +1 -1
- metadata +14 -3
- data/lib/abt/cli/base_command.rb +0 -61
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
class Cli
|
5
|
+
module GlobalCommands
|
6
|
+
class Version < Abt::BaseCommand
|
7
|
+
def self.usage
|
8
|
+
'abt version'
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
'Print abt version'
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :cli
|
16
|
+
|
17
|
+
def perform
|
18
|
+
puts(Abt::VERSION)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/abt/cli/prompt.rb
CHANGED
@@ -69,10 +69,11 @@ module Abt
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def read_option_number(options_length, nil_option)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
str = '('
|
73
|
+
str += options_length > 1 ? "1-#{options_length}" : '1'
|
74
|
+
str += nil_option_string(nil_option)
|
75
|
+
str += '): '
|
76
|
+
output.print str
|
76
77
|
|
77
78
|
input = read_user_input
|
78
79
|
|
data/lib/abt/docs.rb
CHANGED
@@ -10,11 +10,11 @@ module Abt
|
|
10
10
|
def basic_examples
|
11
11
|
{
|
12
12
|
'Getting started:' => {
|
13
|
-
'abt init asana harvest' => 'Setup asana and harvest project git repo
|
14
|
-
'abt pick harvest' => 'Pick harvest
|
15
|
-
'abt pick asana | abt start harvest' => 'Pick asana task and start
|
13
|
+
'abt init asana harvest' => 'Setup asana and harvest project for local git repo',
|
14
|
+
'abt pick harvest' => 'Pick harvest task. This will likely stay the same throughout the project',
|
15
|
+
'abt pick asana | abt start harvest' => 'Pick asana task and start tracking time',
|
16
16
|
'abt stop harvest' => 'Stop time tracker',
|
17
|
-
'abt start asana harvest' => 'Continue working, e.g
|
17
|
+
'abt start asana harvest' => 'Continue working, e.g., after a break',
|
18
18
|
'abt finalize asana' => 'Finalize the selected asana task'
|
19
19
|
}
|
20
20
|
}
|
@@ -22,30 +22,47 @@ module Abt
|
|
22
22
|
|
23
23
|
def extended_examples
|
24
24
|
{
|
25
|
-
'Tracking meetings (without
|
25
|
+
'Tracking meetings (without switching current task setting):' => {
|
26
26
|
'abt pick asana -d | abt track harvest' => 'Track on asana meeting task',
|
27
27
|
'abt pick harvest -d | abt track harvest -c "Name of meeting"' => 'Track on separate harvest-task'
|
28
28
|
},
|
29
|
-
'
|
29
|
+
'Many commands output ARIs that can be piped into other commands:' => {
|
30
30
|
'abt tasks asana | grep -i <name of task>' => nil,
|
31
31
|
'abt tasks asana | grep -i <name of task> | abt start' => nil
|
32
32
|
},
|
33
|
-
'Sharing
|
34
|
-
'abt share asana harvest | tr "\n" " "' => 'Print current
|
35
|
-
'abt share asana harvest | tr "\n" " " | pbcopy' => 'Copy
|
36
|
-
'abt start <
|
33
|
+
'Sharing ARIs:' => {
|
34
|
+
'abt share asana harvest | tr "\n" " "' => 'Print current asana and harvest ARIs on a single line',
|
35
|
+
'abt share asana harvest | tr "\n" " " | pbcopy' => 'Copy ARIs to clipboard (mac only)',
|
36
|
+
'abt start <ARIs from coworker>' => 'Work on a task your coworker shared with you',
|
37
|
+
'abt current <ARIs from coworker> | abt start' => 'Set task as current, then start it'
|
37
38
|
},
|
38
39
|
'Flags:' => {
|
39
|
-
'abt start harvest -c "comment"' => 'Add command flags after
|
40
|
-
'abt start harvest -c "comment" -- asana' => 'Use -- to
|
41
|
-
'abt pick harvest | abt start -c "comment"' => 'Flags placed directly after a command applies to piped in
|
40
|
+
'abt start harvest -c "comment"' => 'Add command flags after ARIs',
|
41
|
+
'abt start harvest -c "comment" -- asana' => 'Use -- to end a list of flags, so that it can be followed by another ARI',
|
42
|
+
'abt pick harvest | abt start -c "comment"' => 'Flags placed directly after a command applies to the piped in ARI'
|
42
43
|
}
|
43
44
|
}
|
44
45
|
end
|
45
46
|
|
46
47
|
def providers
|
47
|
-
@providers ||=
|
48
|
-
|
48
|
+
@providers ||= begin
|
49
|
+
providers = {}
|
50
|
+
|
51
|
+
global_command_names = Abt::Cli.global_command_names
|
52
|
+
providers['Global'] = global_command_names.each_with_object({}) do |name, definition|
|
53
|
+
command_class = Abt::Cli.global_command_class(name)
|
54
|
+
full_name = "abt #{name}"
|
55
|
+
|
56
|
+
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
57
|
+
definition[full_name] = [command_class.usage, command_class.description]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Abt.schemes.sort.each_with_object(providers) do |scheme, definition|
|
62
|
+
definition[scheme] = command_definitions(scheme)
|
63
|
+
end
|
64
|
+
|
65
|
+
providers
|
49
66
|
end
|
50
67
|
end
|
51
68
|
|
data/lib/abt/docs/cli.rb
CHANGED
@@ -8,10 +8,10 @@ module Abt
|
|
8
8
|
<<~TXT
|
9
9
|
Usage: #{usage_line}
|
10
10
|
|
11
|
-
<command>
|
12
|
-
<
|
13
|
-
|
14
|
-
<options>
|
11
|
+
<command> Name of command to execute, e.g. start, finalize etc.
|
12
|
+
<ARI> A URI-like resource identifier with a scheme and an optional path
|
13
|
+
in the format: <scheme>[:<path>]. E.g., harvest:11111111/22222222
|
14
|
+
<options> Optional flags for the command and ARI
|
15
15
|
|
16
16
|
#{formatted_examples(Docs.basic_examples)}
|
17
17
|
|
@@ -45,7 +45,7 @@ module Abt
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def usage_line
|
48
|
-
'abt <command> [<
|
48
|
+
'abt <command> [<ARI>] [<options> --] [<ARI>] ...'
|
49
49
|
end
|
50
50
|
|
51
51
|
def formatted_examples(example_groups)
|
data/lib/abt/docs/markdown.rb
CHANGED
@@ -15,27 +15,28 @@ module Abt
|
|
15
15
|
|
16
16
|
## How does abt work?
|
17
17
|
|
18
|
-
Abt
|
18
|
+
Abt is a hybrid of having small scripts each doing one thing:
|
19
19
|
- `start-asana --project-gid xxxx --task-gid yyyy`
|
20
20
|
- `start-harvest --project-id aaaa --task-id bbbb`
|
21
21
|
|
22
|
-
And having a single highly advanced script that does everything:
|
22
|
+
And having a single highly advanced script that does everything with a single command:
|
23
23
|
- `start xxxx/yyyy aaaa/bbbb`
|
24
24
|
|
25
|
-
Abt looks like one
|
25
|
+
Abt looks like one command, but works like a bunch of light scripts:
|
26
26
|
- `abt start asana:xxxx/yyyy harvest:aaaa/bbbb`
|
27
27
|
|
28
28
|
## Usage
|
29
|
-
`abt <command> [<
|
29
|
+
`abt <command> [<ARI>] [<options> --] [<ARI>] ...`
|
30
30
|
|
31
31
|
Definitions:
|
32
32
|
- `<command>`: Name of command to execute, e.g. `start`, `finalize` etc.
|
33
|
-
- `<
|
34
|
-
- `<options>`: Optional flags for the command and
|
33
|
+
- `<ARI>`: A URI-like resource identifier with a scheme and an optional path in the format: `<scheme>[:<path>]`. E.g., `harvest:11111111/22222222`
|
34
|
+
- `<options>`: Optional flags for the command and ARI
|
35
35
|
|
36
36
|
#{example_commands}
|
37
37
|
|
38
|
-
##
|
38
|
+
## Commands:
|
39
|
+
|
39
40
|
Some commands have `[options]`. Run such a command with `--help` flag to view supported flags, e.g: `abt track harvest -h`
|
40
41
|
|
41
42
|
#{provider_commands}
|
data/lib/abt/git_config.rb
CHANGED
@@ -6,21 +6,7 @@ module Abt
|
|
6
6
|
|
7
7
|
class UnsafeNamespaceError < StandardError; end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
def self.local_available?
|
12
|
-
return @local_available if instance_variables.include?(:@local_available)
|
13
|
-
|
14
|
-
@local_available = begin
|
15
|
-
success = false
|
16
|
-
Open3.popen3(LOCAL_CONFIG_AVAILABLE_CHECK_COMMAND) do |_i, _o, _e, thread|
|
17
|
-
success = thread.value.success?
|
18
|
-
end
|
19
|
-
success
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(namespace: '', scope: 'local')
|
9
|
+
def initialize(scope = 'local', namespace = '')
|
24
10
|
@namespace = namespace
|
25
11
|
|
26
12
|
unless %w[local global].include? scope
|
@@ -30,6 +16,20 @@ module Abt
|
|
30
16
|
@scope = scope
|
31
17
|
end
|
32
18
|
|
19
|
+
def available?
|
20
|
+
unless instance_variables.include?(:available)
|
21
|
+
@available = begin
|
22
|
+
success = false
|
23
|
+
Open3.popen3(availability_check_call) do |_i, _o, _e, thread|
|
24
|
+
success = thread.value.success?
|
25
|
+
end
|
26
|
+
success
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@available
|
31
|
+
end
|
32
|
+
|
33
33
|
def [](key)
|
34
34
|
get(key)
|
35
35
|
end
|
@@ -49,26 +49,6 @@ module Abt
|
|
49
49
|
`git config --#{scope} --get-regexp --name-only ^#{namespace}`.lines.map(&:strip)
|
50
50
|
end
|
51
51
|
|
52
|
-
def local
|
53
|
-
@local ||= begin
|
54
|
-
if scope == 'local'
|
55
|
-
self
|
56
|
-
else
|
57
|
-
self.class.new(namespace: namespace, scope: 'local')
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def global
|
63
|
-
@global ||= begin
|
64
|
-
if scope == 'global'
|
65
|
-
self
|
66
|
-
else
|
67
|
-
self.class.new(namespace: namespace, scope: 'global')
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
52
|
def clear(output: nil)
|
73
53
|
raise UnsafeNamespaceError, 'Keys can only be cleared within a namespace' if namespace.empty?
|
74
54
|
|
@@ -80,8 +60,12 @@ module Abt
|
|
80
60
|
|
81
61
|
private
|
82
62
|
|
63
|
+
def availability_check_call
|
64
|
+
"git config --#{scope} -l"
|
65
|
+
end
|
66
|
+
|
83
67
|
def ensure_scope_available!
|
84
|
-
return if
|
68
|
+
return if available?
|
85
69
|
|
86
70
|
raise StandardError, 'Local configuration is not available outside a git repository'
|
87
71
|
end
|
@@ -3,63 +3,43 @@
|
|
3
3
|
module Abt
|
4
4
|
module Providers
|
5
5
|
module Asana
|
6
|
-
class BaseCommand < Abt::
|
7
|
-
|
6
|
+
class BaseCommand < Abt::BaseCommand
|
7
|
+
extend Forwardable
|
8
8
|
|
9
|
-
|
9
|
+
attr_reader :path, :config
|
10
|
+
|
11
|
+
def_delegators(:@path, :project_gid, :task_gid)
|
12
|
+
|
13
|
+
def initialize(ari:, cli:)
|
10
14
|
super
|
11
15
|
|
12
16
|
@config = Configuration.new(cli: cli)
|
13
17
|
|
14
|
-
|
15
|
-
use_current_path
|
16
|
-
else
|
17
|
-
use_path(path)
|
18
|
-
end
|
18
|
+
@path = ari.path ? Path.new(ari.path) : config.path
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def require_project!
|
24
|
-
|
24
|
+
abort 'No current/specified project. Did you initialize Asana?' if project_gid.nil?
|
25
25
|
end
|
26
26
|
|
27
27
|
def require_task!
|
28
28
|
if project_gid.nil?
|
29
|
-
|
29
|
+
abort 'No current/specified project. Did you initialize Asana and pick a task?'
|
30
30
|
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def same_args_as_config?
|
35
|
-
project_gid == config.project_gid && task_gid == config.task_gid
|
31
|
+
abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
|
36
32
|
end
|
37
33
|
|
38
34
|
def print_project(project)
|
39
|
-
cli.
|
40
|
-
|
35
|
+
cli.print_ari('asana', project['gid'], project['name'])
|
36
|
+
warn project['permalink_url'] if project.key?('permalink_url') && cli.output.isatty
|
41
37
|
end
|
42
38
|
|
43
39
|
def print_task(project, task)
|
44
40
|
project = { 'gid' => project } if project.is_a?(String)
|
45
|
-
cli.
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def use_current_path
|
50
|
-
@project_gid = config.project_gid
|
51
|
-
@task_gid = config.task_gid
|
52
|
-
end
|
53
|
-
|
54
|
-
def use_path(path)
|
55
|
-
args = path.to_s.split('/')
|
56
|
-
@project_gid = args[0].to_s
|
57
|
-
@project_gid = nil if project_gid.empty?
|
58
|
-
|
59
|
-
return if project_gid.nil?
|
60
|
-
|
61
|
-
@task_gid = args[1].to_s
|
62
|
-
@task_gid = nil if @task_gid.empty?
|
41
|
+
cli.print_ari('asana', "#{project['gid']}/#{task['gid']}", task['name'])
|
42
|
+
warn task['permalink_url'] if task.key?('permalink_url') && cli.output.isatty
|
63
43
|
end
|
64
44
|
|
65
45
|
def api
|
@@ -17,9 +17,14 @@ module Abt
|
|
17
17
|
require_project!
|
18
18
|
|
19
19
|
task
|
20
|
-
|
20
|
+
warn 'Task created'
|
21
|
+
|
22
|
+
if section
|
23
|
+
move_task
|
24
|
+
warn "Moved to section: #{section['name']}"
|
25
|
+
end
|
21
26
|
|
22
|
-
|
27
|
+
print_task(project, task)
|
23
28
|
end
|
24
29
|
|
25
30
|
private
|
@@ -33,7 +38,6 @@ module Abt
|
|
33
38
|
projects: [project_gid]
|
34
39
|
}
|
35
40
|
}
|
36
|
-
cli.warn 'Creating task'
|
37
41
|
api.post('tasks', Oj.dump(body, mode: :json))
|
38
42
|
end
|
39
43
|
end
|
@@ -53,7 +57,7 @@ module Abt
|
|
53
57
|
end
|
54
58
|
|
55
59
|
def project
|
56
|
-
@project ||= api.get("projects/#{project_gid}")
|
60
|
+
@project ||= api.get("projects/#{project_gid}", opt_fields: 'name')
|
57
61
|
end
|
58
62
|
|
59
63
|
def section
|
@@ -62,10 +66,8 @@ module Abt
|
|
62
66
|
|
63
67
|
def sections
|
64
68
|
@sections ||= begin
|
65
|
-
|
69
|
+
warn 'Fetching sections...'
|
66
70
|
api.get_paged("projects/#{project_gid}/sections", opt_fields: 'name')
|
67
|
-
rescue Abt::HttpError::HttpError
|
68
|
-
[]
|
69
71
|
end
|
70
72
|
end
|
71
73
|
end
|
@@ -17,7 +17,7 @@ module Abt
|
|
17
17
|
require_task!
|
18
18
|
ensure_current_is_valid!
|
19
19
|
|
20
|
-
|
20
|
+
puts name
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -27,15 +27,20 @@ module Abt
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def ensure_current_is_valid!
|
30
|
-
|
30
|
+
abort "Invalid task gid: #{task_gid}" if task.nil?
|
31
31
|
|
32
32
|
return if task['memberships'].any? { |m| m.dig('project', 'gid') == project_gid }
|
33
33
|
|
34
|
-
|
34
|
+
abort "Invalid or unmatching project gid: #{project_gid}"
|
35
35
|
end
|
36
36
|
|
37
37
|
def task
|
38
|
-
@task ||=
|
38
|
+
@task ||= begin
|
39
|
+
warn 'Fetching task...'
|
40
|
+
api.get("tasks/#{task_gid}", opt_fields: 'name,memberships.project')
|
41
|
+
rescue Abt::HttpError::NotFoundError
|
42
|
+
nil
|
43
|
+
end
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
@@ -14,60 +14,45 @@ module Abt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def perform
|
17
|
+
abort 'Must be run inside a git repository' unless config.local_available?
|
18
|
+
|
17
19
|
require_project!
|
20
|
+
ensure_valid_configuration!
|
18
21
|
|
19
|
-
if
|
20
|
-
|
21
|
-
|
22
|
-
cli.warn 'Updating configuration'
|
23
|
-
update_configuration
|
22
|
+
if path != config.path
|
23
|
+
config.path = path
|
24
|
+
warn 'Configuration updated'
|
24
25
|
end
|
25
|
-
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
def show_current_configuration
|
30
|
-
if task_gid.nil?
|
31
|
-
print_project(project)
|
32
|
-
else
|
33
|
-
print_task(project, task)
|
34
|
-
end
|
27
|
+
print_configuration
|
35
28
|
end
|
36
29
|
|
37
|
-
|
38
|
-
ensure_project_is_valid!
|
39
|
-
config.project_gid = project_gid
|
40
|
-
|
41
|
-
if task_gid.nil?
|
42
|
-
print_project(project)
|
43
|
-
config.task_gid = nil
|
44
|
-
else
|
45
|
-
ensure_task_is_valid!
|
46
|
-
config.task_gid = task_gid
|
47
|
-
|
48
|
-
print_task(project, task)
|
49
|
-
end
|
50
|
-
end
|
30
|
+
private
|
51
31
|
|
52
|
-
def
|
53
|
-
|
32
|
+
def print_configuration
|
33
|
+
task_gid.nil? ? print_project(project) : print_task(project, task)
|
54
34
|
end
|
55
35
|
|
56
|
-
def
|
57
|
-
|
36
|
+
def ensure_valid_configuration!
|
37
|
+
abort "Invalid project: #{project_gid}" if project.nil?
|
38
|
+
abort "Invalid task: #{task_gid}" if task_gid && task.nil?
|
58
39
|
end
|
59
40
|
|
60
41
|
def project
|
61
42
|
@project ||= begin
|
62
|
-
|
43
|
+
warn 'Fetching project...'
|
63
44
|
api.get("projects/#{project_gid}", opt_fields: 'name,permalink_url')
|
45
|
+
rescue Abt::HttpError::NotFoundError
|
46
|
+
nil
|
64
47
|
end
|
65
48
|
end
|
66
49
|
|
67
50
|
def task
|
68
51
|
@task ||= begin
|
69
|
-
|
52
|
+
warn 'Fetching task...'
|
70
53
|
api.get("tasks/#{task_gid}", opt_fields: 'name,permalink_url')
|
54
|
+
rescue Abt::HttpError::NotFoundError
|
55
|
+
nil
|
71
56
|
end
|
72
57
|
end
|
73
58
|
end
|