checkoff 0.40.0 → 0.42.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42f156ed0a4b85a8fa1b0887a74c3df016d8245293c547ea6b2ed49be7254b18
4
- data.tar.gz: 55e6d2c6c560e4c74c18c8db95f079a36637912973e849154f1f2e2bcb724f97
3
+ metadata.gz: 83cd30ff493ada586596020db325eed63c9e4c24919e34c7e9926c0f9bedf51f
4
+ data.tar.gz: fee54feaa76dafc06dba69267f4ea6a7b2877e68df6af43738468e65cca552b6
5
5
  SHA512:
6
- metadata.gz: 8e200395f12114df856c15c030dcbf4d2a8a7a77333cd431881352975655fa7d6a559626dafb15d9f93c9fbcec47e6c1d1be5afc6f7ee6e5d816d315b4566d20
7
- data.tar.gz: 6ce670c2ce1e0b783ca3d044b1e957004dc460cc0b207cda9f7ce12898021c32c94efa44bd4e1a652f76f2986c3a4d9873afbe1fe9e3eeec32df8ab4b9e40072
6
+ metadata.gz: 7d4cb677d35d91830b2aa6145d40a74ebb1e34ddd9a1e17203ef62ac66afb30a3a864924462de263ff663d33cc561272143fd1b0ab45e9499c4f42e2dad97840
7
+ data.tar.gz: b074ac5d6a2675e801b52e96d8453847607bf0e523a5988a50456258f1a668319846c8c1f965eadb09409f83b82d23b1262935c2606bab175de0664c7af2077a
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.42.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)>,String]
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,21 +157,44 @@ 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
100
166
  end
101
167
 
168
+ # :equals? function
169
+ class EqualsPFunctionEvaluator < FunctionEvaluator
170
+ def matches?
171
+ fn?(task_selector, :equals?)
172
+ end
173
+
174
+ # @param _task [Asana::Resources::Task]
175
+ # @param lhs [Object]
176
+ # @param rhs [Object]
177
+ # @return [Boolean]
178
+ def evaluate(_task, lhs, rhs)
179
+ lhs == rhs
180
+ end
181
+ end
182
+
102
183
  # :tag function
103
184
  class TagPFunctionEvaluator < FunctionEvaluator
104
185
  def matches?
105
186
  fn?(task_selector, :tag)
106
187
  end
107
188
 
189
+ # @param _index [Integer]
108
190
  def evaluate_arg?(_index)
109
191
  false
110
192
  end
111
193
 
194
+ # @sg-ignore
195
+ # @param task [Asana::Resources::Task]
196
+ # @param tag_name [String]
197
+ # @return [Boolean]
112
198
  def evaluate(task, tag_name)
113
199
  task.tags.map(&:name).include? tag_name
114
200
  end
@@ -120,6 +206,8 @@ module Checkoff
120
206
  fn?(task_selector, :due)
121
207
  end
122
208
 
209
+ # @param task [Asana::Resources::Task]
210
+ # @return [Boolean]
123
211
  def evaluate(task)
124
212
  @tasks.task_ready?(task)
125
213
  end
@@ -131,6 +219,9 @@ module Checkoff
131
219
  fn?(task_selector, :due_date_set)
132
220
  end
133
221
 
222
+ # @sg-ignore
223
+ # @param task [Asana::Resources::Task]
224
+ # @return [Boolean]
134
225
  def evaluate(task)
135
226
  !task.due_at.nil? || !task.due_on.nil?
136
227
  end
@@ -142,17 +233,16 @@ module Checkoff
142
233
  fn?(task_selector, :custom_field_value)
143
234
  end
144
235
 
236
+ # @param _index [Integer]
145
237
  def evaluate_arg?(_index)
146
238
  false
147
239
  end
148
240
 
241
+ # @param task [Asana::Resources::Task]
242
+ # @param custom_field_name [String]
243
+ # @return [String, nil]
149
244
  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 }
245
+ custom_field = pull_custom_field_by_name(task, custom_field_name)
156
246
  return nil if custom_field.nil?
157
247
 
158
248
  custom_field['display_value']
@@ -169,6 +259,10 @@ module Checkoff
169
259
  false
170
260
  end
171
261
 
262
+ # @sg-ignore
263
+ # @param task [Asana::Resources::Task]
264
+ # @param custom_field_gid [String]
265
+ # @return [String, nil]
172
266
  def evaluate(task, custom_field_gid)
173
267
  custom_field = pull_custom_field_or_raise(task, custom_field_gid)
174
268
  custom_field['display_value']
@@ -185,6 +279,10 @@ module Checkoff
185
279
  false
186
280
  end
187
281
 
282
+ # @param task [Asana::Resources::Task]
283
+ # @param custom_field_gid [String]
284
+ # @param custom_field_values_gids [Array<String>]
285
+ # @return [Boolean]
188
286
  def evaluate(task, custom_field_gid, custom_field_values_gids)
189
287
  actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
190
288
 
@@ -204,6 +302,10 @@ module Checkoff
204
302
  false
205
303
  end
206
304
 
305
+ # @param task [Asana::Resources::Task]
306
+ # @param custom_field_gid [String]
307
+ # @param custom_field_values_gids [Array<String>]
308
+ # @return [Boolean]
207
309
  def evaluate(task, custom_field_gid, custom_field_values_gids)
208
310
  actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
209
311
 
@@ -213,8 +315,48 @@ module Checkoff
213
315
  end
214
316
  end
215
317
 
318
+ # :custom_field_less_than_n_days_from_now function
319
+ class CustomFieldLessThanNDaysFromNowFunctionEvaluator < FunctionEvaluator
320
+ def matches?
321
+ fn?(task_selector, :custom_field_less_than_n_days_from_now)
322
+ end
323
+
324
+ def evaluate_arg?(_index)
325
+ false
326
+ end
327
+
328
+ # @param task [Asana::Resources::Task]
329
+ # @param custom_field_name [String]
330
+ # @param num_days [Integer]
331
+ # @return [Boolean]
332
+ def evaluate(task, custom_field_name, num_days)
333
+ custom_field = pull_custom_field_by_name_or_raise(task, custom_field_name)
334
+
335
+ time_str = custom_field.fetch('display_value')
336
+ time = Time.parse(time_str)
337
+ n_days_from_now = (Time.now + (num_days * 24 * 60 * 60))
338
+ time < n_days_from_now
339
+ end
340
+ end
341
+
342
+ # String literals
343
+ class StringLiteralEvaluator < FunctionEvaluator
344
+ def matches?
345
+ task_selector.is_a?(String)
346
+ end
347
+
348
+ # @sg-ignore
349
+ # @param _task [Asana::Resources::Task]
350
+ # @return [String]
351
+ def evaluate(_task)
352
+ task_selector
353
+ end
354
+ end
355
+
216
356
  # Evaluator task selectors against a task
217
357
  class TaskSelectorEvaluator
358
+ # @param task [Asana::Resources::Task]
359
+ # @param tasks [Checkoff::Tasks]
218
360
  def initialize(task:,
219
361
  tasks: Checkoff::Tasks.new)
220
362
  @task = task
@@ -224,6 +366,7 @@ module Checkoff
224
366
  FUNCTION_EVALUTORS = [
225
367
  NotFunctionEvaluator,
226
368
  NilPFunctionEvaluator,
369
+ EqualsPFunctionEvaluator,
227
370
  TagPFunctionEvaluator,
228
371
  CustomFieldValueFunctionEvaluator,
229
372
  CustomFieldGidValueFunctionEvaluator,
@@ -232,12 +375,19 @@ module Checkoff
232
375
  AndFunctionEvaluator,
233
376
  DuePFunctionEvaluator,
234
377
  DueDateSetPFunctionEvaluator,
378
+ CustomFieldLessThanNDaysFromNowFunctionEvaluator,
379
+ StringLiteralEvaluator,
235
380
  ].freeze
236
381
 
382
+ # @param task_selector [Array]
383
+ # @return [Boolean, Object, nil]
237
384
  def evaluate(task_selector)
238
- return true if task_selector == []
385
+ return true if task_selector.empty?
239
386
 
387
+ # @param evaluator_class [Class<FunctionEvaluator>]
240
388
  FUNCTION_EVALUTORS.each do |evaluator_class|
389
+ # @sg-ignore
390
+ # @type [FunctionEvaluator]
241
391
  evaluator = evaluator_class.new(task_selector: task_selector,
242
392
  tasks: tasks)
243
393
 
@@ -246,23 +396,37 @@ module Checkoff
246
396
  return try_this_evaluator(task_selector, evaluator)
247
397
  end
248
398
 
249
- raise "Syntax issue trying to handle #{task_selector}"
399
+ raise "Syntax issue trying to handle #{task_selector.inspect}"
250
400
  end
251
401
 
252
402
  private
253
403
 
404
+ # @sg-ignore
405
+ # @param task_selector [Array]
406
+ # @param evaluator [FunctionEvaluator]
407
+ # @return [Boolean, Object, nil]
254
408
  def try_this_evaluator(task_selector, evaluator)
255
- evaluated_args = task_selector[1..].map.with_index do |item, index|
256
- if evaluator.evaluate_arg?(index)
257
- evaluate(item)
258
- else
259
- item
260
- end
261
- end
409
+ # if task_selector is an array
410
+ evaluated_args = if task_selector.is_a?(Array)
411
+ task_selector[1..].map.with_index do |item, index|
412
+ if evaluator.evaluate_arg?(index)
413
+ evaluate(item)
414
+ else
415
+ item
416
+ end
417
+ end
418
+ else
419
+ []
420
+ end
262
421
 
263
422
  evaluator.evaluate(task, *evaluated_args)
264
423
  end
265
424
 
266
- attr_reader :task, :tasks, :task_selector
425
+ # @return [Asana::Resources::Task]
426
+ attr_reader :task
427
+ # @return [Checkoff::Tasks]
428
+ attr_reader :tasks
429
+ # @return [Array<(Symbol, Array)>]
430
+ attr_reader :task_selector
267
431
  end
268
432
  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.42.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.42.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-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport