abt-cli 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/bin/abt +3 -3
  3. data/lib/abt.rb +6 -6
  4. data/lib/abt/ari.rb +1 -1
  5. data/lib/abt/ari_list.rb +1 -1
  6. data/lib/abt/base_command.rb +7 -7
  7. data/lib/abt/cli.rb +27 -40
  8. data/lib/abt/cli/arguments_parser.rb +5 -9
  9. data/lib/abt/cli/global_commands.rb +23 -0
  10. data/lib/abt/cli/global_commands/commands.rb +2 -2
  11. data/lib/abt/cli/global_commands/examples.rb +2 -2
  12. data/lib/abt/cli/global_commands/help.rb +2 -2
  13. data/lib/abt/cli/global_commands/readme.rb +2 -2
  14. data/lib/abt/cli/global_commands/share.rb +6 -6
  15. data/lib/abt/cli/global_commands/version.rb +2 -2
  16. data/lib/abt/cli/prompt.rb +51 -20
  17. data/lib/abt/docs.rb +39 -33
  18. data/lib/abt/docs/cli.rb +3 -3
  19. data/lib/abt/docs/markdown.rb +5 -5
  20. data/lib/abt/git_config.rb +4 -6
  21. data/lib/abt/providers/asana/api.rb +9 -9
  22. data/lib/abt/providers/asana/base_command.rb +8 -10
  23. data/lib/abt/providers/asana/commands/add.rb +13 -12
  24. data/lib/abt/providers/asana/commands/branch_name.rb +8 -8
  25. data/lib/abt/providers/asana/commands/clear.rb +7 -8
  26. data/lib/abt/providers/asana/commands/current.rb +14 -14
  27. data/lib/abt/providers/asana/commands/finalize.rb +11 -12
  28. data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +11 -11
  29. data/lib/abt/providers/asana/commands/init.rb +8 -41
  30. data/lib/abt/providers/asana/commands/pick.rb +17 -17
  31. data/lib/abt/providers/asana/commands/projects.rb +5 -5
  32. data/lib/abt/providers/asana/commands/share.rb +5 -5
  33. data/lib/abt/providers/asana/commands/start.rb +21 -20
  34. data/lib/abt/providers/asana/commands/tasks.rb +6 -6
  35. data/lib/abt/providers/asana/configuration.rb +25 -25
  36. data/lib/abt/providers/asana/path.rb +5 -5
  37. data/lib/abt/providers/devops/api.rb +12 -12
  38. data/lib/abt/providers/devops/base_command.rb +10 -10
  39. data/lib/abt/providers/devops/commands/boards.rb +5 -7
  40. data/lib/abt/providers/devops/commands/branch_name.rb +9 -9
  41. data/lib/abt/providers/devops/commands/clear.rb +7 -8
  42. data/lib/abt/providers/devops/commands/current.rb +17 -17
  43. data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +13 -13
  44. data/lib/abt/providers/devops/commands/init.rb +17 -13
  45. data/lib/abt/providers/devops/commands/pick.rb +11 -11
  46. data/lib/abt/providers/devops/commands/share.rb +5 -5
  47. data/lib/abt/providers/devops/commands/{work-items.rb → work_items.rb} +3 -3
  48. data/lib/abt/providers/devops/configuration.rb +19 -15
  49. data/lib/abt/providers/devops/path.rb +5 -4
  50. data/lib/abt/providers/git/commands/branch.rb +17 -19
  51. data/lib/abt/providers/harvest/api.rb +8 -8
  52. data/lib/abt/providers/harvest/base_command.rb +6 -8
  53. data/lib/abt/providers/harvest/commands/clear.rb +7 -8
  54. data/lib/abt/providers/harvest/commands/current.rb +13 -13
  55. data/lib/abt/providers/harvest/commands/init.rb +10 -38
  56. data/lib/abt/providers/harvest/commands/pick.rb +11 -11
  57. data/lib/abt/providers/harvest/commands/projects.rb +5 -5
  58. data/lib/abt/providers/harvest/commands/share.rb +5 -5
  59. data/lib/abt/providers/harvest/commands/start.rb +5 -3
  60. data/lib/abt/providers/harvest/commands/stop.rb +12 -12
  61. data/lib/abt/providers/harvest/commands/tasks.rb +7 -7
  62. data/lib/abt/providers/harvest/commands/track.rb +21 -20
  63. data/lib/abt/providers/harvest/configuration.rb +18 -18
  64. data/lib/abt/providers/harvest/path.rb +5 -5
  65. data/lib/abt/version.rb +1 -1
  66. metadata +6 -5
data/lib/abt/docs.rb CHANGED
@@ -9,37 +9,39 @@ module Abt
9
9
  class << self
10
10
  def basic_examples
11
11
  {
12
- 'Getting started:' => {
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
- 'abt stop harvest' => 'Stop time tracker',
17
- 'abt start asana harvest' => 'Continue working, e.g., after a break',
18
- 'abt finalize asana' => 'Finalize the selected asana task'
12
+ "Getting started:" => {
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
+ "abt stop harvest" => "Stop time tracker",
17
+ "abt start asana harvest" => "Continue working, e.g., after a break",
18
+ "abt finalize asana" => "Finalize the selected asana task"
19
19
  }
20
20
  }
21
21
  end
22
22
 
23
- def extended_examples
23
+ def extended_examples # rubocop:disable Metrics/MethodLength
24
24
  {
25
- 'Tracking meetings (without switching current task setting):' => {
26
- 'abt pick asana -d | abt track harvest' => 'Track on asana meeting task',
27
- 'abt pick harvest -d | abt track harvest -c "Name of meeting"' => 'Track on separate harvest-task'
25
+ "Tracking meetings (without switching current task setting):" => {
26
+ "abt pick asana -d | abt track harvest" => "Track on asana meeting task",
27
+ 'abt pick harvest -d | abt track harvest -c "Name of meeting"' => "Track on separate harvest-task"
28
28
  },
29
- 'Many commands output ARIs that can be piped into other commands:' => {
30
- 'abt tasks asana | grep -i <name of task>' => nil,
31
- 'abt tasks asana | grep -i <name of task> | abt start' => nil
29
+ "Many commands output ARIs that can be piped into other commands:" => {
30
+ "abt tasks asana | grep -i <name of task>" => nil,
31
+ "abt tasks asana | grep -i <name of task> | abt start" => nil
32
32
  },
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'
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"
38
38
  },
39
- 'Flags:' => {
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'
39
+ "Flags:" => {
40
+ 'abt start harvest -c "comment"' => "Add command flags after ARIs",
41
+ 'abt start harvest -c "comment" -- asana' =>
42
+ "Use -- to end a list of flags, so that it can be followed by another ARI",
43
+ 'abt pick harvest | abt start -c "comment"' =>
44
+ "Flags placed directly after a command applies to the piped in ARI"
43
45
  }
44
46
  }
45
47
  end
@@ -48,15 +50,7 @@ module Abt
48
50
  @providers ||= begin
49
51
  providers = {}
50
52
 
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
53
+ providers["Global"] = global_command_definitions
60
54
 
61
55
  Abt.schemes.sort.each_with_object(providers) do |scheme, definition|
62
56
  definition[scheme] = command_definitions(scheme)
@@ -68,6 +62,18 @@ module Abt
68
62
 
69
63
  private
70
64
 
65
+ def global_command_definitions
66
+ global_command_names = Abt::Cli::GlobalCommands.command_names
67
+ global_command_names.each_with_object({}) do |name, definition|
68
+ command_class = Abt::Cli::GlobalCommands.command_class(name)
69
+ full_name = "abt #{name}"
70
+
71
+ if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
72
+ definition[full_name] = [command_class.usage.strip, command_class.description.strip]
73
+ end
74
+ end
75
+ end
76
+
71
77
  def command_definitions(scheme)
72
78
  provider = Abt.scheme_provider(scheme)
73
79
  provider.command_names.each_with_object({}) do |name, definition|
@@ -75,7 +81,7 @@ module Abt
75
81
  full_name = "abt #{name} #{scheme}"
76
82
 
77
83
  if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
78
- definition[full_name] = [command_class.usage, command_class.description]
84
+ definition[full_name] = [command_class.usage.strip, command_class.description.strip]
79
85
  end
80
86
  end
81
87
  end
data/lib/abt/docs/cli.rb CHANGED
@@ -45,14 +45,14 @@ module Abt
45
45
  private
46
46
 
47
47
  def usage_line
48
- 'abt <command> [<ARI>] [<options> --] [<ARI>] ...'
48
+ "abt <command> [<ARI>] [<options> --] [<ARI>] ..."
49
49
  end
50
50
 
51
51
  def formatted_examples(example_groups)
52
52
  lines = []
53
53
 
54
54
  example_groups.each_with_index do |(title, examples), index|
55
- lines << '' unless index.zero?
55
+ lines << "" unless index.zero?
56
56
  lines << title
57
57
 
58
58
  max_length = examples.keys.map(&:length).max
@@ -68,7 +68,7 @@ module Abt
68
68
  lines = []
69
69
 
70
70
  Docs.providers.each_with_index do |(scheme, commands_definition), index|
71
- lines << '' unless index.zero?
71
+ lines << "" unless index.zero?
72
72
  lines << "#{inflector.humanize(scheme)}:"
73
73
 
74
74
  max_length = commands_definition.keys.map(&:length).max
@@ -52,11 +52,11 @@ module Abt
52
52
 
53
53
  examples = Docs.basic_examples.merge(Docs.extended_examples)
54
54
  examples.each_with_index do |(title, commands), index|
55
- lines << '' unless index.zero?
55
+ lines << "" unless index.zero?
56
56
  lines << title
57
57
 
58
58
  commands.each do |(command, description)|
59
- formatted_description = description.nil? ? '' : ": #{description}"
59
+ formatted_description = description.nil? ? "" : ": #{description}"
60
60
  lines << "- `#{command}`#{formatted_description}"
61
61
  end
62
62
  end
@@ -68,10 +68,10 @@ module Abt
68
68
  lines = []
69
69
 
70
70
  Docs.providers.each_with_index do |(scheme, commands), index|
71
- lines << '' unless index.zero?
71
+ lines << "" unless index.zero?
72
72
  lines << "### #{inflector.humanize(scheme)}"
73
- lines << '| Command | Description |'
74
- lines << '| :------ | :---------- |'
73
+ lines << "| Command | Description |"
74
+ lines << "| :------ | :---------- |"
75
75
 
76
76
  max_length = commands.values.map(&:first).map(&:length).max
77
77
 
@@ -6,12 +6,10 @@ module Abt
6
6
 
7
7
  class UnsafeNamespaceError < StandardError; end
8
8
 
9
- def initialize(scope = 'local', namespace = '')
9
+ def initialize(scope = "local", namespace = "")
10
10
  @namespace = namespace
11
11
 
12
- unless %w[local global].include? scope
13
- raise ArgumentError, 'scope must be "local" or "global"'
14
- end
12
+ raise ArgumentError, 'scope must be "local" or "global"' unless %w[local global].include?(scope)
15
13
 
16
14
  @scope = scope
17
15
  end
@@ -50,7 +48,7 @@ module Abt
50
48
  end
51
49
 
52
50
  def clear(output: nil)
53
- raise UnsafeNamespaceError, 'Keys can only be cleared within a namespace' if namespace.empty?
51
+ raise UnsafeNamespaceError, "Keys can only be cleared within a namespace" if namespace.empty?
54
52
 
55
53
  keys.each do |key|
56
54
  output&.puts "Clearing #{scope}: #{key_with_namespace(key)}"
@@ -67,7 +65,7 @@ module Abt
67
65
  def ensure_scope_available!
68
66
  return if available?
69
67
 
70
- raise StandardError, 'Local configuration is not available outside a git repository'
68
+ raise StandardError, "Local configuration is not available outside a git repository"
71
69
  end
72
70
 
73
71
  def key_with_namespace(key)
@@ -4,8 +4,8 @@ module Abt
4
4
  module Providers
5
5
  module Asana
6
6
  class Api
7
- API_ENDPOINT = 'https://app.asana.com/api/1.0'
8
- VERBS = %i[get post put].freeze
7
+ API_ENDPOINT = "https://app.asana.com/api/1.0"
8
+ VERBS = [:get, :post, :put].freeze
9
9
 
10
10
  attr_reader :access_token
11
11
 
@@ -15,7 +15,7 @@ module Abt
15
15
 
16
16
  VERBS.each do |verb|
17
17
  define_method(verb) do |*args|
18
- request(verb, *args)['data']
18
+ request(verb, *args)["data"]
19
19
  end
20
20
  end
21
21
 
@@ -24,10 +24,10 @@ module Abt
24
24
 
25
25
  loop do
26
26
  result = request(:get, path, query.merge(limit: 100))
27
- records += result['data']
28
- break if result['next_page'].nil?
27
+ records += result["data"]
28
+ break if result["next_page"].nil?
29
29
 
30
- path = result['next_page']['path'][1..-1]
30
+ path = result["next_page"]["path"][1..-1]
31
31
  end
32
32
 
33
33
  records
@@ -40,15 +40,15 @@ module Abt
40
40
  Oj.load(response.body)
41
41
  else
42
42
  error_class = Abt::HttpError.error_class_for_status(response.status)
43
- encoded_response_body = response.body.force_encoding('utf-8')
43
+ encoded_response_body = response.body.force_encoding("utf-8")
44
44
  raise error_class, "Code: #{response.status}, body: #{encoded_response_body}"
45
45
  end
46
46
  end
47
47
 
48
48
  def connection
49
49
  @connection ||= Faraday.new(API_ENDPOINT) do |connection|
50
- connection.headers['Authorization'] = "Bearer #{access_token}"
51
- connection.headers['Content-Type'] = 'application/json'
50
+ connection.headers["Authorization"] = "Bearer #{access_token}"
51
+ connection.headers["Content-Type"] = "application/json"
52
52
  end
53
53
  end
54
54
  end
@@ -21,25 +21,23 @@ module Abt
21
21
  private
22
22
 
23
23
  def require_project!
24
- abort 'No current/specified project. Did you initialize Asana?' if project_gid.nil?
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
- if project_gid.nil?
29
- abort 'No current/specified project. Did you initialize Asana and pick a task?'
30
- end
31
- abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
28
+ abort("No current/specified project. Did you initialize Asana and pick a task?") if project_gid.nil?
29
+ abort("No current/specified task. Did you pick an Asana task?") if task_gid.nil?
32
30
  end
33
31
 
34
32
  def print_project(project)
35
- cli.print_ari('asana', project['gid'], project['name'])
36
- warn project['permalink_url'] if project.key?('permalink_url') && cli.output.isatty
33
+ cli.print_ari("asana", project["gid"], project["name"])
34
+ warn(project["permalink_url"]) if project.key?("permalink_url") && cli.output.isatty
37
35
  end
38
36
 
39
37
  def print_task(project, task)
40
- project = { 'gid' => project } if project.is_a?(String)
41
- cli.print_ari('asana', "#{project['gid']}/#{task['gid']}", task['name'])
42
- warn task['permalink_url'] if task.key?('permalink_url') && cli.output.isatty
38
+ project = { "gid" => project } if project.is_a?(String)
39
+ cli.print_ari("asana", "#{project['gid']}/#{task['gid']}", task["name"])
40
+ warn(task["permalink_url"]) if task.key?("permalink_url") && cli.output.isatty
43
41
  end
44
42
 
45
43
  def api
@@ -6,22 +6,22 @@ module Abt
6
6
  module Commands
7
7
  class Add < BaseCommand
8
8
  def self.usage
9
- 'abt add asana[:<project-gid>]'
9
+ "abt add asana[:<project-gid>]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Create a new task for the current/specified Asana project'
13
+ "Create a new task for the current/specified Asana project"
14
14
  end
15
15
 
16
16
  def perform
17
17
  require_project!
18
18
 
19
19
  task
20
- warn 'Task created'
20
+ warn("Task created")
21
21
 
22
22
  if section
23
23
  move_task
24
- warn "Moved to section: #{section['name']}"
24
+ warn("Moved to section: #{section['name']}")
25
25
  end
26
26
 
27
27
  print_task(project, task)
@@ -38,36 +38,37 @@ module Abt
38
38
  projects: [project_gid]
39
39
  }
40
40
  }
41
- api.post('tasks', Oj.dump(body, mode: :json))
41
+ api.post("tasks", Oj.dump(body, mode: :json))
42
42
  end
43
43
  end
44
44
 
45
45
  def move_task
46
- body = { data: { task: task['gid'] } }
46
+ body = { data: { task: task["gid"] } }
47
47
  body_json = Oj.dump(body, mode: :json)
48
48
  api.post("sections/#{section['gid']}/addTask", body_json)
49
49
  end
50
50
 
51
51
  def name
52
- @name ||= cli.prompt.text 'Enter task description'
52
+ @name ||= cli.prompt.text("Enter task description")
53
53
  end
54
54
 
55
55
  def notes
56
- @notes ||= cli.prompt.text 'Enter task notes'
56
+ @notes ||= cli.prompt.text("Enter task notes")
57
57
  end
58
58
 
59
59
  def project
60
- @project ||= api.get("projects/#{project_gid}", opt_fields: 'name')
60
+ @project ||= api.get("projects/#{project_gid}", opt_fields: "name")
61
61
  end
62
62
 
63
63
  def section
64
- @section ||= cli.prompt.choice 'Add to section?', sections, ['q', 'Don\'t add to section']
64
+ @section ||= cli.prompt.choice("Add to section?", sections,
65
+ nil_option: ["q", "Don't add to section"])
65
66
  end
66
67
 
67
68
  def sections
68
69
  @sections ||= begin
69
- warn 'Fetching sections...'
70
- api.get_paged("projects/#{project_gid}/sections", opt_fields: 'name')
70
+ warn("Fetching sections...")
71
+ api.get_paged("projects/#{project_gid}/sections", opt_fields: "name")
71
72
  end
72
73
  end
73
74
  end
@@ -6,11 +6,11 @@ module Abt
6
6
  module Commands
7
7
  class BranchName < BaseCommand
8
8
  def self.usage
9
- 'abt branch-name asana[:<project-gid>/<task-gid>]'
9
+ "abt branch-name asana[:<project-gid>/<task-gid>]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Suggest a git branch name for the current/specified task.'
13
+ "Suggest a git branch name for the current/specified task."
14
14
  end
15
15
 
16
16
  def perform
@@ -23,21 +23,21 @@ module Abt
23
23
  private
24
24
 
25
25
  def name
26
- task['name'].downcase.gsub(/[^\w]+/, '-')
26
+ task["name"].downcase.gsub(/[^\w]+/, "-").gsub(/(^-|-$)/, "")
27
27
  end
28
28
 
29
29
  def ensure_current_is_valid!
30
- abort "Invalid task gid: #{task_gid}" if task.nil?
30
+ abort("Invalid task gid: #{task_gid}") if task.nil?
31
31
 
32
- return if task['memberships'].any? { |m| m.dig('project', 'gid') == project_gid }
32
+ return if task["memberships"].any? { |m| m.dig("project", "gid") == project_gid }
33
33
 
34
- abort "Invalid or unmatching project gid: #{project_gid}"
34
+ abort("Invalid or unmatching project gid: #{project_gid}")
35
35
  end
36
36
 
37
37
  def task
38
38
  @task ||= begin
39
- warn 'Fetching task...'
40
- api.get("tasks/#{task_gid}", opt_fields: 'name,memberships.project')
39
+ warn("Fetching task...")
40
+ api.get("tasks/#{task_gid}", opt_fields: "name,memberships.project")
41
41
  rescue Abt::HttpError::NotFoundError
42
42
  nil
43
43
  end
@@ -6,29 +6,28 @@ module Abt
6
6
  module Commands
7
7
  class Clear < BaseCommand
8
8
  def self.usage
9
- 'abt clear asana'
9
+ "abt clear asana"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Clear asana configuration'
13
+ "Clear asana configuration"
14
14
  end
15
15
 
16
16
  def self.flags
17
17
  [
18
- ['-g', '--global', 'Clear global instead of local asana configuration (credentials etc.)'],
19
- ['-a', '--all', 'Clear all asana configuration']
18
+ ["-g", "--global",
19
+ "Clear global instead of local asana configuration (credentials etc.)"],
20
+ ["-a", "--all", "Clear all asana configuration"]
20
21
  ]
21
22
  end
22
23
 
23
24
  def perform
24
- if flags[:global] && flags[:all]
25
- abort('Flags --global and --all cannot be used at the same time')
26
- end
25
+ abort("Flags --global and --all cannot be used at the same time") if flags[:global] && flags[:all]
27
26
 
28
27
  config.clear_local unless flags[:global]
29
28
  config.clear_global if flags[:global] || flags[:all]
30
29
 
31
- warn 'Configuration cleared'
30
+ warn("Configuration cleared")
32
31
  end
33
32
  end
34
33
  end
@@ -6,22 +6,22 @@ module Abt
6
6
  module Commands
7
7
  class Current < BaseCommand
8
8
  def self.usage
9
- 'abt current asana[:<project-gid>[/<task-gid>]]'
9
+ "abt current asana[:<project-gid>[/<task-gid>]]"
10
10
  end
11
11
 
12
12
  def self.description
13
- 'Get or set project and or task for current git repository'
13
+ "Get or set project and or task for current git repository"
14
14
  end
15
15
 
16
16
  def perform
17
- abort 'Must be run inside a git repository' unless config.local_available?
17
+ abort("Must be run inside a git repository") unless config.local_available?
18
18
 
19
19
  require_project!
20
20
  ensure_valid_configuration!
21
21
 
22
22
  if path != config.path
23
23
  config.path = path
24
- warn 'Configuration updated'
24
+ warn("Configuration updated")
25
25
  end
26
26
 
27
27
  print_configuration
@@ -34,25 +34,25 @@ module Abt
34
34
  end
35
35
 
36
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?
37
+ abort("Invalid project: #{project_gid}") if project.nil?
38
+ abort("Invalid task: #{task_gid}") if task_gid && task.nil?
39
39
  end
40
40
 
41
41
  def project
42
42
  @project ||= begin
43
- warn 'Fetching project...'
44
- api.get("projects/#{project_gid}", opt_fields: 'name,permalink_url')
45
- rescue Abt::HttpError::NotFoundError
46
- nil
43
+ warn("Fetching project...")
44
+ api.get("projects/#{project_gid}", opt_fields: "name,permalink_url")
45
+ rescue Abt::HttpError::NotFoundError
46
+ nil
47
47
  end
48
48
  end
49
49
 
50
50
  def task
51
51
  @task ||= begin
52
- warn 'Fetching task...'
53
- api.get("tasks/#{task_gid}", opt_fields: 'name,permalink_url')
54
- rescue Abt::HttpError::NotFoundError
55
- nil
52
+ warn("Fetching task...")
53
+ api.get("tasks/#{task_gid}", opt_fields: "name,permalink_url")
54
+ rescue Abt::HttpError::NotFoundError
55
+ nil
56
56
  end
57
57
  end
58
58
  end