checkoff 0.39.4 → 0.41.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|