checkoff 0.41.0 → 0.43.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/Gemfile.lock +1 -1
- data/lib/checkoff/internal/config_loader.rb +17 -1
- data/lib/checkoff/internal/task_selector_evaluator.rb +45 -9
- data/lib/checkoff/task_searches.rb +25 -1
- data/lib/checkoff/tasks.rb +26 -7
- data/lib/checkoff/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af9b4c58d6440cc99eab9508632a3140d3f1519ad32fdf5f21b65833291999ba
|
4
|
+
data.tar.gz: 8d32790e9071087f948f9c1e3a3f3870f2de3dd0a1ab835e2b3727b7725d69e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf699102f03d8d6169e317f94aa88229537626ea74ec3b17446396e35fcf7e9d758d1b881d3c0d4fade4324a52e4ec64ed60d6540cac736e11440a9b4fbb1ca4
|
7
|
+
data.tar.gz: ea3952f4603dcb3344b856df43f11686589f8341763bb360d467bb99c852922b8480c3a41a86829dd25a79d3874561675e4ac4d98e09dddbc059720d86577583
|
data/Gemfile.lock
CHANGED
@@ -8,19 +8,27 @@ module Checkoff
|
|
8
8
|
# Use the provided config from a YAML file, and fall back to env
|
9
9
|
# variable if it's not populated for a key'
|
10
10
|
class EnvFallbackConfigLoader
|
11
|
+
# @param config [Hash<Symbol, Object>]
|
12
|
+
# @param sym [Symbol]
|
13
|
+
# @param yaml_filename [String]
|
11
14
|
def initialize(config, sym, yaml_filename)
|
12
15
|
@config = config
|
13
16
|
@envvar_prefix = sym.upcase
|
14
17
|
@yaml_filename = yaml_filename
|
15
18
|
end
|
16
19
|
|
20
|
+
# @param key [Symbol]
|
21
|
+
# @return [Object]
|
17
22
|
def [](key)
|
18
23
|
config_value = @config[key]
|
19
24
|
return config_value unless config_value.nil?
|
20
25
|
|
26
|
+
# @sg-ignore
|
21
27
|
ENV.fetch(envvar_name(key), nil)
|
22
28
|
end
|
23
29
|
|
30
|
+
# @param key [Symbol]
|
31
|
+
# @return [Object]
|
24
32
|
def fetch(key)
|
25
33
|
out = self[key]
|
26
34
|
return out unless out.nil?
|
@@ -31,6 +39,8 @@ module Checkoff
|
|
31
39
|
|
32
40
|
private
|
33
41
|
|
42
|
+
# @param key [Symbol]
|
43
|
+
# @return [String]
|
34
44
|
def envvar_name(key)
|
35
45
|
"#{@envvar_prefix}__#{key.upcase}"
|
36
46
|
end
|
@@ -39,7 +49,8 @@ module Checkoff
|
|
39
49
|
# Load configuration file
|
40
50
|
class ConfigLoader
|
41
51
|
class << self
|
42
|
-
# @
|
52
|
+
# @sg-ignore
|
53
|
+
# @return [Hash<Symbol, Object>]
|
43
54
|
def load(sym)
|
44
55
|
yaml_result = load_yaml_file(sym)
|
45
56
|
EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
|
@@ -47,13 +58,18 @@ module Checkoff
|
|
47
58
|
|
48
59
|
private
|
49
60
|
|
61
|
+
# @param sym [Symbol]
|
62
|
+
# @return [Hash<[String, Symbol], Object>]
|
50
63
|
def load_yaml_file(sym)
|
51
64
|
filename = yaml_filename(sym)
|
52
65
|
return {} unless File.exist?(filename)
|
53
66
|
|
67
|
+
# @sg-ignore
|
54
68
|
YAML.load_file(filename).with_indifferent_access
|
55
69
|
end
|
56
70
|
|
71
|
+
# @param sym [Symbol]
|
72
|
+
# @return [String]
|
57
73
|
def yaml_filename(sym)
|
58
74
|
file = "#{sym}.yml"
|
59
75
|
File.expand_path("~/.#{file}")
|
@@ -3,7 +3,7 @@
|
|
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)
|
6
|
+
# @param task_selector [Array<(Symbol, Array)>,String]
|
7
7
|
# @param tasks [Checkoff::Tasks]
|
8
8
|
def initialize(task_selector:,
|
9
9
|
tasks:)
|
@@ -101,7 +101,7 @@ module Checkoff
|
|
101
101
|
def pull_custom_field_by_name(task, custom_field_name)
|
102
102
|
custom_fields = task.custom_fields
|
103
103
|
if custom_fields.nil?
|
104
|
-
raise "custom fields not found on task - did you add '
|
104
|
+
raise "custom fields not found on task - did you add 'custom_fields' in your extra_fields argument?"
|
105
105
|
end
|
106
106
|
|
107
107
|
# @sg-ignore
|
@@ -165,6 +165,21 @@ module Checkoff
|
|
165
165
|
end
|
166
166
|
end
|
167
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
|
+
|
168
183
|
# :tag function
|
169
184
|
class TagPFunctionEvaluator < FunctionEvaluator
|
170
185
|
def matches?
|
@@ -324,6 +339,20 @@ module Checkoff
|
|
324
339
|
end
|
325
340
|
end
|
326
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
|
+
|
327
356
|
# Evaluator task selectors against a task
|
328
357
|
class TaskSelectorEvaluator
|
329
358
|
# @param task [Asana::Resources::Task]
|
@@ -337,6 +366,7 @@ module Checkoff
|
|
337
366
|
FUNCTION_EVALUTORS = [
|
338
367
|
NotFunctionEvaluator,
|
339
368
|
NilPFunctionEvaluator,
|
369
|
+
EqualsPFunctionEvaluator,
|
340
370
|
TagPFunctionEvaluator,
|
341
371
|
CustomFieldValueFunctionEvaluator,
|
342
372
|
CustomFieldGidValueFunctionEvaluator,
|
@@ -346,6 +376,7 @@ module Checkoff
|
|
346
376
|
DuePFunctionEvaluator,
|
347
377
|
DueDateSetPFunctionEvaluator,
|
348
378
|
CustomFieldLessThanNDaysFromNowFunctionEvaluator,
|
379
|
+
StringLiteralEvaluator,
|
349
380
|
].freeze
|
350
381
|
|
351
382
|
# @param task_selector [Array]
|
@@ -375,13 +406,18 @@ module Checkoff
|
|
375
406
|
# @param evaluator [FunctionEvaluator]
|
376
407
|
# @return [Boolean, Object, nil]
|
377
408
|
def try_this_evaluator(task_selector, evaluator)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
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
|
385
421
|
|
386
422
|
evaluator.evaluate(task, *evaluated_args)
|
387
423
|
end
|
@@ -27,6 +27,17 @@ module Checkoff
|
|
27
27
|
|
28
28
|
include Asana::Resources::ResponseHelper
|
29
29
|
|
30
|
+
# @!parse
|
31
|
+
# extend CacheMethod::ClassMethods
|
32
|
+
|
33
|
+
# @param config [Hash<Symbol, Object>]
|
34
|
+
# @param workspaces [Checkoff::Workspaces]
|
35
|
+
# @param task_selectors [Checkoff::TaskSelectors]
|
36
|
+
# @param projects [Checkoff::Projects]
|
37
|
+
# @param clients [Checkoff::Clients]
|
38
|
+
# @param client [Asana::Client]
|
39
|
+
# @param search_url_parser [Checkoff::Internal::SearchUrl::Parser]
|
40
|
+
# @param asana_resources_collection_class [Class<Asana::Resources::Collection>]
|
30
41
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
31
42
|
workspaces: Checkoff::Workspaces.new(config: config),
|
32
43
|
task_selectors: Checkoff::TaskSelectors.new(config: config),
|
@@ -49,6 +60,7 @@ module Checkoff
|
|
49
60
|
# @return [Array<Asana::Resources::Task>]
|
50
61
|
def task_search(workspace_name, url, extra_fields: [])
|
51
62
|
workspace = workspaces.workspace_or_raise(workspace_name)
|
63
|
+
# @sg-ignore
|
52
64
|
api_params, task_selector = @search_url_parser.convert_params(url)
|
53
65
|
path = "/workspaces/#{workspace.gid}/tasks/search"
|
54
66
|
options = calculate_api_options(extra_fields)
|
@@ -76,8 +88,13 @@ module Checkoff
|
|
76
88
|
# bundle exec ./task_searches.rb
|
77
89
|
# :nocov:
|
78
90
|
class << self
|
91
|
+
# @return [void]
|
79
92
|
def run
|
93
|
+
# @sg-ignore
|
94
|
+
# @type [String]
|
80
95
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
96
|
+
# @sg-ignore
|
97
|
+
# @type [String]
|
81
98
|
url = ARGV[1] || raise('Please pass task search URL as second argument')
|
82
99
|
task_searches = Checkoff::TaskSearches.new
|
83
100
|
task_search = task_searches.task_search(workspace_name, url)
|
@@ -86,7 +103,14 @@ module Checkoff
|
|
86
103
|
end
|
87
104
|
# :nocov:
|
88
105
|
|
89
|
-
|
106
|
+
# @return [Checkoff::TaskSelectors]
|
107
|
+
attr_reader :task_selectors
|
108
|
+
# @return [Checkoff::Projects]
|
109
|
+
attr_reader :projects
|
110
|
+
# @return [Checkoff::Workspaces]
|
111
|
+
attr_reader :workspaces
|
112
|
+
# @return [Asana::Client]
|
113
|
+
attr_reader :client
|
90
114
|
end
|
91
115
|
end
|
92
116
|
|
data/lib/checkoff/tasks.rb
CHANGED
@@ -4,25 +4,25 @@
|
|
4
4
|
|
5
5
|
require_relative 'sections'
|
6
6
|
require_relative 'workspaces'
|
7
|
+
require_relative 'internal/config_loader'
|
7
8
|
require 'asana'
|
8
9
|
|
9
10
|
module Checkoff
|
10
11
|
# Pull tasks from Asana
|
11
12
|
class Tasks
|
12
13
|
MINUTE = 60
|
13
|
-
# @sg-ignore
|
14
14
|
HOUR = MINUTE * 60
|
15
15
|
DAY = 24 * HOUR
|
16
16
|
REALLY_LONG_CACHE_TIME = HOUR
|
17
|
-
# @sg-ignore
|
18
17
|
LONG_CACHE_TIME = MINUTE * 15
|
19
|
-
# @sg-ignore
|
20
18
|
SHORT_CACHE_TIME = MINUTE * 5
|
21
19
|
|
20
|
+
# @param config [Hash<Symbol, Object>]
|
22
21
|
# @param client [Asana::Client]
|
23
22
|
# @param workspaces [Checkoff::Workspaces]
|
24
|
-
# @param time_class [Class<Time>]
|
25
23
|
# @param sections [Checkoff::Sections]
|
24
|
+
# @param time_class [Class<Time>]
|
25
|
+
# @param asana_task [Class<Asana::Resources::Task>]
|
26
26
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
27
27
|
client: Checkoff::Clients.new(config: config).client,
|
28
28
|
workspaces: Checkoff::Workspaces.new(config: config,
|
@@ -39,8 +39,10 @@ module Checkoff
|
|
39
39
|
@workspaces = workspaces
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# @param task [Asana::Resources::Task]
|
43
|
+
# @param ignore_dependencies [Boolean]
|
44
|
+
def task_ready?(task, ignore_dependencies: false)
|
45
|
+
return false if !ignore_dependencies && incomplete_dependencies?(task)
|
44
46
|
|
45
47
|
due = due_time(task)
|
46
48
|
|
@@ -50,11 +52,19 @@ module Checkoff
|
|
50
52
|
end
|
51
53
|
|
52
54
|
# Pull a specific task by name
|
55
|
+
# @param workspace_name [String]
|
56
|
+
# @param project_name [String, Symbol]
|
57
|
+
# @param section_name [String, nil, Symbol]
|
58
|
+
# @param task_name [String]
|
59
|
+
# @param only_uncompleted [Boolean]
|
60
|
+
# @param extra_fields [Array<String>]
|
61
|
+
# @sg-ignore
|
53
62
|
# @return [Asana::Resources::Task, nil]
|
54
63
|
def task(workspace_name, project_name, task_name,
|
55
64
|
section_name: :unspecified,
|
56
65
|
only_uncompleted: true,
|
57
66
|
extra_fields: [])
|
67
|
+
# @sg-ignore
|
58
68
|
tasks = tasks_from_section(workspace_name,
|
59
69
|
project_name,
|
60
70
|
section_name: section_name,
|
@@ -74,6 +84,7 @@ module Checkoff
|
|
74
84
|
client.tasks.find_by_id(task_gid, options: options)
|
75
85
|
end
|
76
86
|
|
87
|
+
# @param name [String]
|
77
88
|
# @param workspace_gid [String]
|
78
89
|
# @param assignee_gid [String]
|
79
90
|
# @return [Asana::Resources::Task]
|
@@ -85,13 +96,19 @@ module Checkoff
|
|
85
96
|
workspace: workspace_gid, name: name)
|
86
97
|
end
|
87
98
|
|
88
|
-
#
|
99
|
+
# @param task [Asana::Resources::Task]
|
100
|
+
# @return [String] end-user URL to the task in question
|
89
101
|
def url_of_task(task)
|
90
102
|
"https://app.asana.com/0/0/#{task.gid}/f"
|
91
103
|
end
|
92
104
|
|
93
105
|
private
|
94
106
|
|
107
|
+
# @param workspace_name [String]
|
108
|
+
# @param project_name [String, Symbol]
|
109
|
+
# @param section_name [String, nil, :unspecified]
|
110
|
+
# @param only_uncompleted [Boolean]
|
111
|
+
# @param extra_fields [Array<String>]
|
95
112
|
# @return [Array<Asana::Resources::Task>]
|
96
113
|
def tasks_from_section(workspace_name, project_name,
|
97
114
|
section_name:,
|
@@ -109,6 +126,7 @@ module Checkoff
|
|
109
126
|
end
|
110
127
|
end
|
111
128
|
|
129
|
+
# @return [Asana::Client]
|
112
130
|
attr_reader :client
|
113
131
|
|
114
132
|
# @return [Checkoff::Projects]
|
@@ -131,6 +149,7 @@ module Checkoff
|
|
131
149
|
nil
|
132
150
|
end
|
133
151
|
|
152
|
+
# @param task [Asana::Resources::Task]
|
134
153
|
def incomplete_dependencies?(task)
|
135
154
|
# Avoid a redundant fetch. Unfortunately, Ruby SDK allows
|
136
155
|
# dependencies to be fetched along with other attributes--but
|
data/lib/checkoff/version.rb
CHANGED
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.
|
4
|
+
version: 0.43.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-
|
11
|
+
date: 2023-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|