checkoff 0.257.0 → 0.260.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/checkoff/attachments.rb +14 -15
  3. data/lib/checkoff/cli.rb +7 -12
  4. data/lib/checkoff/clients.rb +0 -2
  5. data/lib/checkoff/custom_fields.rb +13 -26
  6. data/lib/checkoff/internal/asana_event_enrichment.rb +11 -13
  7. data/lib/checkoff/internal/asana_event_filter.rb +1 -1
  8. data/lib/checkoff/internal/config_loader.rb +0 -2
  9. data/lib/checkoff/internal/logging.rb +0 -2
  10. data/lib/checkoff/internal/project_hashes.rb +5 -4
  11. data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +9 -9
  12. data/lib/checkoff/internal/search_url/custom_field_variant.rb +7 -0
  13. data/lib/checkoff/internal/search_url/date_param_converter.rb +13 -4
  14. data/lib/checkoff/internal/search_url/parser.rb +2 -7
  15. data/lib/checkoff/internal/search_url/simple_param_converter.rb +11 -5
  16. data/lib/checkoff/internal/section_selector_evaluator.rb +1 -1
  17. data/lib/checkoff/internal/selector_classes/common/function_evaluator.rb +5 -2
  18. data/lib/checkoff/internal/selector_classes/common.rb +13 -31
  19. data/lib/checkoff/internal/selector_classes/function_evaluator.rb +20 -4
  20. data/lib/checkoff/internal/selector_classes/project/function_evaluator.rb +11 -2
  21. data/lib/checkoff/internal/selector_classes/project.rb +5 -13
  22. data/lib/checkoff/internal/selector_classes/section/function_evaluator.rb +8 -5
  23. data/lib/checkoff/internal/selector_classes/section.rb +2 -5
  24. data/lib/checkoff/internal/selector_classes/task/function_evaluator.rb +2 -2
  25. data/lib/checkoff/internal/selector_classes/task.rb +24 -23
  26. data/lib/checkoff/internal/selector_evaluator.rb +8 -9
  27. data/lib/checkoff/internal/task_hashes.rb +17 -9
  28. data/lib/checkoff/internal/task_timing.rb +0 -4
  29. data/lib/checkoff/internal/thread_local.rb +0 -2
  30. data/lib/checkoff/monkeypatches/resource_marshalling.rb +3 -3
  31. data/lib/checkoff/my_tasks.rb +1 -4
  32. data/lib/checkoff/portfolios.rb +0 -1
  33. data/lib/checkoff/project_selectors.rb +1 -1
  34. data/lib/checkoff/projects.rb +19 -8
  35. data/lib/checkoff/resources.rb +14 -3
  36. data/lib/checkoff/sections.rb +13 -19
  37. data/lib/checkoff/subtasks.rb +1 -2
  38. data/lib/checkoff/tags.rb +3 -4
  39. data/lib/checkoff/task_searches.rb +1 -8
  40. data/lib/checkoff/task_selectors.rb +25 -12
  41. data/lib/checkoff/tasks.rb +8 -16
  42. data/lib/checkoff/timelines.rb +5 -10
  43. data/lib/checkoff/timing.rb +1 -2
  44. data/lib/checkoff/version.rb +1 -1
  45. data/lib/checkoff/workspaces.rb +1 -7
  46. data/rbi/checkoff.rbi +122 -179
  47. data/sig/checkoff.rbs +83 -181
  48. metadata +2 -2
@@ -22,12 +22,19 @@ require 'asana'
22
22
  module Checkoff
23
23
  # Work with projects in Asana
24
24
  class Projects
25
+ # @return [Integer]
25
26
  MINUTE = 60
27
+ # @return [Integer]
26
28
  HOUR = MINUTE * 60
29
+ # @return [Integer]
27
30
  DAY = 24 * HOUR
31
+ # @return [Integer]
28
32
  REALLY_LONG_CACHE_TIME = MINUTE * 30
33
+ # @return [Integer]
29
34
  LONG_CACHE_TIME = MINUTE * 15
35
+ # @return [Integer]
30
36
  MEDIUM_CACHE_TIME = MINUTE * 5
37
+ # @return [Integer]
31
38
  SHORT_CACHE_TIME = MINUTE
32
39
 
33
40
  # @!parse
@@ -65,6 +72,12 @@ module Checkoff
65
72
 
66
73
  # Default options used in Asana API to pull tasks
67
74
  #
75
+ # Keys are :per_page (Integer), :options (a Hash whose :fields is an
76
+ # Array<String>) and, when only_uncompleted is set, :completed_since
77
+ # (String). The value type is genuinely per-key, which a single YARD
78
+ # Hash value type cannot express.
79
+ # Callers who want the field list should call #task_fields directly.
80
+ #
68
81
  # @param extra_fields [Array<String>]
69
82
  # @param [Boolean] only_uncompleted
70
83
  #
@@ -109,9 +122,7 @@ module Checkoff
109
122
  # @type [Enumerable<Asana::Resources::Project>]
110
123
  ps = projects_by_workspace_name(workspace_name, extra_fields:)
111
124
  # @type [Asana::Resources::Project,nil]
112
- # @sg-ignore Variable type could not be inferred / Unresolved call to _1 — Solargraph
113
- # doesn't fully support numbered block parameters
114
- project = ps.find { _1.name == project_name }
125
+ project = ps.find { |p| p.name == project_name }
115
126
  project_by_gid(project.gid, extra_fields:) unless project.nil?
116
127
  end
117
128
  end
@@ -122,7 +133,6 @@ module Checkoff
122
133
  # @param [Array<String>] extra_fields
123
134
  #
124
135
  # @return [Asana::Resources::Project]
125
- # @sg-ignore nil check above is not flow-sensitive
126
136
  def project_or_raise(workspace_name, project_name, extra_fields: [])
127
137
  p = project(workspace_name, project_name, extra_fields:)
128
138
  raise "Could not find project #{project_name.inspect} under workspace #{workspace_name}." if p.nil?
@@ -199,9 +209,10 @@ module Checkoff
199
209
  cache_method :projects_by_workspace_name, REALLY_LONG_CACHE_TIME
200
210
 
201
211
  # @param project_obj [Asana::Resources::Project]
202
- # @param project [String, Symbol] - :not_specified, :my_tasks or a project name
212
+ # @param project [String, :not_specified, :my_tasks, nil] - a String is a
213
+ # project name
203
214
  #
204
- # @return [Hash]
215
+ # @return [Hash{String => Object}]
205
216
  def project_to_h(project_obj, project: :not_specified)
206
217
  project_hashes.project_to_h(project_obj, project:)
207
218
  end
@@ -214,14 +225,14 @@ module Checkoff
214
225
  # date is today or in the past.
215
226
  #
216
227
  # @param project [Asana::Resources::Project]
217
- # @param period [Symbol, Array(Symbol, Integer)] See Checkoff::Timing#in_period? - :now_or_before,:this_week
228
+ # @param period [Symbol, Array<Symbol, Integer>] See Checkoff::Timing#in_period? - :now_or_before,:this_week
218
229
  def project_ready?(project, period: :now_or_before)
219
230
  in_period?(project, :ready, period)
220
231
  end
221
232
 
222
233
  # @param project [Asana::Resources::Project]
223
234
  # @param field_name [Symbol,Array]
224
- # @param period [Symbol, Array(Symbol,Integer)] See Checkoff::Timing#in_period? - :now_or_before,:this_week
235
+ # @param period [Symbol, Array<Symbol, Integer>] See Checkoff::Timing#in_period? - :now_or_before,:this_week
225
236
  def in_period?(project, field_name, period)
226
237
  # @type [Date,Time,nil]
227
238
  project_date = project_timing.date_or_time_field_by_name(project, field_name)
@@ -62,9 +62,7 @@ module Checkoff
62
62
  %w[task section project].each do |resource_type_to_try|
63
63
  next unless [resource_type_to_try, nil].include?(resource_type)
64
64
 
65
- # @type [Asana::Resources::Resource, nil]
66
- resource = T.cast(method(:"fetch_#{resource_type_to_try}_gid").call(gid),
67
- T.nilable(Asana::Resources::Resource))
65
+ resource = fetch_gid_by_type(resource_type_to_try, gid)
68
66
  return [resource, resource_type_to_try] if resource
69
67
  end
70
68
  [nil, nil]
@@ -72,6 +70,19 @@ module Checkoff
72
70
 
73
71
  private
74
72
 
73
+ # @param resource_type [String]
74
+ # @param gid [String]
75
+ #
76
+ # @return [Asana::Resources::Resource, nil]
77
+ def fetch_gid_by_type(resource_type, gid)
78
+ case resource_type
79
+ when 'task' then fetch_task_gid(gid)
80
+ when 'section' then fetch_section_gid(gid)
81
+ when 'project' then fetch_project_gid(gid)
82
+ else raise "Unexpected resource_type: #{resource_type}"
83
+ end
84
+ end
85
+
75
86
  # @param gid [String]
76
87
  #
77
88
  # @return [Asana::Resources::Task, nil]
@@ -14,10 +14,15 @@ module Checkoff
14
14
  # @!parse
15
15
  # extend CacheMethod::ClassMethods
16
16
 
17
+ # @return [Integer]
17
18
  MINUTE = 60
19
+ # @return [Integer]
18
20
  HOUR = MINUTE * 60
21
+ # @return [Integer]
19
22
  REALLY_LONG_CACHE_TIME = MINUTE * 30
23
+ # @return [Integer]
20
24
  LONG_CACHE_TIME = MINUTE * 15
25
+ # @return [Integer]
21
26
  SHORT_CACHE_TIME = MINUTE * 5
22
27
 
23
28
  extend Forwardable
@@ -142,11 +147,9 @@ module Checkoff
142
147
  # @param section_name [String, nil]
143
148
  #
144
149
  # @return [Array<String>]
145
- # @sg-ignore
146
150
  def section_task_names(workspace_name, project_name, section_name)
147
151
  task_array = tasks(workspace_name, project_name, section_name)
148
- # @type [Array<String>]
149
- T.cast(task_array.map(&:name), T::Array[String])
152
+ task_array.filter_map(&:name)
150
153
  end
151
154
  cache_method :section_task_names, SHORT_CACHE_TIME
152
155
 
@@ -156,7 +159,6 @@ module Checkoff
156
159
  # @param extra_section_fields [Array<String>]
157
160
  #
158
161
  # @return [Asana::Resources::Section]
159
- # @sg-ignore section() is nil-checked below
160
162
  def section_or_raise(workspace_name, project_name, section_name, extra_section_fields: [])
161
163
  s = section(workspace_name, project_name, section_name,
162
164
  extra_section_fields:)
@@ -184,17 +186,15 @@ module Checkoff
184
186
  #
185
187
  # @return [Asana::Resources::Section, nil]
186
188
  def previous_section(section)
187
- sections = sections_by_project_gid(section.project.fetch('gid'))
189
+ project_sections = sections_by_project_gid(section.project.fetch('gid'))
188
190
 
189
191
  # @type [Array<Asana::Resources::Section>]
190
- # @sg-ignore
191
- sections = sections.to_a
192
+ project_sections_arr = project_sections.to_a
192
193
 
193
- index = sections.find_index { |s| s.gid == section.gid }
194
+ index = project_sections_arr.find_index { |s| s.gid == section.gid }
194
195
  return nil if index.nil? || index.zero?
195
196
 
196
- # @sg-ignore
197
- sections[index - 1]
197
+ project_sections_arr[index - 1]
198
198
  end
199
199
  cache_method :previous_section, SHORT_CACHE_TIME
200
200
 
@@ -283,28 +283,22 @@ module Checkoff
283
283
  # @return [void]
284
284
  def file_task_by_section(by_section, task, project_gid)
285
285
  membership = task.memberships.find do |m|
286
- membership_hash = T.unsafe(m)
287
- T.cast(membership_hash['project'], T::Hash[String, T.untyped])['gid'] == project_gid
286
+ m.fetch('project')['gid'] == project_gid
288
287
  end
289
288
  raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
290
289
 
291
- membership_data = T.unsafe(membership)
292
- section = T.cast(membership_data['section'], T::Hash[String, T.untyped])
293
- section_name = T.cast(section['name'], String)
290
+ section = membership.fetch('section')
291
+ section_name = section.fetch('name')
294
292
 
295
293
  # @type [String, nil]
296
294
  current_section = section_key(section_name)
297
295
 
298
- # @sg-ignore Hash#fetch arg0 expected String,NilClass received String,nil — Solargraph
299
- # treats the nil literal in a Hash{K,nil=>V} declaration and an inferred `nil` union
300
- # member as distinct types; no comment-only fix found
301
296
  by_section.fetch(current_section) << task
302
297
  end
303
298
 
304
299
  # @param workspace_name [String, Symbol]
305
300
  # @param project_name [String, Symbol]
306
301
  # @return [Asana::Resources::Project]
307
- # @sg-ignore projects.project may be nil but is raised below
308
302
  def project_or_raise(workspace_name, project_name)
309
303
  raise ArgumentError, 'Provide nil project_name' if T.unsafe(project_name).nil?
310
304
 
@@ -72,7 +72,7 @@ module Checkoff
72
72
  def subtasks_by_gid(task_gid,
73
73
  extra_fields: [],
74
74
  only_uncompleted: true)
75
- # @type [Hash]
75
+ # @type [Hash{Symbol => undefined}]
76
76
  all_options = projects.task_options(extra_fields: extra_fields + %w[is_rendered_as_separator],
77
77
  only_uncompleted:)
78
78
  options = all_options.fetch(:options, {})
@@ -90,7 +90,6 @@ module Checkoff
90
90
  #
91
91
  # @param subtask [Asana::Resources::Task]
92
92
  # @return [Boolean, nil]
93
- # @sg-ignore Checkoff::Subtasks#subtask_section? return type could not be inferred
94
93
  def subtask_section?(subtask)
95
94
  subtask.is_rendered_as_separator
96
95
  end
data/lib/checkoff/tags.rb CHANGED
@@ -79,7 +79,6 @@ module Checkoff
79
79
  # @param tag_name [String]
80
80
  #
81
81
  # @return [Asana::Resources::Tag]
82
- # @sg-ignore nil check above is not flow-sensitive
83
82
  def tag_or_raise(workspace_name, tag_name)
84
83
  t = tag(workspace_name, tag_name)
85
84
 
@@ -113,9 +112,9 @@ module Checkoff
113
112
  #
114
113
  # @return [Hash{Symbol => Object}]
115
114
  def build_params(options)
116
- { limit: options[:per_page], completed_since: options[:completed_since] }.reject do |_, v|
117
- # @sg-ignore Unresolved call to nil? on Object — Solargraph doesn't treat a Hash's
118
- # declared `Object` value type as full Object here
115
+ # @type [Hash{Symbol => Object}]
116
+ params = { limit: options[:per_page], completed_since: options[:completed_since] }
117
+ params.reject do |_, v|
119
118
  v.nil? || Array(v).empty?
120
119
  end
121
120
  end
@@ -73,9 +73,6 @@ module Checkoff
73
73
  workspace = workspaces.workspace_or_raise(workspace_name)
74
74
  api_params, task_selector = @search_url_parser.convert_params(url)
75
75
  debug { "Task search params: api_params=#{api_params}, task_selector=#{task_selector}" }
76
- # @sg-ignore task_selector expected Symbol,Array<Symbol,Integer,Array> received
77
- # Hash{String=>String} — Solargraph mis-assigns tuple-destructured types from
78
- # `api_params, task_selector = @search_url_parser.convert_params(url)` above
79
76
  raw_task_search(api_params, workspace_gid: workspace.gid, task_selector:,
80
77
  extra_fields:)
81
78
  end
@@ -189,12 +186,8 @@ module Checkoff
189
186
 
190
187
  # @param [Array<String>] extra_fields
191
188
  # @return [Hash{Symbol => Array<String>}]
192
- # @sg-ignore return type could not be inferred — bracket access on a Hash{Symbol=>undefined}
193
- # local doesn't narrow even with a T.cast on the return expression
194
189
  def calculate_api_options(extra_fields)
195
- # @type [Hash{Symbol => undefined}]
196
- all_options = projects.task_options(extra_fields: ['custom_fields'] + extra_fields)
197
- all_options[:options]
190
+ { fields: projects.task_fields(extra_fields: ['custom_fields'] + extra_fields) }
198
191
  end
199
192
 
200
193
  # bundle exec ./task_searches.rb
@@ -40,7 +40,7 @@ module Checkoff
40
40
  end
41
41
 
42
42
  # @param [Asana::Resources::Task] task
43
- # @param [Symbol, Array<Symbol, Integer, Array>] task_selector Filter based on
43
+ # @param [Symbol, Array<Symbol, String, Integer, Array>] task_selector Filter based on
44
44
  # task details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
45
45
  # @return [Boolean]
46
46
  def filter_via_task_selector(task, task_selector)
@@ -60,30 +60,43 @@ module Checkoff
60
60
  # @return [Checkoff::CustomFields]
61
61
  attr_reader :custom_fields
62
62
 
63
+ class << self
64
+ # @return [Array(Symbol, Array)]
65
+ def task_selector
66
+ task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
67
+ function_name, args = validate_task_selector(JSON.parse(task_selector_json))
68
+ [function_name.to_sym, args]
69
+ end
70
+
71
+ # @param parsed [Object] result of JSON.parse on a task selector
72
+ # @return [Array(String, Array)]
73
+ def validate_task_selector(parsed)
74
+ raise "Expected a 2-element [function_name, args] array, got #{parsed.inspect}" \
75
+ unless parsed.is_a?(Array) && parsed.length == 2
76
+
77
+ function_name = parsed.fetch(0)
78
+ args = parsed.fetch(1)
79
+ raise "Expected function_name to be a String, got #{function_name.inspect}" \
80
+ unless function_name.is_a?(String)
81
+ raise "Expected args to be an Array, got #{args.inspect}" unless args.is_a?(Array)
82
+
83
+ [function_name, args]
84
+ end
85
+ end
86
+
63
87
  # bundle exec ./task_selectors.rb
64
88
  # :nocov:
65
89
  class << self
66
90
  # @return [String]
67
- # @sg-ignore
68
91
  def project_name
69
92
  ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
70
93
  end
71
94
 
72
- # @sg-ignore
73
95
  # @return [String]
74
96
  def workspace_name
75
97
  ARGV[0] || raise('Please pass workspace name as first argument')
76
98
  end
77
99
 
78
- # @return [Array(Symbol, Array)]
79
- # @sg-ignore
80
- def task_selector
81
- task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
82
-
83
- # @return [Symbol, Array]
84
- T.cast(JSON.parse(task_selector_json), [Symbol, Array])
85
- end
86
-
87
100
  # @return [void]
88
101
  def run
89
102
  require 'checkoff/projects'
@@ -32,7 +32,7 @@ module Checkoff
32
32
  LONG_CACHE_TIME = MINUTE * 15
33
33
  SHORT_CACHE_TIME = MINUTE * 5
34
34
 
35
- # @param config [Hash, Checkoff::Internal::EnvFallbackConfigLoader]
35
+ # @param config [Hash{Symbol => Object}, Checkoff::Internal::EnvFallbackConfigLoader]
36
36
  # @param client [Asana::Client]
37
37
  # @param workspaces [Checkoff::Workspaces]
38
38
  # @param sections [Checkoff::Sections]
@@ -180,8 +180,6 @@ module Checkoff
180
180
  # @param assignee_gid [String]
181
181
  #
182
182
  # @return [Asana::Resources::Task]
183
- # @sg-ignore @asana_task.create return type could not be inferred — calling a class method
184
- # through a Class<T>-typed ivar is a known Solargraph dispatch limitation
185
183
  def add_task(name,
186
184
  workspace_gid: @workspaces.default_workspace_gid,
187
185
  assignee_gid: default_assignee_gid)
@@ -282,7 +280,7 @@ module Checkoff
282
280
  task_hashes.task_to_h(task)
283
281
  end
284
282
 
285
- # @param task_data [Hash]
283
+ # @param task_data [Hash{String => Object}]
286
284
  #
287
285
  # @return [Asana::Resources::Task]
288
286
  def h_to_task(task_data)
@@ -295,16 +293,14 @@ module Checkoff
295
293
  # @param portfolio_name [String]
296
294
  # @param workspace_name [String]
297
295
  # @return [Boolean]
298
- # @sg-ignore Checkoff::Tasks#in_portfolio_named? return type could not be inferred
299
296
  def in_portfolio_named?(task,
300
297
  portfolio_name,
301
- workspace_name: @workspaces.default_workspace.name)
298
+ workspace_name: T.must(@workspaces.default_workspace.name))
302
299
  portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
303
- T.cast(task.memberships.any? do |membership|
304
- m = T.cast(membership, T::Hash[String, T.untyped])
305
- project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
300
+ task.memberships.any? do |membership|
301
+ project_gid = membership.fetch('project').fetch('gid')
306
302
  portfolio_projects.any? { |portfolio_project| portfolio_project.gid == project_gid }
307
- end, T::Boolean)
303
+ end
308
304
  end
309
305
 
310
306
  # True if the task is in a project which is in the given portfolio
@@ -315,13 +311,12 @@ module Checkoff
315
311
  # @return [Boolean]
316
312
  def in_portfolio_more_than_once?(task,
317
313
  portfolio_name,
318
- workspace_name: @workspaces.default_workspace.name)
314
+ workspace_name: T.must(@workspaces.default_workspace.name))
319
315
  portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
320
316
  portfolio_project_gids = portfolio_projects.map(&:gid)
321
317
  seen = T.let(false, T::Boolean)
322
318
  task.memberships.each do |membership|
323
- m = T.cast(membership, T::Hash[String, T.untyped])
324
- project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
319
+ project_gid = membership.fetch('project').fetch('gid')
325
320
  next unless portfolio_project_gids.include?(project_gid)
326
321
  return true if seen
327
322
 
@@ -386,9 +381,6 @@ module Checkoff
386
381
  end
387
382
 
388
383
  # @return [String]
389
- # @sg-ignore @config.fetch return type could not be inferred — @config is intentionally
390
- # dual-typed [Hash, EnvFallbackConfigLoader] throughout this codebase; Solargraph can't
391
- # unify #fetch across that union
392
384
  def default_assignee_gid
393
385
  @config.fetch(:default_assignee_gid)
394
386
  end
@@ -57,9 +57,8 @@ module Checkoff
57
57
  section_data = membership_data.fetch('section')
58
58
  section_gid = section_data.fetch('gid')
59
59
  section = @sections.section_by_gid(section_gid)
60
- # @sg-ignore section_by_gid can return nil on a gid lookup miss, and
61
- # task_data_dependent_on_previous_section_last_milestone? dereferences it immediately
62
- # without a nil guard — looks like a real gap, flagging for the follow-up code PR
60
+ next false if section.nil?
61
+
63
62
  task_data_dependent_on_previous_section_last_milestone?(task_data, section)
64
63
  end
65
64
  end
@@ -69,7 +68,7 @@ module Checkoff
69
68
  # @return [Boolean]
70
69
  def last_task_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
71
70
  unless limit_to_portfolio_name.nil?
72
- limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
71
+ limit_to_projects = @portfolios.projects_in_portfolio(T.must(@workspaces.default_workspace.name),
73
72
  limit_to_portfolio_name)
74
73
  end
75
74
 
@@ -97,8 +96,6 @@ module Checkoff
97
96
 
98
97
  all_dependent_task_gids ||= @tasks.all_dependent_tasks(task).map(&:gid)
99
98
 
100
- # @sg-ignore all_dependent_task_gids is nil-initialized then set via ||= above, but
101
- # Solargraph doesn't narrow it back to non-nil after the reassignment
102
99
  all_dependent_task_gids.include? last_milestone.gid
103
100
  end
104
101
  end
@@ -108,7 +105,7 @@ module Checkoff
108
105
  # @return [Boolean]
109
106
  def any_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
110
107
  unless limit_to_portfolio_name.nil?
111
- limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
108
+ limit_to_projects = @portfolios.projects_in_portfolio(T.must(@workspaces.default_workspace.name),
112
109
  limit_to_portfolio_name)
113
110
  end
114
111
 
@@ -134,8 +131,6 @@ module Checkoff
134
131
  dependent_task.resource_subtype == 'milestone'
135
132
  end
136
133
 
137
- # @sg-ignore all_dependent_milestones is nil-initialized then set via ||= above, but
138
- # Solargraph doesn't narrow it back to non-nil after the reassignment
139
134
  all_dependent_milestones.any? do |milestone|
140
135
  milestone.memberships.any? do |milestone_membership_data|
141
136
  milestone_membership_data.fetch('project').fetch('gid') == project_gid
@@ -157,7 +152,7 @@ module Checkoff
157
152
 
158
153
  private
159
154
 
160
- # @param task_data [Hash]
155
+ # @param task_data [Hash{String => Object}]
161
156
  # @param section [Asana::Resources::Section]
162
157
  #
163
158
  # @return [Boolean]
@@ -36,7 +36,7 @@ module Checkoff
36
36
  end
37
37
 
38
38
  # @param date_or_time [Date,Time,nil]
39
- # @param period [Symbol, Array(Symbol,Integer)]
39
+ # @param period [Symbol, Array<Symbol, Integer>]
40
40
  #
41
41
  # Valid values: :this_week, :now_or_before, :indefinite, [:less_than_n_days_ago, Integer]
42
42
  def in_period?(date_or_time, period)
@@ -175,7 +175,6 @@ module Checkoff
175
175
  # @param num_days [Integer]
176
176
  #
177
177
  # @return [Date]
178
- # @sg-ignore Checkoff::Timing#n_days_from_today return type could not be inferred
179
178
  def n_days_from_today(num_days)
180
179
  T.cast(@today_getter.today + num_days, Date)
181
180
  end
@@ -4,5 +4,5 @@
4
4
  # Command-line and gem client for Asana (unofficial)
5
5
  module Checkoff
6
6
  # Version of library
7
- VERSION = '0.257.0'
7
+ VERSION = '0.260.0'
8
8
  end
@@ -22,7 +22,7 @@ module Checkoff
22
22
  # @!parse
23
23
  # extend CacheMethod::ClassMethods
24
24
 
25
- # @param config [Hash, Checkoff::Internal::EnvFallbackConfigLoader]
25
+ # @param config [Hash{Symbol => Object}, Checkoff::Internal::EnvFallbackConfigLoader]
26
26
  # @param client [Asana::Client]
27
27
  # @param asana_workspace [Class<Asana::Resources::Workspace>]
28
28
  def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
@@ -44,8 +44,6 @@ module Checkoff
44
44
  cache_method :workspace, LONG_CACHE_TIME
45
45
 
46
46
  # @return [Asana::Resources::Workspace]
47
- # @sg-ignore @asana_workspace.find_by_id return type could not be inferred — calling a
48
- # class method through a Class<T>-typed ivar is a known Solargraph dispatch limitation
49
47
  def default_workspace
50
48
  @asana_workspace.find_by_id(client, default_workspace_gid)
51
49
  end
@@ -53,7 +51,6 @@ module Checkoff
53
51
 
54
52
  # @param [String, Symbol] workspace_name
55
53
  # @return [Asana::Resources::Workspace]
56
- # @sg-ignore workspace() is nil-checked below
57
54
  def workspace_or_raise(workspace_name)
58
55
  w = workspace(workspace_name)
59
56
  raise "Could not find workspace #{workspace_name}" if w.nil?
@@ -62,9 +59,6 @@ module Checkoff
62
59
  end
63
60
 
64
61
  # @return [String]
65
- # @sg-ignore @config.fetch return type could not be inferred — @config is intentionally
66
- # dual-typed [Hash, EnvFallbackConfigLoader] throughout this codebase; Solargraph can't
67
- # unify #fetch across that union
68
62
  def default_workspace_gid
69
63
  @config.fetch(:default_workspace_gid)
70
64
  end