checkoff 0.40.0 → 0.41.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42f156ed0a4b85a8fa1b0887a74c3df016d8245293c547ea6b2ed49be7254b18
4
- data.tar.gz: 55e6d2c6c560e4c74c18c8db95f079a36637912973e849154f1f2e2bcb724f97
3
+ metadata.gz: ca8318553efc488d88a70b394e290ec8030f532ac9b3d4fa4fc67eb60849270a
4
+ data.tar.gz: cd86cc76ca2f74bed8e35212da234f5cbcad358db6a08d181f702fda149e785a
5
5
  SHA512:
6
- metadata.gz: 8e200395f12114df856c15c030dcbf4d2a8a7a77333cd431881352975655fa7d6a559626dafb15d9f93c9fbcec47e6c1d1be5afc6f7ee6e5d816d315b4566d20
7
- data.tar.gz: 6ce670c2ce1e0b783ca3d044b1e957004dc460cc0b207cda9f7ce12898021c32c94efa44bd4e1a652f76f2986c3a4d9873afbe1fe9e3eeec32df8ab4b9e40072
6
+ metadata.gz: ecde71759c34fe1cee589084e7c06577726af223cc207f3cb4236f02d3d4fba75662bf622d69de7cabc7f22acb6f041cd8ddfeb142f60aba07051b35a3ac9e00
7
+ data.tar.gz: bb87fb6a83333fc0efffcd4ce5a37da55689bd617642a15dbbb4b4a1c94cb8511aa9a2e9d11e4fc16fc6ec6631d8191976e125156a1241b8f27367fb0afd4d27
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.40.0)
15
+ checkoff (0.41.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -39,6 +39,7 @@ module Checkoff
39
39
  # Load configuration file
40
40
  class ConfigLoader
41
41
  class << self
42
+ # @return EnvFallbackConfigLoader
42
43
  def load(sym)
43
44
  yaml_result = load_yaml_file(sym)
44
45
  EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
@@ -3,28 +3,47 @@
3
3
  module Checkoff
4
4
  # Base class to evaluate a task selector function given fully evaluated arguments
5
5
  class FunctionEvaluator
6
+ # @param task_selector [Array<(Symbol, Array)>]
7
+ # @param tasks [Checkoff::Tasks]
6
8
  def initialize(task_selector:,
7
9
  tasks:)
8
10
  @task_selector = task_selector
9
11
  @tasks = tasks
10
12
  end
11
13
 
14
+ # @sg-ignore
15
+ # @param _index [Integer]
12
16
  def evaluate_arg?(_index)
13
17
  true
14
18
  end
15
19
 
20
+ # @sg-ignore
21
+ # @return [Boolean]
22
+ def matches?
23
+ raise 'Override me!'
24
+ end
25
+
16
26
  private
17
27
 
28
+ # @param object [Object]
29
+ # @param fn_name [Symbol]
18
30
  def fn?(object, fn_name)
19
31
  object.is_a?(Array) && !object.empty? && [fn_name, fn_name.to_s].include?(object[0])
20
32
  end
21
33
 
34
+ # @sg-ignore
35
+ # @param task [Asana::Resources::Task]
36
+ # @param custom_field_gid [String]
37
+ # @return [Hash]
22
38
  def pull_custom_field_or_raise(task, custom_field_gid)
39
+ # @type [Array<Hash>]
23
40
  custom_fields = task.custom_fields
24
41
  if custom_fields.nil?
25
42
  raise "Could not find custom_fields under task (was 'custom_fields' included in 'extra_fields'?)"
26
43
  end
27
44
 
45
+ # @sg-ignore
46
+ # @type [Hash, nil]
28
47
  matched_custom_field = custom_fields.find { |data| data.fetch('gid') == custom_field_gid }
29
48
  if matched_custom_field.nil?
30
49
  raise "Could not find custom field with gid #{custom_field_gid} " \
@@ -34,8 +53,12 @@ module Checkoff
34
53
  matched_custom_field
35
54
  end
36
55
 
56
+ # @return [Array<(Symbol, Array)>]
37
57
  attr_reader :task_selector
38
58
 
59
+ # @sg-ignore
60
+ # @param custom_field [Hash]
61
+ # @return [Array<String>]
39
62
  def pull_enum_values(custom_field)
40
63
  resource_subtype = custom_field.fetch('resource_subtype')
41
64
  case resource_subtype
@@ -48,6 +71,9 @@ module Checkoff
48
71
  end
49
72
  end
50
73
 
74
+ # @param custom_field [Hash]
75
+ # @param enum_value [Object, nil]
76
+ # @return [Array<String>]
51
77
  def find_gids(custom_field, enum_value)
52
78
  if enum_value.nil?
53
79
  []
@@ -58,12 +84,42 @@ module Checkoff
58
84
  end
59
85
  end
60
86
 
87
+ # @param task [Asana::Resources::Task]
88
+ # @param custom_field_gid [String]
89
+ # @return [Array<String>]
61
90
  def pull_custom_field_values_gids(task, custom_field_gid)
62
91
  custom_field = pull_custom_field_or_raise(task, custom_field_gid)
63
92
  pull_enum_values(custom_field).flat_map do |enum_value|
64
93
  find_gids(custom_field, enum_value)
65
94
  end
66
95
  end
96
+
97
+ # @sg-ignore
98
+ # @param task [Asana::Resources::Task]
99
+ # @param custom_field_name [String]
100
+ # @return [Hash, nil]
101
+ def pull_custom_field_by_name(task, custom_field_name)
102
+ custom_fields = task.custom_fields
103
+ if custom_fields.nil?
104
+ raise "custom fields not found on task - did you add 'custom_field' in your extra_fields argument?"
105
+ end
106
+
107
+ # @sg-ignore
108
+ # @type [Hash, nil]
109
+ custom_fields.find { |field| field.fetch('name') == custom_field_name }
110
+ end
111
+
112
+ # @param task [Asana::Resources::Task]
113
+ # @param custom_field_name [String]
114
+ # @return [Hash]
115
+ def pull_custom_field_by_name_or_raise(task, custom_field_name)
116
+ custom_field = pull_custom_field_by_name(task, custom_field_name)
117
+ if custom_field.nil?
118
+ raise "Could not find custom field with name #{custom_field_name} " \
119
+ "in task #{task.gid} with custom fields #{task.custom_fields}"
120
+ end
121
+ custom_field
122
+ end
67
123
  end
68
124
 
69
125
  # :and function
@@ -72,6 +128,10 @@ module Checkoff
72
128
  fn?(task_selector, :and)
73
129
  end
74
130
 
131
+ # @param _task [Asana::Resources::Task]
132
+ # @param lhs [Object]
133
+ # @param rhs [Object]
134
+ # @return [Object]
75
135
  def evaluate(_task, lhs, rhs)
76
136
  lhs && rhs
77
137
  end
@@ -83,6 +143,9 @@ module Checkoff
83
143
  fn?(task_selector, :not)
84
144
  end
85
145
 
146
+ # @param _task [Asana::Resources::Task]
147
+ # @param subvalue [Object]
148
+ # @return [Boolean]
86
149
  def evaluate(_task, subvalue)
87
150
  !subvalue
88
151
  end
@@ -94,6 +157,9 @@ module Checkoff
94
157
  fn?(task_selector, :nil?)
95
158
  end
96
159
 
160
+ # @param _task [Asana::Resources::Task]
161
+ # @param subvalue [Object]
162
+ # @return [Boolean]
97
163
  def evaluate(_task, subvalue)
98
164
  subvalue.nil?
99
165
  end
@@ -105,10 +171,15 @@ module Checkoff
105
171
  fn?(task_selector, :tag)
106
172
  end
107
173
 
174
+ # @param _index [Integer]
108
175
  def evaluate_arg?(_index)
109
176
  false
110
177
  end
111
178
 
179
+ # @sg-ignore
180
+ # @param task [Asana::Resources::Task]
181
+ # @param tag_name [String]
182
+ # @return [Boolean]
112
183
  def evaluate(task, tag_name)
113
184
  task.tags.map(&:name).include? tag_name
114
185
  end
@@ -120,6 +191,8 @@ module Checkoff
120
191
  fn?(task_selector, :due)
121
192
  end
122
193
 
194
+ # @param task [Asana::Resources::Task]
195
+ # @return [Boolean]
123
196
  def evaluate(task)
124
197
  @tasks.task_ready?(task)
125
198
  end
@@ -131,6 +204,9 @@ module Checkoff
131
204
  fn?(task_selector, :due_date_set)
132
205
  end
133
206
 
207
+ # @sg-ignore
208
+ # @param task [Asana::Resources::Task]
209
+ # @return [Boolean]
134
210
  def evaluate(task)
135
211
  !task.due_at.nil? || !task.due_on.nil?
136
212
  end
@@ -142,17 +218,16 @@ module Checkoff
142
218
  fn?(task_selector, :custom_field_value)
143
219
  end
144
220
 
221
+ # @param _index [Integer]
145
222
  def evaluate_arg?(_index)
146
223
  false
147
224
  end
148
225
 
226
+ # @param task [Asana::Resources::Task]
227
+ # @param custom_field_name [String]
228
+ # @return [String, nil]
149
229
  def evaluate(task, custom_field_name)
150
- custom_fields = task.custom_fields
151
- if custom_fields.nil?
152
- raise "custom fields not found on task - did you add 'custom_field' in your extra_fields argument?"
153
- end
154
-
155
- custom_field = custom_fields.find { |field| field.fetch('name') == custom_field_name }
230
+ custom_field = pull_custom_field_by_name(task, custom_field_name)
156
231
  return nil if custom_field.nil?
157
232
 
158
233
  custom_field['display_value']
@@ -169,6 +244,10 @@ module Checkoff
169
244
  false
170
245
  end
171
246
 
247
+ # @sg-ignore
248
+ # @param task [Asana::Resources::Task]
249
+ # @param custom_field_gid [String]
250
+ # @return [String, nil]
172
251
  def evaluate(task, custom_field_gid)
173
252
  custom_field = pull_custom_field_or_raise(task, custom_field_gid)
174
253
  custom_field['display_value']
@@ -185,6 +264,10 @@ module Checkoff
185
264
  false
186
265
  end
187
266
 
267
+ # @param task [Asana::Resources::Task]
268
+ # @param custom_field_gid [String]
269
+ # @param custom_field_values_gids [Array<String>]
270
+ # @return [Boolean]
188
271
  def evaluate(task, custom_field_gid, custom_field_values_gids)
189
272
  actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
190
273
 
@@ -204,6 +287,10 @@ module Checkoff
204
287
  false
205
288
  end
206
289
 
290
+ # @param task [Asana::Resources::Task]
291
+ # @param custom_field_gid [String]
292
+ # @param custom_field_values_gids [Array<String>]
293
+ # @return [Boolean]
207
294
  def evaluate(task, custom_field_gid, custom_field_values_gids)
208
295
  actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
209
296
 
@@ -213,8 +300,34 @@ module Checkoff
213
300
  end
214
301
  end
215
302
 
303
+ # :custom_field_less_than_n_days_from_now function
304
+ class CustomFieldLessThanNDaysFromNowFunctionEvaluator < FunctionEvaluator
305
+ def matches?
306
+ fn?(task_selector, :custom_field_less_than_n_days_from_now)
307
+ end
308
+
309
+ def evaluate_arg?(_index)
310
+ false
311
+ end
312
+
313
+ # @param task [Asana::Resources::Task]
314
+ # @param custom_field_name [String]
315
+ # @param num_days [Integer]
316
+ # @return [Boolean]
317
+ def evaluate(task, custom_field_name, num_days)
318
+ custom_field = pull_custom_field_by_name_or_raise(task, custom_field_name)
319
+
320
+ time_str = custom_field.fetch('display_value')
321
+ time = Time.parse(time_str)
322
+ n_days_from_now = (Time.now + (num_days * 24 * 60 * 60))
323
+ time < n_days_from_now
324
+ end
325
+ end
326
+
216
327
  # Evaluator task selectors against a task
217
328
  class TaskSelectorEvaluator
329
+ # @param task [Asana::Resources::Task]
330
+ # @param tasks [Checkoff::Tasks]
218
331
  def initialize(task:,
219
332
  tasks: Checkoff::Tasks.new)
220
333
  @task = task
@@ -232,12 +345,18 @@ module Checkoff
232
345
  AndFunctionEvaluator,
233
346
  DuePFunctionEvaluator,
234
347
  DueDateSetPFunctionEvaluator,
348
+ CustomFieldLessThanNDaysFromNowFunctionEvaluator,
235
349
  ].freeze
236
350
 
351
+ # @param task_selector [Array]
352
+ # @return [Boolean, Object, nil]
237
353
  def evaluate(task_selector)
238
- return true if task_selector == []
354
+ return true if task_selector.empty?
239
355
 
356
+ # @param evaluator_class [Class<FunctionEvaluator>]
240
357
  FUNCTION_EVALUTORS.each do |evaluator_class|
358
+ # @sg-ignore
359
+ # @type [FunctionEvaluator]
241
360
  evaluator = evaluator_class.new(task_selector: task_selector,
242
361
  tasks: tasks)
243
362
 
@@ -246,11 +365,15 @@ module Checkoff
246
365
  return try_this_evaluator(task_selector, evaluator)
247
366
  end
248
367
 
249
- raise "Syntax issue trying to handle #{task_selector}"
368
+ raise "Syntax issue trying to handle #{task_selector.inspect}"
250
369
  end
251
370
 
252
371
  private
253
372
 
373
+ # @sg-ignore
374
+ # @param task_selector [Array]
375
+ # @param evaluator [FunctionEvaluator]
376
+ # @return [Boolean, Object, nil]
254
377
  def try_this_evaluator(task_selector, evaluator)
255
378
  evaluated_args = task_selector[1..].map.with_index do |item, index|
256
379
  if evaluator.evaluate_arg?(index)
@@ -263,6 +386,11 @@ module Checkoff
263
386
  evaluator.evaluate(task, *evaluated_args)
264
387
  end
265
388
 
266
- attr_reader :task, :tasks, :task_selector
389
+ # @return [Asana::Resources::Task]
390
+ attr_reader :task
391
+ # @return [Checkoff::Tasks]
392
+ attr_reader :tasks
393
+ # @return [Array<(Symbol, Array)>]
394
+ attr_reader :task_selector
267
395
  end
268
396
  end
@@ -19,12 +19,9 @@ module Checkoff
19
19
  # Run task searches against the Asana API
20
20
  class TaskSearches
21
21
  MINUTE = 60
22
- # @sg-ignore
23
22
  HOUR = MINUTE * 60
24
23
  DAY = 24 * HOUR
25
- # @sg-ignore
26
24
  REALLY_LONG_CACHE_TIME = HOUR * 1
27
- # @sg-ignore
28
25
  LONG_CACHE_TIME = MINUTE * 15
29
26
  SHORT_CACHE_TIME = MINUTE
30
27
 
@@ -62,7 +59,6 @@ module Checkoff
62
59
  client: client)
63
60
  tasks.select { |task| task_selectors.filter_via_task_selector(task, task_selector) }
64
61
  end
65
- # @sg-ignore
66
62
  cache_method :task_search, LONG_CACHE_TIME
67
63
 
68
64
  private
@@ -81,11 +77,7 @@ module Checkoff
81
77
  # :nocov:
82
78
  class << self
83
79
  def run
84
- # @sg-ignore
85
- # @type [String]
86
80
  workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
87
- # @sg-ignore
88
- # @type [String]
89
81
  url = ARGV[1] || raise('Please pass task search URL as second argument')
90
82
  task_searches = Checkoff::TaskSearches.new
91
83
  task_search = task_searches.task_search(workspace_name, url)
@@ -21,6 +21,10 @@ module Checkoff
21
21
  LONG_CACHE_TIME = MINUTE * 15
22
22
  SHORT_CACHE_TIME = MINUTE
23
23
 
24
+ # @sg-ignore
25
+ # @param [Hash] config
26
+ # @param [Asana::Client] client
27
+ # @param [Checkoff::Tasks] tasks
24
28
  def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
25
29
  client: Checkoff::Clients.new(config: config).client,
26
30
  tasks: Checkoff::Tasks.new(config: config,
@@ -30,8 +34,8 @@ module Checkoff
30
34
  end
31
35
 
32
36
  # @param [Asana::Resources::Task] task
33
- # @param [Hash<Symbol, Object>] task_selector Filter based on
34
- # description. Examples: {tag: 'foo'} {:not {tag: 'foo'} (:tag 'foo')
37
+ # @param [Array<(Symbol, Array)>] task_selector Filter based on
38
+ # task details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
35
39
  # @return [Boolean]
36
40
  def filter_via_task_selector(task, task_selector)
37
41
  evaluator = TaskSelectorEvaluator.new(task: task, tasks: tasks)
@@ -40,16 +44,19 @@ module Checkoff
40
44
 
41
45
  private
42
46
 
47
+ # @return [Checkoff::Tasks]
43
48
  attr_reader :tasks
44
49
 
45
50
  # bundle exec ./task_selectors.rb
46
51
  # :nocov:
47
52
  class << self
53
+ # @sg-ignore
48
54
  # @return [String]
49
55
  def project_name
50
56
  ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
51
57
  end
52
58
 
59
+ # @sg-ignore
53
60
  # @return [String]
54
61
  def workspace_name
55
62
  ARGV[0] || raise('Please pass workspace name as first argument')
@@ -61,6 +68,7 @@ module Checkoff
61
68
  JSON.parse(task_selector_json)
62
69
  end
63
70
 
71
+ # @return [void]
64
72
  def run
65
73
  require 'checkoff/projects'
66
74
 
@@ -3,5 +3,5 @@
3
3
  # Command-line and gem client for Asana (unofficial)
4
4
  module Checkoff
5
5
  # Version of library
6
- VERSION = '0.40.0'
6
+ VERSION = '0.41.0'
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkoff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.40.0
4
+ version: 0.41.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vince Broz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-18 00:00:00.000000000 Z
11
+ date: 2023-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport