checkoff 0.256.0 → 0.258.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.
- checksums.yaml +4 -4
- data/lib/checkoff/attachments.rb +12 -11
- data/lib/checkoff/cli.rb +11 -14
- data/lib/checkoff/clients.rb +2 -2
- data/lib/checkoff/custom_fields.rb +9 -7
- data/lib/checkoff/internal/asana_event_enrichment.rb +17 -11
- data/lib/checkoff/internal/asana_event_filter.rb +4 -0
- data/lib/checkoff/internal/config_loader.rb +2 -2
- data/lib/checkoff/internal/logging.rb +2 -2
- data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +8 -7
- data/lib/checkoff/internal/search_url/custom_field_variant.rb +2 -0
- data/lib/checkoff/internal/search_url/date_param_converter.rb +8 -2
- data/lib/checkoff/internal/search_url/parser.rb +7 -5
- data/lib/checkoff/internal/search_url/results_merger.rb +1 -2
- data/lib/checkoff/internal/search_url/simple_param_converter.rb +13 -4
- data/lib/checkoff/internal/selector_classes/common/function_evaluator.rb +3 -0
- data/lib/checkoff/internal/selector_classes/common.rb +14 -30
- data/lib/checkoff/internal/selector_classes/function_evaluator.rb +13 -2
- data/lib/checkoff/internal/selector_classes/project/function_evaluator.rb +9 -0
- data/lib/checkoff/internal/selector_classes/project.rb +6 -13
- data/lib/checkoff/internal/selector_classes/section/function_evaluator.rb +6 -3
- data/lib/checkoff/internal/selector_classes/section.rb +2 -4
- data/lib/checkoff/internal/selector_classes/task.rb +42 -16
- data/lib/checkoff/internal/selector_evaluator.rb +4 -6
- data/lib/checkoff/internal/task_timing.rb +6 -5
- data/lib/checkoff/internal/thread_local.rb +2 -1
- data/lib/checkoff/my_tasks.rb +3 -1
- data/lib/checkoff/portfolios.rb +1 -1
- data/lib/checkoff/projects.rb +4 -3
- data/lib/checkoff/sections.rb +7 -11
- data/lib/checkoff/subtasks.rb +2 -1
- data/lib/checkoff/tags.rb +3 -2
- data/lib/checkoff/task_searches.rb +6 -3
- data/lib/checkoff/task_selectors.rb +3 -3
- data/lib/checkoff/tasks.rb +10 -9
- data/lib/checkoff/timelines.rb +21 -5
- data/lib/checkoff/timing.rb +4 -8
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff/workspaces.rb +5 -2
- data/rbi/checkoff.rbi +145 -104
- data/sig/checkoff.rbs +110 -97
- metadata +2 -2
|
@@ -105,12 +105,9 @@ module Checkoff
|
|
|
105
105
|
# @param custom_field_name [String]
|
|
106
106
|
# @return [String, nil]
|
|
107
107
|
def evaluate(resource, custom_field_name)
|
|
108
|
-
|
|
109
|
-
custom_field = @custom_fields.resource_custom_field_by_name(resource, custom_field_name)
|
|
110
|
-
# @sg-ignore
|
|
108
|
+
custom_field = custom_fields.resource_custom_field_by_name(resource, custom_field_name)
|
|
111
109
|
return nil if custom_field.nil?
|
|
112
110
|
|
|
113
|
-
# @sg-ignore
|
|
114
111
|
custom_field['display_value']
|
|
115
112
|
end
|
|
116
113
|
end
|
|
@@ -125,17 +122,13 @@ module Checkoff
|
|
|
125
122
|
false
|
|
126
123
|
end
|
|
127
124
|
|
|
128
|
-
# @sg-ignore
|
|
129
125
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
130
|
-
# @sg-ignore
|
|
131
126
|
# @param custom_field_gid [String]
|
|
132
127
|
# @return [String, nil]
|
|
133
|
-
# @sg-ignore
|
|
134
128
|
def evaluate(resource, custom_field_gid)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
custom_field['display_value']
|
|
129
|
+
custom_field = custom_fields.resource_custom_field_by_gid_or_raise(resource, custom_field_gid)
|
|
130
|
+
display_value = custom_field['display_value']
|
|
131
|
+
display_value.nil? ? nil : T.cast(display_value, String)
|
|
139
132
|
end
|
|
140
133
|
end
|
|
141
134
|
|
|
@@ -152,17 +145,13 @@ module Checkoff
|
|
|
152
145
|
end
|
|
153
146
|
|
|
154
147
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
155
|
-
# @sg-ignore
|
|
156
148
|
# @param custom_field_gid [String]
|
|
157
149
|
# @param custom_field_values_gids [Array<String>]
|
|
158
150
|
# @return [Boolean]
|
|
159
|
-
# @sg-ignore
|
|
160
151
|
def evaluate(resource, custom_field_gid, custom_field_values_gids)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
custom_field_gid)
|
|
152
|
+
actual_custom_field_values_gids = custom_fields.resource_custom_field_values_gids_or_raise(resource,
|
|
153
|
+
custom_field_gid)
|
|
164
154
|
|
|
165
|
-
# @sg-ignore
|
|
166
155
|
actual_custom_field_values_gids.intersect?(custom_field_values_gids)
|
|
167
156
|
end
|
|
168
157
|
end
|
|
@@ -181,17 +170,13 @@ module Checkoff
|
|
|
181
170
|
|
|
182
171
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
183
172
|
# @param custom_field_name [String]
|
|
184
|
-
# @sg-ignore
|
|
185
173
|
# @param custom_field_value_names [Array<String>]
|
|
186
|
-
# @sg-ignore
|
|
187
174
|
# @return [Boolean]
|
|
188
175
|
def evaluate(resource, custom_field_name, custom_field_value_names)
|
|
189
176
|
actual_custom_field_values_names =
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
custom_field_name)
|
|
177
|
+
custom_fields.resource_custom_field_values_names_by_name(resource,
|
|
178
|
+
custom_field_name)
|
|
193
179
|
|
|
194
|
-
# @sg-ignore
|
|
195
180
|
actual_custom_field_values_names.intersect?(custom_field_value_names)
|
|
196
181
|
end
|
|
197
182
|
end
|
|
@@ -214,12 +199,10 @@ module Checkoff
|
|
|
214
199
|
# @return [Boolean]
|
|
215
200
|
def evaluate(resource, custom_field_gid, custom_field_values_gids)
|
|
216
201
|
actual_custom_field_values_gids =
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
custom_field_gid)
|
|
202
|
+
custom_fields.resource_custom_field_values_gids_or_raise(resource,
|
|
203
|
+
custom_field_gid)
|
|
220
204
|
|
|
221
205
|
custom_field_values_gids.all? do |custom_field_value|
|
|
222
|
-
# @sg-ignore
|
|
223
206
|
actual_custom_field_values_gids.include?(custom_field_value)
|
|
224
207
|
end
|
|
225
208
|
end
|
|
@@ -238,9 +221,10 @@ module Checkoff
|
|
|
238
221
|
end
|
|
239
222
|
|
|
240
223
|
# @param resource [Asana::Resources::Task, Asana::Resources::Project]
|
|
241
|
-
# @sg-ignore
|
|
242
224
|
# @param prefix [String]
|
|
243
225
|
# @return [boolish]
|
|
226
|
+
# @sg-ignore Checkoff::SelectorClasses::Common::NameStartsWithPFunctionEvaluator#evaluate
|
|
227
|
+
# return type could not be inferred
|
|
244
228
|
def evaluate(resource, prefix)
|
|
245
229
|
resource.name&.start_with?(prefix)
|
|
246
230
|
end
|
|
@@ -254,9 +238,9 @@ module Checkoff
|
|
|
254
238
|
|
|
255
239
|
# @param _resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
256
240
|
# @return [String]
|
|
257
|
-
# @sg-ignore
|
|
241
|
+
# @sg-ignore selector is only String here per matches?'s is_a?(String) check, in another method
|
|
258
242
|
def evaluate(_resource)
|
|
259
|
-
|
|
243
|
+
selector
|
|
260
244
|
end
|
|
261
245
|
end
|
|
262
246
|
end
|
|
@@ -9,21 +9,32 @@ module Checkoff
|
|
|
9
9
|
class FunctionEvaluator
|
|
10
10
|
include Logging
|
|
11
11
|
|
|
12
|
+
# Concrete subclasses each declare their own initialize with their
|
|
13
|
+
# specific keyword args (and set @selector themselves before calling
|
|
14
|
+
# super()); this signature exists only so Solargraph can resolve
|
|
15
|
+
# `evaluator_class.new(selector:, **kwargs)` call sites that hold a
|
|
16
|
+
# `Class<FunctionEvaluator>` reference rather than a concrete subclass -
|
|
17
|
+
# deliberately a no-op so a bare `super()` call from those subclasses
|
|
18
|
+
# can't clobber the @selector they already set.
|
|
19
|
+
# @param selector [Symbol, Array, String, nil]
|
|
20
|
+
# @param _kwargs [Hash]
|
|
21
|
+
def initialize(selector: nil, **_kwargs); end
|
|
22
|
+
|
|
12
23
|
# @param _index [Integer]
|
|
13
24
|
def evaluate_arg?(_index)
|
|
14
25
|
true
|
|
15
26
|
end
|
|
16
27
|
|
|
17
28
|
# @return [Boolean]
|
|
18
|
-
# @sg-ignore
|
|
29
|
+
# @sg-ignore return type could not be inferred — abstract method body is only `raise`
|
|
19
30
|
def matches?
|
|
20
31
|
raise 'Override me!'
|
|
21
32
|
end
|
|
22
33
|
|
|
23
34
|
# @param _task [Asana::Resources::Task]
|
|
24
35
|
# @param _args [Array<Object>]
|
|
25
|
-
# @sg-ignore
|
|
26
36
|
# @return [Object]
|
|
37
|
+
# @sg-ignore return type could not be inferred — abstract method body is only `raise`
|
|
27
38
|
def evaluate(_task, *_args)
|
|
28
39
|
raise 'Implement me!'
|
|
29
40
|
end
|
|
@@ -25,6 +25,15 @@ module Checkoff
|
|
|
25
25
|
super()
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# @return [Checkoff::Projects]
|
|
29
|
+
attr_reader :projects
|
|
30
|
+
|
|
31
|
+
# @return [Checkoff::Portfolios]
|
|
32
|
+
attr_reader :portfolios
|
|
33
|
+
|
|
34
|
+
# @return [Checkoff::Workspaces]
|
|
35
|
+
attr_reader :workspaces
|
|
36
|
+
|
|
28
37
|
private
|
|
29
38
|
|
|
30
39
|
# @return [Array(Symbol, Array)]
|
|
@@ -17,7 +17,7 @@ module Checkoff
|
|
|
17
17
|
|
|
18
18
|
# @param resource [Asana::Resources::Project]
|
|
19
19
|
# @return [String, nil]
|
|
20
|
-
# @sg-ignore
|
|
20
|
+
# @sg-ignore nilable return from an @!parse-declared method isn't inferred
|
|
21
21
|
def evaluate(resource)
|
|
22
22
|
resource.due_date
|
|
23
23
|
end
|
|
@@ -33,11 +33,9 @@ module Checkoff
|
|
|
33
33
|
|
|
34
34
|
# @param project [Asana::Resources::Project]
|
|
35
35
|
# @param period [Symbol] - :now_or_before or :this_week
|
|
36
|
-
# @sg-ignore
|
|
37
36
|
# @return [Boolean]
|
|
38
37
|
def evaluate(project, period = :now_or_before)
|
|
39
|
-
|
|
40
|
-
@projects.project_ready?(project, period:)
|
|
38
|
+
projects.project_ready?(project, period:)
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
|
|
@@ -53,19 +51,14 @@ module Checkoff
|
|
|
53
51
|
# @param portfolio_name [String]
|
|
54
52
|
# @param workspace_name [String, nil]
|
|
55
53
|
# @param extra_project_fields [Array<String>]
|
|
56
|
-
# @sg-ignore
|
|
57
54
|
#
|
|
58
|
-
# @sg-ignore
|
|
59
55
|
# @return [Boolean]
|
|
60
56
|
def evaluate(project, portfolio_name, workspace_name: nil, extra_project_fields: [])
|
|
61
57
|
workspace_name ||= project.workspace&.name
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
extra_project_fields:)
|
|
67
|
-
# @sg-ignore
|
|
68
|
-
projects.any? { |p| p.name == project.name }
|
|
58
|
+
workspace_name ||= workspaces.default_workspace.name
|
|
59
|
+
matching_projects = portfolios.projects_in_portfolio(T.must(workspace_name), portfolio_name,
|
|
60
|
+
extra_project_fields:)
|
|
61
|
+
matching_projects.any? { |p| p.name == project.name }
|
|
69
62
|
end
|
|
70
63
|
end
|
|
71
64
|
end
|
|
@@ -22,13 +22,16 @@ module Checkoff
|
|
|
22
22
|
super()
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# @return [Checkoff::Sections]
|
|
26
|
+
attr_reader :sections
|
|
27
|
+
|
|
28
|
+
# @return [Asana::Client]
|
|
29
|
+
attr_reader :client
|
|
30
|
+
|
|
25
31
|
private
|
|
26
32
|
|
|
27
33
|
# @return [Array(Symbol, Array)]
|
|
28
34
|
attr_reader :selector
|
|
29
|
-
|
|
30
|
-
# @return [Asana::Client]
|
|
31
|
-
attr_reader :client
|
|
32
35
|
end
|
|
33
36
|
end
|
|
34
37
|
end
|
|
@@ -18,7 +18,7 @@ module Checkoff
|
|
|
18
18
|
# @param section [Asana::Resources::Section]
|
|
19
19
|
#
|
|
20
20
|
# @return [Boolean]
|
|
21
|
-
# @sg-ignore
|
|
21
|
+
# @sg-ignore String, nil safe-nav chain isn't inferred as returning Boolean
|
|
22
22
|
def evaluate(section)
|
|
23
23
|
tasks = client.tasks.get_tasks(section: section.gid,
|
|
24
24
|
per_page: 100,
|
|
@@ -37,11 +37,9 @@ module Checkoff
|
|
|
37
37
|
|
|
38
38
|
# @param section [Asana::Resources::Section]
|
|
39
39
|
#
|
|
40
|
-
# @sg-ignore
|
|
41
40
|
# @return [Boolean]
|
|
42
41
|
def evaluate(section)
|
|
43
|
-
|
|
44
|
-
@sections.tasks_by_section_gid(section.gid).any?
|
|
42
|
+
sections.tasks_by_section_gid(section.gid).any?
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
end
|
|
@@ -24,8 +24,12 @@ module Checkoff
|
|
|
24
24
|
# @type [Hash{'unwrapped' => Hash}]
|
|
25
25
|
task_data = tasks.task_to_h(task)
|
|
26
26
|
# @type [Hash{'membership_by_project_name' => Hash}]
|
|
27
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
28
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
27
29
|
unwrapped = task_data.fetch('unwrapped')
|
|
28
30
|
# @type [Array]
|
|
31
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
32
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
29
33
|
projects = unwrapped.fetch('membership_by_project_name').keys
|
|
30
34
|
!(projects - [:my_tasks]).empty?
|
|
31
35
|
end
|
|
@@ -45,10 +49,18 @@ module Checkoff
|
|
|
45
49
|
# @param task [Asana::Resources::Task]
|
|
46
50
|
# @param section_name_prefix [String]
|
|
47
51
|
# @return [Boolean]
|
|
48
|
-
# @sg-ignore
|
|
49
52
|
def evaluate(task, section_name_prefix)
|
|
53
|
+
# @type [Hash{'unwrapped' => Hash}]
|
|
50
54
|
task_data = tasks.task_to_h(task)
|
|
51
|
-
|
|
55
|
+
# @type [Hash{'membership_by_section_name' => Hash}]
|
|
56
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
57
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
58
|
+
unwrapped = task_data.fetch('unwrapped')
|
|
59
|
+
# @type [Array]
|
|
60
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
61
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
62
|
+
section_names = unwrapped.fetch('membership_by_section_name').keys
|
|
63
|
+
section_names.any? do |section_name|
|
|
52
64
|
String(section_name).start_with?(section_name_prefix)
|
|
53
65
|
end
|
|
54
66
|
end
|
|
@@ -65,13 +77,21 @@ module Checkoff
|
|
|
65
77
|
false
|
|
66
78
|
end
|
|
67
79
|
|
|
68
|
-
# @sg-ignore
|
|
69
80
|
# @param task [Asana::Resources::Task]
|
|
70
81
|
# @param section_name [String]
|
|
71
82
|
# @return [Boolean]
|
|
72
83
|
def evaluate(task, section_name)
|
|
84
|
+
# @type [Hash{'unwrapped' => Hash}]
|
|
73
85
|
task_data = tasks.task_to_h(task)
|
|
74
|
-
|
|
86
|
+
# @type [Hash{'membership_by_section_name' => Hash}]
|
|
87
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
88
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
89
|
+
unwrapped = task_data.fetch('unwrapped')
|
|
90
|
+
# @type [Array]
|
|
91
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
92
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
93
|
+
section_names = unwrapped.fetch('membership_by_section_name').keys
|
|
94
|
+
section_names.any?(section_name)
|
|
75
95
|
end
|
|
76
96
|
end
|
|
77
97
|
|
|
@@ -89,13 +109,15 @@ module Checkoff
|
|
|
89
109
|
# @param task [Asana::Resources::Task]
|
|
90
110
|
# @param project_name [String]
|
|
91
111
|
# @return [Boolean]
|
|
92
|
-
# @sg-ignore
|
|
112
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
113
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
93
114
|
def evaluate(task, project_name)
|
|
94
115
|
project_names = task.memberships.map do |membership|
|
|
95
|
-
|
|
96
|
-
|
|
116
|
+
# @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
|
|
117
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
118
|
+
membership.fetch('project').fetch('name')
|
|
97
119
|
end
|
|
98
|
-
|
|
120
|
+
project_names.include?(project_name)
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
123
|
|
|
@@ -132,9 +154,11 @@ module Checkoff
|
|
|
132
154
|
# @param task [Asana::Resources::Task]
|
|
133
155
|
# @param tag_name [String]
|
|
134
156
|
# @return [Boolean]
|
|
135
|
-
# @sg-ignore
|
|
136
157
|
def evaluate(task, tag_name)
|
|
137
|
-
|
|
158
|
+
# @type [Array<Asana::Resources::Tag>]
|
|
159
|
+
tags = task.tags.to_a
|
|
160
|
+
tag_names = tags.map(&:name)
|
|
161
|
+
tag_names.include?(tag_name)
|
|
138
162
|
end
|
|
139
163
|
end
|
|
140
164
|
|
|
@@ -235,13 +259,13 @@ module Checkoff
|
|
|
235
259
|
|
|
236
260
|
# @type [Array<Asana::Resources::Story>]
|
|
237
261
|
stories = task.stories(per_page: 100).to_a.reject do |story|
|
|
238
|
-
# @sg-ignore
|
|
262
|
+
# @sg-ignore story's inferred block param type regressed on rbs >= 4.1.0, fix in progress upstream
|
|
263
|
+
# https://github.com/castwide/solargraph/pull/1228
|
|
239
264
|
excluding_resource_subtypes.include? story.resource_subtype
|
|
240
265
|
end
|
|
241
266
|
return true if stories.empty? # no stories == infinitely old!
|
|
242
267
|
|
|
243
|
-
last_story = stories.last
|
|
244
|
-
# @sg-ignore
|
|
268
|
+
last_story = T.must(stories.last)
|
|
245
269
|
last_story_created_at = Time.parse(last_story.created_at)
|
|
246
270
|
n_days_ago = Time.at(Time.now.to_i - (num_days * 86_400))
|
|
247
271
|
last_story_created_at < n_days_ago
|
|
@@ -362,11 +386,13 @@ module Checkoff
|
|
|
362
386
|
# @param task [Asana::Resources::Task]
|
|
363
387
|
#
|
|
364
388
|
# @return [Boolean]
|
|
365
|
-
# @sg-ignore
|
|
389
|
+
# @sg-ignore Checkoff::SelectorClasses::Task::MilestonePFunctionEvaluator#evaluate
|
|
390
|
+
# return type could not be inferred
|
|
366
391
|
def evaluate(task)
|
|
367
|
-
|
|
392
|
+
resource_subtype = task.resource_subtype
|
|
393
|
+
raise 'Please add resource_subtype to extra_fields' if resource_subtype.nil?
|
|
368
394
|
|
|
369
|
-
|
|
395
|
+
resource_subtype == 'milestone'
|
|
370
396
|
end
|
|
371
397
|
end
|
|
372
398
|
|
|
@@ -11,13 +11,11 @@ module Checkoff
|
|
|
11
11
|
|
|
12
12
|
function_evaluators.each do |evaluator_class|
|
|
13
13
|
# @type [Checkoff::SelectorClasses::FunctionEvaluator]
|
|
14
|
-
# @sg-ignore
|
|
15
14
|
evaluator = evaluator_class.new(selector:,
|
|
16
15
|
**initializer_kwargs)
|
|
17
16
|
|
|
18
17
|
next unless evaluator.matches?
|
|
19
18
|
|
|
20
|
-
# @sg-ignore
|
|
21
19
|
return try_this_evaluator(selector, evaluator)
|
|
22
20
|
end
|
|
23
21
|
|
|
@@ -32,18 +30,18 @@ module Checkoff
|
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
# @return [Array<Class<Checkoff::SelectorClasses::FunctionEvaluator>>]
|
|
35
|
-
# @sg-ignore
|
|
33
|
+
# @sg-ignore abstract method always raises; declared type documents the override contract, not this body
|
|
36
34
|
def function_evaluators
|
|
37
35
|
raise 'Implement me!'
|
|
38
36
|
end
|
|
39
37
|
|
|
40
|
-
# @param selector [Array]
|
|
38
|
+
# @param selector [Symbol, Array]
|
|
41
39
|
# @param evaluator [Checkoff::SelectorClasses::FunctionEvaluator]
|
|
42
40
|
# @return [Array]
|
|
43
41
|
def evaluate_args(selector, evaluator)
|
|
44
42
|
return [] unless selector.is_a?(Array)
|
|
45
43
|
|
|
46
|
-
# @sg-ignore
|
|
44
|
+
# @sg-ignore Array#[] with an open-ended range is inferred as nilable even though it never is here
|
|
47
45
|
selector[1..].map.with_index do |item, index|
|
|
48
46
|
if evaluator.evaluate_arg?(index)
|
|
49
47
|
evaluate(item)
|
|
@@ -53,7 +51,7 @@ module Checkoff
|
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
|
|
56
|
-
# @param selector [Array]
|
|
54
|
+
# @param selector [Symbol, Array]
|
|
57
55
|
# @param evaluator [Checkoff::SelectorClasses::FunctionEvaluator]
|
|
58
56
|
# @return [Boolean, Object, nil]
|
|
59
57
|
def try_this_evaluator(selector, evaluator)
|
|
@@ -19,14 +19,16 @@ module Checkoff
|
|
|
19
19
|
|
|
20
20
|
# @param task [Asana::Resources::Task]
|
|
21
21
|
# @return [Time, nil]
|
|
22
|
-
# @sg-ignore
|
|
22
|
+
# @sg-ignore return type could not be inferred — #to_time called via &. on a Date|Time
|
|
23
|
+
# union receiver
|
|
23
24
|
def start_time(task)
|
|
24
25
|
date_or_time_field_by_name(task, :start)&.to_time
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# @param task [Asana::Resources::Task]
|
|
28
|
-
# @sg-ignore
|
|
29
29
|
# @return [Time, nil]
|
|
30
|
+
# @sg-ignore return type could not be inferred — #to_time called via &. on a Date|Time
|
|
31
|
+
# union receiver
|
|
30
32
|
def due_time(task)
|
|
31
33
|
date_or_time_field_by_name(task, :due)&.to_time
|
|
32
34
|
end
|
|
@@ -35,8 +37,7 @@ module Checkoff
|
|
|
35
37
|
#
|
|
36
38
|
# @return [Date, Time, nil]
|
|
37
39
|
def start_date_or_time(task)
|
|
38
|
-
|
|
39
|
-
return @time_class.parse(task.start_at).localtime unless task.start_at.nil?
|
|
40
|
+
return @time_class.parse(T.must(task.start_at)).localtime unless task.start_at.nil?
|
|
40
41
|
|
|
41
42
|
return @date_class.parse(task.start_on) unless task.start_on.nil?
|
|
42
43
|
|
|
@@ -58,7 +59,7 @@ module Checkoff
|
|
|
58
59
|
#
|
|
59
60
|
# @return [Time, nil]
|
|
60
61
|
def modified_time(task)
|
|
61
|
-
@time_class.parse(task.modified_at).localtime unless task.modified_at.nil?
|
|
62
|
+
@time_class.parse(T.must(task.modified_at)).localtime unless task.modified_at.nil?
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
# @param task [Asana::Resources::Task]
|
|
@@ -10,7 +10,8 @@ module Checkoff
|
|
|
10
10
|
# @param value [Object,Boolean]
|
|
11
11
|
# @yieldreturn [generic<T>]
|
|
12
12
|
# @return [generic<T>]
|
|
13
|
-
# @sg-ignore
|
|
13
|
+
# @sg-ignore return type could not be inferred — Solargraph's generic/yield-return
|
|
14
|
+
# inference gap for #with_thread_local_variable
|
|
14
15
|
def with_thread_local_variable(name, value, &block)
|
|
15
16
|
old_value = Thread.current[name]
|
|
16
17
|
Thread.current[name] = value
|
data/lib/checkoff/my_tasks.rb
CHANGED
|
@@ -67,7 +67,9 @@ module Checkoff
|
|
|
67
67
|
sections.each_entry { |section| by_section[section_key(section.name)] = [] }
|
|
68
68
|
tasks.each do |task|
|
|
69
69
|
assignee_section = task.assignee_section
|
|
70
|
-
# @sg-ignore
|
|
70
|
+
# @sg-ignore assignee_section can be nil (task has no My Tasks section) but is
|
|
71
|
+
# dereferenced without a nil guard — looks like a real gap, flagging for the
|
|
72
|
+
# follow-up code PR
|
|
71
73
|
current_section = section_key(assignee_section.name)
|
|
72
74
|
by_section[current_section] ||= []
|
|
73
75
|
by_section[current_section] << task
|
data/lib/checkoff/portfolios.rb
CHANGED
|
@@ -47,7 +47,7 @@ module Checkoff
|
|
|
47
47
|
# @param portfolio_name [String]
|
|
48
48
|
#
|
|
49
49
|
# @return [Asana::Resources::Portfolio]
|
|
50
|
-
# @sg-ignore
|
|
50
|
+
# @sg-ignore nil check above is not flow-sensitive
|
|
51
51
|
def portfolio_or_raise(workspace_name, portfolio_name)
|
|
52
52
|
portfolio_obj = portfolio(workspace_name, portfolio_name)
|
|
53
53
|
raise "Could not find portfolio #{portfolio_name} under workspace #{workspace_name}." if portfolio_obj.nil?
|
data/lib/checkoff/projects.rb
CHANGED
|
@@ -108,8 +108,9 @@ module Checkoff
|
|
|
108
108
|
else
|
|
109
109
|
# @type [Enumerable<Asana::Resources::Project>]
|
|
110
110
|
ps = projects_by_workspace_name(workspace_name, extra_fields:)
|
|
111
|
-
# @type
|
|
112
|
-
# @sg-ignore
|
|
111
|
+
# @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
|
|
113
114
|
project = ps.find { _1.name == project_name }
|
|
114
115
|
project_by_gid(project.gid, extra_fields:) unless project.nil?
|
|
115
116
|
end
|
|
@@ -120,8 +121,8 @@ module Checkoff
|
|
|
120
121
|
# @param project_name [String,Symbol] - :my_tasks or a project name
|
|
121
122
|
# @param [Array<String>] extra_fields
|
|
122
123
|
#
|
|
123
|
-
# @sg-ignore
|
|
124
124
|
# @return [Asana::Resources::Project]
|
|
125
|
+
# @sg-ignore nil check above is not flow-sensitive
|
|
125
126
|
def project_or_raise(workspace_name, project_name, extra_fields: [])
|
|
126
127
|
p = project(workspace_name, project_name, extra_fields:)
|
|
127
128
|
raise "Could not find project #{project_name.inspect} under workspace #{workspace_name}." if p.nil?
|
data/lib/checkoff/sections.rb
CHANGED
|
@@ -80,7 +80,7 @@ module Checkoff
|
|
|
80
80
|
|
|
81
81
|
# Given a workspace name and project name, then provide a Hash of
|
|
82
82
|
# tasks with section name -> task list of the uncompleted tasks
|
|
83
|
-
# @param workspace_name [String]
|
|
83
|
+
# @param workspace_name [String, Symbol]
|
|
84
84
|
# @param project_name [String, Symbol]
|
|
85
85
|
# @param only_uncompleted [Boolean]
|
|
86
86
|
# @param extra_fields [Array<String>]
|
|
@@ -142,11 +142,10 @@ module Checkoff
|
|
|
142
142
|
# @param section_name [String, nil]
|
|
143
143
|
#
|
|
144
144
|
# @return [Array<String>]
|
|
145
|
-
# @sg-ignore
|
|
145
|
+
# @sg-ignore Task#name is nilable; task names are assumed present here
|
|
146
146
|
def section_task_names(workspace_name, project_name, section_name)
|
|
147
147
|
task_array = tasks(workspace_name, project_name, section_name)
|
|
148
|
-
|
|
149
|
-
T.cast(task_array.map(&:name), T::Array[String])
|
|
148
|
+
T.cast(task_array.map(&:name), Array)
|
|
150
149
|
end
|
|
151
150
|
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
152
151
|
|
|
@@ -184,17 +183,15 @@ module Checkoff
|
|
|
184
183
|
#
|
|
185
184
|
# @return [Asana::Resources::Section, nil]
|
|
186
185
|
def previous_section(section)
|
|
187
|
-
|
|
186
|
+
project_sections = sections_by_project_gid(section.project.fetch('gid'))
|
|
188
187
|
|
|
189
188
|
# @type [Array<Asana::Resources::Section>]
|
|
190
|
-
|
|
191
|
-
sections = sections.to_a
|
|
189
|
+
project_sections_arr = project_sections.to_a
|
|
192
190
|
|
|
193
|
-
index =
|
|
191
|
+
index = project_sections_arr.find_index { |s| s.gid == section.gid }
|
|
194
192
|
return nil if index.nil? || index.zero?
|
|
195
193
|
|
|
196
|
-
|
|
197
|
-
sections[index - 1]
|
|
194
|
+
project_sections_arr[index - 1]
|
|
198
195
|
end
|
|
199
196
|
cache_method :previous_section, SHORT_CACHE_TIME
|
|
200
197
|
|
|
@@ -295,7 +292,6 @@ module Checkoff
|
|
|
295
292
|
# @type [String, nil]
|
|
296
293
|
current_section = section_key(section_name)
|
|
297
294
|
|
|
298
|
-
# @sg-ignore
|
|
299
295
|
by_section.fetch(current_section) << task
|
|
300
296
|
end
|
|
301
297
|
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -89,7 +89,8 @@ module Checkoff
|
|
|
89
89
|
# as memberships with a separate API within a task.
|
|
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
|
|
93
94
|
def subtask_section?(subtask)
|
|
94
95
|
subtask.is_rendered_as_separator
|
|
95
96
|
end
|
data/lib/checkoff/tags.rb
CHANGED
|
@@ -79,7 +79,7 @@ module Checkoff
|
|
|
79
79
|
# @param tag_name [String]
|
|
80
80
|
#
|
|
81
81
|
# @return [Asana::Resources::Tag]
|
|
82
|
-
# @sg-ignore
|
|
82
|
+
# @sg-ignore nil check above is not flow-sensitive
|
|
83
83
|
def tag_or_raise(workspace_name, tag_name)
|
|
84
84
|
t = tag(workspace_name, tag_name)
|
|
85
85
|
|
|
@@ -114,7 +114,8 @@ module Checkoff
|
|
|
114
114
|
# @return [Hash{Symbol => Object}]
|
|
115
115
|
def build_params(options)
|
|
116
116
|
{ limit: options[:per_page], completed_since: options[:completed_since] }.reject do |_, v|
|
|
117
|
-
# @sg-ignore
|
|
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
|
|
118
119
|
v.nil? || Array(v).empty?
|
|
119
120
|
end
|
|
120
121
|
end
|
|
@@ -73,7 +73,9 @@ 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
|
|
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
|
|
77
79
|
raw_task_search(api_params, workspace_gid: workspace.gid, task_selector:,
|
|
78
80
|
extra_fields:)
|
|
79
81
|
end
|
|
@@ -186,8 +188,9 @@ module Checkoff
|
|
|
186
188
|
end
|
|
187
189
|
|
|
188
190
|
# @param [Array<String>] extra_fields
|
|
189
|
-
# @
|
|
190
|
-
# @return
|
|
191
|
+
# @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
|
|
191
194
|
def calculate_api_options(extra_fields)
|
|
192
195
|
# @type [Hash{Symbol => undefined}]
|
|
193
196
|
all_options = projects.task_options(extra_fields: ['custom_fields'] + extra_fields)
|
|
@@ -64,19 +64,19 @@ module Checkoff
|
|
|
64
64
|
# :nocov:
|
|
65
65
|
class << self
|
|
66
66
|
# @return [String]
|
|
67
|
-
# @sg-ignore
|
|
67
|
+
# @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
|
|
68
68
|
def project_name
|
|
69
69
|
ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# @sg-ignore
|
|
73
72
|
# @return [String]
|
|
73
|
+
# @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
|
|
74
74
|
def workspace_name
|
|
75
75
|
ARGV[0] || raise('Please pass workspace name as first argument')
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
# @return [Array(Symbol, Array)]
|
|
79
|
-
# @sg-ignore
|
|
79
|
+
# @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
|
|
80
80
|
def task_selector
|
|
81
81
|
task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
|
|
82
82
|
|