checkoff 0.39.4 → 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 +4 -4
- data/.git-hooks/pre_commit/circle_ci.rb +1 -0
- data/.git-hooks/pre_commit/punchlist.rb +3 -1
- data/.git-hooks/pre_commit/solargraph_typecheck.rb +8 -1
- data/.solargraph.yml +2 -1
- data/Gemfile +2 -3
- data/Gemfile.lock +17 -16
- data/lib/checkoff/internal/config_loader.rb +1 -0
- data/lib/checkoff/internal/search_url/simple_param_converter.rb +31 -1
- data/lib/checkoff/internal/task_selector_evaluator.rb +137 -9
- data/lib/checkoff/task_searches.rb +0 -8
- data/lib/checkoff/task_selectors.rb +10 -2
- 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: ca8318553efc488d88a70b394e290ec8030f532ac9b3d4fa4fc67eb60849270a
|
4
|
+
data.tar.gz: cd86cc76ca2f74bed8e35212da234f5cbcad358db6a08d181f702fda149e785a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecde71759c34fe1cee589084e7c06577726af223cc207f3cb4236f02d3d4fba75662bf622d69de7cabc7f22acb6f041cd8ddfeb142f60aba07051b35a3ac9e00
|
7
|
+
data.tar.gz: bb87fb6a83333fc0efffcd4ce5a37da55689bd617642a15dbbb4b4a1c94cb8511aa9a2e9d11e4fc16fc6ec6631d8191976e125156a1241b8f27367fb0afd4d27
|
@@ -10,7 +10,7 @@ module Overcommit
|
|
10
10
|
# Runs `punchlist` against any modified Ruby files.
|
11
11
|
class Punchlist < Base
|
12
12
|
# @param stdout [String]
|
13
|
-
# @return [Array<Overcommit::Hook::Message]
|
13
|
+
# @return [Array<Overcommit::Hook::Message>]
|
14
14
|
def parse_output(stdout)
|
15
15
|
stdout.split("\n").map do |line|
|
16
16
|
# @sg-ignore
|
@@ -19,12 +19,14 @@ module Overcommit
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
# @return [String]
|
22
23
|
def files_glob
|
23
24
|
"{" \
|
24
25
|
"#{applicable_files.join(',')}" \
|
25
26
|
"}"
|
26
27
|
end
|
27
28
|
|
29
|
+
# @return [Symbol, Array<Overcommit::Hook::Message>]
|
28
30
|
def run
|
29
31
|
# @sg-ignore
|
30
32
|
# @type [Overcommit::Subprocess::Result]
|
@@ -11,6 +11,7 @@ module Overcommit
|
|
11
11
|
module PreCommit
|
12
12
|
# Runs `solargraph typecheck` against any modified Ruby files.
|
13
13
|
class SolargraphTypecheck < Base
|
14
|
+
# @return [Array<String>]
|
14
15
|
def run
|
15
16
|
errors = []
|
16
17
|
|
@@ -24,8 +25,11 @@ module Overcommit
|
|
24
25
|
|
25
26
|
private
|
26
27
|
|
28
|
+
# @param file [String]
|
29
|
+
# @param errors [Array<String>]
|
30
|
+
# @return [void]
|
27
31
|
def generate_errors_for_file(file, errors)
|
28
|
-
result = execute(['bundle', 'exec', 'solargraph', 'typecheck', '--level', '
|
32
|
+
result = execute(['bundle', 'exec', 'solargraph', 'typecheck', '--level', 'strong', file])
|
29
33
|
return if result.success?
|
30
34
|
|
31
35
|
# @type [String]
|
@@ -37,6 +41,9 @@ module Overcommit
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
44
|
+
# @param file [String]
|
45
|
+
# @param error [String]
|
46
|
+
# @return [Overcommit::Hook::Message, nil]
|
40
47
|
def parse_error(file, error)
|
41
48
|
# Parse the result for the line number # @type [MatchData]
|
42
49
|
match = error.match(/^(.+?):(\d+)/)
|
data/.solargraph.yml
CHANGED
@@ -4,6 +4,7 @@ include:
|
|
4
4
|
- ".git-hooks/**/*.rb"
|
5
5
|
exclude:
|
6
6
|
- spec/**/*
|
7
|
+
- feature/**/*
|
7
8
|
- test/**/*
|
8
9
|
- vendor/**/*
|
9
10
|
- ".bundle/**/*"
|
@@ -12,7 +13,7 @@ domains: []
|
|
12
13
|
reporters:
|
13
14
|
- rubocop
|
14
15
|
- require_not_found
|
15
|
-
- typecheck:
|
16
|
+
- typecheck:strong
|
16
17
|
- update_errors
|
17
18
|
formatter:
|
18
19
|
rubocop:
|
data/Gemfile
CHANGED
@@ -19,15 +19,14 @@ gem 'overcommit', ['>=0.60.0', '<0.61.0']
|
|
19
19
|
gem 'pry'
|
20
20
|
gem 'punchlist'
|
21
21
|
gem 'rake', '~> 13.0'
|
22
|
-
|
23
|
-
gem 'rbs', ['>3.0.3']
|
22
|
+
gem 'rbs'
|
24
23
|
gem 'rubocop', ['~> 1.52']
|
25
24
|
gem 'rubocop-minitest'
|
26
25
|
gem 'rubocop-rake'
|
27
26
|
# ensure version with branch coverage
|
28
27
|
gem 'simplecov', ['>=0.18.0']
|
29
28
|
gem 'simplecov-lcov'
|
30
|
-
gem 'solargraph'
|
29
|
+
gem 'solargraph', ['>=0.49.0']
|
31
30
|
gem 'undercover'
|
32
31
|
gem 'webmock'
|
33
32
|
|
data/Gemfile.lock
CHANGED
@@ -12,7 +12,7 @@ GIT
|
|
12
12
|
PATH
|
13
13
|
remote: .
|
14
14
|
specs:
|
15
|
-
checkoff (0.
|
15
|
+
checkoff (0.41.0)
|
16
16
|
activesupport
|
17
17
|
asana (> 0.10.0)
|
18
18
|
cache_method
|
@@ -79,7 +79,7 @@ GEM
|
|
79
79
|
imagen (0.1.8)
|
80
80
|
parser (>= 2.5, != 2.5.1.1)
|
81
81
|
iniparse (1.5.0)
|
82
|
-
jaro_winkler (1.5.
|
82
|
+
jaro_winkler (1.5.6)
|
83
83
|
json (2.6.3)
|
84
84
|
jwt (2.2.3)
|
85
85
|
kramdown (2.4.0)
|
@@ -110,12 +110,12 @@ GEM
|
|
110
110
|
multi_json (1.15.0)
|
111
111
|
multi_xml (0.6.0)
|
112
112
|
multipart-post (2.1.1)
|
113
|
-
nokogiri (1.
|
114
|
-
mini_portile2 (~> 2.8.
|
113
|
+
nokogiri (1.15.2)
|
114
|
+
mini_portile2 (~> 2.8.2)
|
115
115
|
racc (~> 1.4)
|
116
|
-
nokogiri (1.
|
116
|
+
nokogiri (1.15.2-x86_64-darwin)
|
117
117
|
racc (~> 1.4)
|
118
|
-
nokogiri (1.
|
118
|
+
nokogiri (1.15.2-x86_64-linux)
|
119
119
|
racc (~> 1.4)
|
120
120
|
oauth2 (1.4.11)
|
121
121
|
faraday (>= 0.17.3, < 3.0)
|
@@ -136,11 +136,11 @@ GEM
|
|
136
136
|
public_suffix (5.0.0)
|
137
137
|
punchlist (1.3.0)
|
138
138
|
source_finder (>= 2)
|
139
|
-
racc (1.
|
139
|
+
racc (1.7.1)
|
140
140
|
rack (3.0.6.1)
|
141
141
|
rainbow (3.1.1)
|
142
142
|
rake (13.0.3)
|
143
|
-
rbs (
|
143
|
+
rbs (2.8.4)
|
144
144
|
regexp_parser (2.8.0)
|
145
145
|
reverse_markdown (2.1.1)
|
146
146
|
nokogiri
|
@@ -171,24 +171,25 @@ GEM
|
|
171
171
|
simplecov-html (0.12.3)
|
172
172
|
simplecov-lcov (0.8.0)
|
173
173
|
simplecov_json_formatter (0.1.3)
|
174
|
-
solargraph (0.
|
174
|
+
solargraph (0.49.0)
|
175
175
|
backport (~> 1.2)
|
176
176
|
benchmark
|
177
|
-
bundler (
|
177
|
+
bundler (~> 2.0)
|
178
178
|
diff-lcs (~> 1.4)
|
179
179
|
e2mmap
|
180
180
|
jaro_winkler (~> 1.5)
|
181
181
|
kramdown (~> 2.3)
|
182
182
|
kramdown-parser-gfm (~> 1.1)
|
183
183
|
parser (~> 3.0)
|
184
|
-
|
185
|
-
|
184
|
+
rbs (~> 2.0)
|
185
|
+
reverse_markdown (~> 2.0)
|
186
|
+
rubocop (~> 1.38)
|
186
187
|
thor (~> 1.0)
|
187
188
|
tilt (~> 2.0)
|
188
189
|
yard (~> 0.9, >= 0.9.24)
|
189
190
|
source_finder (3.2.1)
|
190
|
-
thor (1.2.
|
191
|
-
tilt (2.
|
191
|
+
thor (1.2.2)
|
192
|
+
tilt (2.2.0)
|
192
193
|
tomlrb (2.0.3)
|
193
194
|
tzinfo (2.0.6)
|
194
195
|
concurrent-ruby (~> 1.0)
|
@@ -225,13 +226,13 @@ DEPENDENCIES
|
|
225
226
|
pry
|
226
227
|
punchlist
|
227
228
|
rake (~> 13.0)
|
228
|
-
rbs
|
229
|
+
rbs
|
229
230
|
rubocop (~> 1.52)
|
230
231
|
rubocop-minitest
|
231
232
|
rubocop-rake
|
232
233
|
simplecov (>= 0.18.0)
|
233
234
|
simplecov-lcov
|
234
|
-
solargraph
|
235
|
+
solargraph (>= 0.49.0)
|
235
236
|
undercover
|
236
237
|
webmock
|
237
238
|
|
@@ -31,12 +31,20 @@ module Checkoff
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
# @return [String]
|
35
|
+
attr_reader :key
|
36
|
+
|
37
|
+
# @return [Array<String>]
|
38
|
+
attr_reader :values
|
35
39
|
|
36
40
|
# Inputs:
|
37
41
|
# 123_column_456 means "abc" project, "def" section
|
38
42
|
# 123 means "abc" project
|
39
43
|
# 123~456 means "abc" and "def" projects
|
44
|
+
#
|
45
|
+
# @param projects [Array<String>]
|
46
|
+
# @param sections [Array<String>]
|
47
|
+
# @return [void]
|
40
48
|
def parse_projects_and_sections(projects, sections)
|
41
49
|
single_value.split('~').each do |project_section_pair|
|
42
50
|
# @sg-ignore
|
@@ -49,6 +57,8 @@ module Checkoff
|
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
60
|
+
# @param verb [String]
|
61
|
+
# @return [Array<String>]
|
52
62
|
def convert_from_projects_and_sections(verb)
|
53
63
|
projects = []
|
54
64
|
sections = []
|
@@ -62,6 +72,7 @@ module Checkoff
|
|
62
72
|
|
63
73
|
# Handle 'any_projects.ids' search url param
|
64
74
|
class AnyProjectsIds < SimpleParam
|
75
|
+
# @return [Array<String>]
|
65
76
|
def convert
|
66
77
|
convert_from_projects_and_sections('any')
|
67
78
|
end
|
@@ -69,6 +80,7 @@ module Checkoff
|
|
69
80
|
|
70
81
|
# Handle 'not_projects.ids' search url param
|
71
82
|
class NotProjectsIds < SimpleParam
|
83
|
+
# @return [Array<String>]
|
72
84
|
def convert
|
73
85
|
convert_from_projects_and_sections('not')
|
74
86
|
end
|
@@ -76,6 +88,7 @@ module Checkoff
|
|
76
88
|
|
77
89
|
# Handle 'completion' search url param
|
78
90
|
class Completion < SimpleParam
|
91
|
+
# @return [Array<String>]
|
79
92
|
def convert
|
80
93
|
case single_value
|
81
94
|
when 'incomplete'
|
@@ -90,6 +103,7 @@ module Checkoff
|
|
90
103
|
|
91
104
|
# Handle 'not_tags.ids' search url param
|
92
105
|
class NotTagsIds < SimpleParam
|
106
|
+
# @return [Array<String>]
|
93
107
|
def convert
|
94
108
|
tag_ids = single_value.split('~')
|
95
109
|
['tags.not', tag_ids.join(',')]
|
@@ -98,6 +112,7 @@ module Checkoff
|
|
98
112
|
|
99
113
|
# handle 'subtask' search url param
|
100
114
|
class Subtask < SimpleParam
|
115
|
+
# @return [Array<[String, Boolean]>]
|
101
116
|
def convert
|
102
117
|
case single_value
|
103
118
|
when 'is_not_subtask'
|
@@ -112,6 +127,7 @@ module Checkoff
|
|
112
127
|
|
113
128
|
# Handle 'any_tags.ids' search url param
|
114
129
|
class AnyTagsIds < SimpleParam
|
130
|
+
# @return [Array<String>]
|
115
131
|
def convert
|
116
132
|
tag_ids = single_value.split('~')
|
117
133
|
['tags.any', tag_ids.join(',')]
|
@@ -120,6 +136,7 @@ module Checkoff
|
|
120
136
|
|
121
137
|
# Handle 'sort' search url param
|
122
138
|
class Sort < SimpleParam
|
139
|
+
# @return [Array<String>]
|
123
140
|
def convert
|
124
141
|
# https://developers.asana.com/reference/searchtasksforworkspace
|
125
142
|
conversion = {
|
@@ -132,6 +149,16 @@ module Checkoff
|
|
132
149
|
['sort_by', conversion.fetch(single_value)]
|
133
150
|
end
|
134
151
|
end
|
152
|
+
|
153
|
+
# Handle 'milestone' search url param
|
154
|
+
class Milestone < SimpleParam
|
155
|
+
# @return [Array<String>]
|
156
|
+
def convert
|
157
|
+
return %w[resource_subtype milestone] if single_value == 'is_milestone'
|
158
|
+
|
159
|
+
raise "Teach me how to handle #{key} = #{values}"
|
160
|
+
end
|
161
|
+
end
|
135
162
|
end
|
136
163
|
|
137
164
|
# Convert simple parameters - ones where the param name itself
|
@@ -160,6 +187,7 @@ module Checkoff
|
|
160
187
|
'any_tags.ids' => SimpleParam::AnyTagsIds,
|
161
188
|
'subtask' => SimpleParam::Subtask,
|
162
189
|
'sort' => SimpleParam::Sort,
|
190
|
+
'milestone' => SimpleParam::Milestone,
|
163
191
|
}.freeze
|
164
192
|
|
165
193
|
# https://developers.asana.com/docs/search-tasks-in-a-workspace
|
@@ -170,12 +198,14 @@ module Checkoff
|
|
170
198
|
def convert_arg(key, values)
|
171
199
|
# @type [Class<SimpleParam::SimpleParam>]
|
172
200
|
clazz = ARGS.fetch(key)
|
201
|
+
# @sg-ignore
|
173
202
|
# @type [SimpleParam::SimpleParam]
|
174
203
|
obj = clazz.new(key: key, values: values)
|
175
204
|
# @sg-ignore
|
176
205
|
obj.convert
|
177
206
|
end
|
178
207
|
|
208
|
+
# @return [Hash<String, Array<String>>]
|
179
209
|
attr_reader :simple_url_params
|
180
210
|
end
|
181
211
|
end
|
@@ -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
|
-
|
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
|
-
|
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 [
|
34
|
-
#
|
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
|
|
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.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-
|
11
|
+
date: 2023-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|