checkoff 0.40.0 → 0.41.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: 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