checkoff 0.16.1 → 0.18.1

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: 935a2426ba2f1d83fd784c8db089c4bf1d47d1a46e7ba67e8cd75e8bd797ee7e
4
- data.tar.gz: 040062ba44ad30695289c383f2bf25f1cd7e9f0e0b0abf289de2c58118cc271e
3
+ metadata.gz: e25d521fa8afff414681761d8adde914f090fb3d09f3972301e1b7ce7a96c2ad
4
+ data.tar.gz: 63d68ed07099060b34ea6ab89cb584782912fd72726ff593491b7c9d9a9f1e94
5
5
  SHA512:
6
- metadata.gz: b72f34b4a882d9299cddaa740a81399b8930009666dcac323dcd148954095a6f292a46ecc16829c8e129420f1335bd1e8c41453c7b4401991a15a6cae7a96b68
7
- data.tar.gz: 2d2c3fcdecf30aeedcfd718e84d874ff8596f078c2f0ae276459f0224e802a968d7c08234fe2b7d55a15261c4dcadabd5240f0c4a08a119849728a176738f688
6
+ metadata.gz: 2c2b4ba30b21e9d43c79cf5df47a28f197755c3689209f6ce9fe880802f33da1b8dd899f089160135f3d0536060eee31a33a13a7ce5987cf9108d3acaedf02f8
7
+ data.tar.gz: f7a5dfaecb8dbb18e7faf5a7d79fe3e76b42836990a1f3ad153378d67abca691d9dfb2047ae3b0565ec5d7fb397ccf5b744f9ea0b0ce346174964d0d96549001
data/.envrc CHANGED
@@ -1,3 +1,4 @@
1
1
  # shellcheck shell=bash
2
2
  PATH_add bin
3
- eval "$(with-op op-env sh -e ASANA__PERSONAL_ACCESS_TOKEN -e ASANA__DEFAULT_ASSIGNEE_GID)"
3
+ export ASANA__PERSONAL_ACCESS_TOKEN="op://Private/Asana access token - VLD read-write/token" ASANA__DEFAULT_ASSIGNEE_GID="op://Private/Asana access token - VLD read-write/default_workspace_gid" ASANA__DEFAULT_WORKSPACE_GID="op://Private/Asana access token - VLD read-write/default_workspace_gid"
4
+ direnv_load with-op op run --cache -- direnv dump
data/.gitignore CHANGED
@@ -62,4 +62,3 @@ requirements_dev.txt.installed
62
62
  /coverage/assets/
63
63
  /coverage/index.html
64
64
  /coverage/lcov/
65
- /pkg
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ rule 'MD029', style: 'ordered'
data/.mdlrc ADDED
@@ -0,0 +1 @@
1
+ style ".markdownlint_style.rb"
data/CONTRIBUTING.rst ADDED
@@ -0,0 +1,75 @@
1
+ ============
2
+ Contributing
3
+ ============
4
+
5
+ Contributions are welcome, and they are greatly appreciated! Every little bit
6
+ helps, and credit will always be given.
7
+
8
+ You can contribute in many ways:
9
+
10
+ Types of Contributions
11
+ ----------------------
12
+
13
+ Report Bugs
14
+ ~~~~~~~~~~~
15
+
16
+ Report bugs at https://github.com/apiology/checkoff/issues
17
+
18
+ If you are reporting a bug, please include:
19
+
20
+ * Your operating system name and version.
21
+ * Any details about your local setup that might be helpful in troubleshooting.
22
+ * Detailed steps to reproduce the bug.
23
+
24
+ Fix Bugs
25
+ ~~~~~~~~
26
+
27
+ Look through the GitHub issues for bugs. Anything tagged with "bug"
28
+ and "help wanted" is open to whoever wants to implement a fix for it.
29
+
30
+ Implement Features
31
+ ~~~~~~~~~~~~~~~~~~
32
+
33
+ Look through the GitHub issues for features. Anything tagged with "enhancement"
34
+ and "help wanted" is open to whoever wants to implement it.
35
+
36
+ If you have a new feature in mind, please start it by filing an issue
37
+ so we can discuss the need and approach before you invest time into
38
+ coding it.
39
+
40
+ Write Documentation
41
+ ~~~~~~~~~~~~~~~~~~~
42
+
43
+ Checkoff could always use more documentation, whether as part of
44
+ the official Checkoff docs, in docstrings, or even on the web in blog posts, articles,
45
+ and such.
46
+
47
+ Submit Feedback
48
+ ~~~~~~~~~~~~~~~
49
+
50
+ The best way to send feedback is to file an issue at
51
+ https://github.com/apiology/checkoff/issues.
52
+
53
+ If you are proposing a new feature:
54
+
55
+ * Explain in detail how it would work.
56
+ * Keep the scope as narrow as possible, to make it easier to implement.
57
+ * Remember that this is a volunteer-driven project, and that contributions
58
+ are welcome :)
59
+
60
+ Get Started!
61
+ ------------
62
+
63
+ Ready to contribute? See DEVELOPMENT.md in this directory for details
64
+ on how to set yourself up for local development.
65
+
66
+ Pull Request Guidelines
67
+ -----------------------
68
+
69
+ Before you submit a pull request, check that it meets these guidelines:
70
+
71
+ 1. The pull request should include tests.
72
+
73
+ 2. If the pull request adds functionality, the docs should be updated. Put your
74
+ new functionality into a function with a docstring, and add the feature to
75
+ the list in README.md.
data/DEVELOPMENT.md CHANGED
@@ -57,4 +57,13 @@ git log "v$(bump current)..."
57
57
  bump --tag --tag-prefix=v ${type_of_bump:?}
58
58
  rake release
59
59
  git push
60
+ git push --tags
61
+ ```
62
+
63
+
64
+ ## Developing
65
+
66
+ ```sh
67
+ bundle install
68
+ bundle exec exe/checkoff --help
60
69
  ```
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.16.1)
15
+ checkoff (0.18.1)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -22,7 +22,7 @@ PATH
22
22
  GEM
23
23
  remote: https://rubygems.org/
24
24
  specs:
25
- activesupport (6.1.5.1)
25
+ activesupport (6.1.6.1)
26
26
  concurrent-ruby (~> 1.0, >= 1.0.2)
27
27
  i18n (>= 1.6, < 2)
28
28
  minitest (>= 5.1)
@@ -33,10 +33,12 @@ GEM
33
33
  cache (0.4.1)
34
34
  cache_method (0.2.7)
35
35
  cache (>= 0.2.1)
36
+ chef-utils (17.10.0)
37
+ concurrent-ruby
36
38
  childprocess (4.0.0)
37
39
  coderay (1.1.3)
38
40
  concurrent-ruby (1.1.10)
39
- dalli (3.2.1)
41
+ dalli (3.2.2)
40
42
  docile (1.4.0)
41
43
  faraday (1.5.1)
42
44
  faraday-em_http (~> 1.0)
@@ -61,15 +63,30 @@ GEM
61
63
  faraday_middleware
62
64
  multi_json
63
65
  gli (2.21.0)
64
- i18n (1.10.0)
66
+ i18n (1.11.0)
65
67
  concurrent-ruby (~> 1.0)
66
68
  imagen (0.1.8)
67
69
  parser (>= 2.5, != 2.5.1.1)
68
70
  iniparse (1.5.0)
69
71
  jwt (2.2.3)
72
+ kramdown (2.4.0)
73
+ rexml
74
+ kramdown-parser-gfm (1.1.0)
75
+ kramdown (~> 2.0)
76
+ mdl (0.11.0)
77
+ kramdown (~> 2.3)
78
+ kramdown-parser-gfm (~> 1.1)
79
+ mixlib-cli (~> 2.1, >= 2.1.1)
80
+ mixlib-config (>= 2.2.1, < 4)
81
+ mixlib-shellout
70
82
  method_source (1.0.0)
71
- minitest (5.15.0)
83
+ minitest (5.16.2)
72
84
  minitest-profile (0.0.2)
85
+ mixlib-cli (2.1.8)
86
+ mixlib-config (3.0.27)
87
+ tomlrb
88
+ mixlib-shellout (3.2.7)
89
+ chef-utils
73
90
  mocha (1.12.0)
74
91
  multi_json (1.15.0)
75
92
  multi_xml (0.6.0)
@@ -90,7 +107,7 @@ GEM
90
107
  pry (0.14.1)
91
108
  coderay (~> 1.1)
92
109
  method_source (~> 1.0)
93
- rack (2.2.3)
110
+ rack (2.2.4)
94
111
  rainbow (3.0.0)
95
112
  rake (13.0.3)
96
113
  regexp_parser (2.1.1)
@@ -120,6 +137,7 @@ GEM
120
137
  simplecov-html (0.12.3)
121
138
  simplecov-lcov (0.8.0)
122
139
  simplecov_json_formatter (0.1.3)
140
+ tomlrb (2.0.3)
123
141
  tzinfo (2.0.4)
124
142
  concurrent-ruby (~> 1.0)
125
143
  undercover (0.4.3)
@@ -127,7 +145,7 @@ GEM
127
145
  rainbow (>= 2.1, < 4.0)
128
146
  rugged (>= 0.27, < 1.2)
129
147
  unicode-display_width (2.0.0)
130
- zeitwerk (2.5.4)
148
+ zeitwerk (2.6.0)
131
149
 
132
150
  PLATFORMS
133
151
  ruby
@@ -140,6 +158,7 @@ DEPENDENCIES
140
158
  bump
141
159
  bundler
142
160
  checkoff!
161
+ mdl
143
162
  minitest-profile
144
163
  mocha
145
164
  overcommit (>= 0.58.0)
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021, Vince Broz
3
+ Copyright (c) 2016-2022, Vince Broz
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
data/Makefile CHANGED
@@ -39,6 +39,9 @@ clear_metrics: ## remove or reset result artifacts created by tests and quality
39
39
 
40
40
  clean: clear_metrics ## remove all built artifacts
41
41
 
42
+
43
+ typecheck: ## validate types in code and configuration
44
+
42
45
  overcommit: ## run precommit quality checks
43
46
  bundle exec overcommit --run
44
47
 
data/README.md CHANGED
@@ -149,18 +149,14 @@ personal_access_token: 'some_big_long_string_from_asana.com_here'
149
149
 
150
150
  Alternately you can set environment variables to match - e.g., `ASANA__PERSONAL_ACCESS_TOKEN`
151
151
 
152
- ## Developing
152
+ ## Contributions
153
153
 
154
- ```sh
155
- bundle install
156
- bundle exec exe/checkoff --help
157
- ```
154
+ This project, as with all others, rests on the shoulders of a broad
155
+ ecosystem supported by many volunteers doing thankless work, along
156
+ with specific contributors.
158
157
 
159
- To publish new version as a maintainer:
158
+ In particular I'd like to call out:
160
159
 
161
- ```sh
162
- git log "v$(bump current)..."
163
- # Set type_of_bump to patch, minor, or major
164
- bump --tag --tag-prefix=v ${type_of_bump:?}
165
- rake release
166
- ```
160
+ * [Audrey Roy Greenfeld](https://github.com/audreyfeldroy) for the
161
+ cookiecutter tool and associated examples, which keep my many
162
+ projects building with shared boilerplate with a minimum of fuss.
data/checkoff.gemspec CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.add_development_dependency 'bump'
34
34
  spec.add_development_dependency 'bundler'
35
+ spec.add_development_dependency 'mdl'
35
36
  spec.add_development_dependency 'minitest-profile'
36
37
  spec.add_development_dependency 'mocha'
37
38
  # 0.58.0 and 0.57.0 don't seem super compatible with signatures, and
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "result": {
3
- "line": 99.78,
4
- "branch": 94.31
3
+ "line": 98.68,
4
+ "branch": 93.26
5
5
  }
6
6
  }
data/fix.sh CHANGED
@@ -284,12 +284,7 @@ ensure_python_versions() {
284
284
  }
285
285
 
286
286
  major_minor="$(cut -d. -f1-2 <<<"${ver}")"
287
- if [ "${major_minor}" == 3.6 ]
288
- then
289
- pyenv_install --patch "${ver}" < <(curl -sSL https://github.com/python/cpython/commit/8ea6353.patch\?full_index=1)
290
- else
291
- pyenv_install "${ver}"
292
- fi
287
+ pyenv_install "${ver}"
293
288
  else
294
289
  pyenv install -s "${ver}"
295
290
  fi
@@ -307,12 +302,12 @@ ensure_pyenv_virtualenvs() {
307
302
  }
308
303
 
309
304
  ensure_pip_and_wheel() {
310
- # Make sure we have a pip with the 20.3 resolver, and after the
311
- # initial bugfix release
305
+ # pip 22 seems to be better at finding pandas pre-compiled wheels
306
+ # for macOS, so let's make sure we're using at least that version
312
307
  major_pip_version=$(pip --version | cut -d' ' -f2 | cut -d '.' -f 1)
313
308
  if [[ major_pip_version -lt 21 ]]
314
309
  then
315
- pip install 'pip>=20.3.1'
310
+ pip install 'pip>=22'
316
311
  fi
317
312
  # wheel is helpful for being able to cache long package builds
318
313
  pip show wheel >/dev/null 2>&1 || pip install wheel
data/lib/checkoff/cli.rb CHANGED
@@ -61,7 +61,7 @@ module Checkoff
61
61
  to_workspace_arg:,
62
62
  to_project_arg:,
63
63
  to_section_arg:,
64
- config: Checkoff::ConfigLoader.load(:asana),
64
+ config: Checkoff::Internal::ConfigLoader.load(:asana),
65
65
  projects: Checkoff::Projects.new(config: config),
66
66
  sections: Checkoff::Sections.new(config: config),
67
67
  logger: $stderr)
@@ -122,7 +122,7 @@ module Checkoff
122
122
  class ViewSubcommand
123
123
  def initialize(workspace_name, project_name, section_name,
124
124
  task_name,
125
- config: Checkoff::ConfigLoader.load(:asana),
125
+ config: Checkoff::Internal::ConfigLoader.load(:asana),
126
126
  projects: Checkoff::Projects.new(config: config),
127
127
  sections: Checkoff::Sections.new(config: config,
128
128
  projects: projects),
@@ -201,7 +201,7 @@ module Checkoff
201
201
  # CLI subcommand that creates a task
202
202
  class QuickaddSubcommand
203
203
  def initialize(workspace_name, task_name,
204
- config: Checkoff::ConfigLoader.load(:asana),
204
+ config: Checkoff::Internal::ConfigLoader.load(:asana),
205
205
  workspaces: Checkoff::Workspaces.new(config: config),
206
206
  tasks: Checkoff::Tasks.new(config: config))
207
207
  @workspace_name = workspace_name
@@ -3,7 +3,7 @@
3
3
  # frozen_string_literal: true
4
4
 
5
5
  require 'forwardable'
6
- require_relative 'config_loader'
6
+ require_relative 'internal/config_loader'
7
7
  require 'asana'
8
8
 
9
9
  # https://developers.asana.com/docs/clients
@@ -18,7 +18,7 @@ module Checkoff
18
18
  LONG_CACHE_TIME = MINUTE * 15
19
19
  SHORT_CACHE_TIME = MINUTE
20
20
 
21
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
21
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
22
22
  asana_client_class: Asana::Client)
23
23
  @config = config
24
24
  @asana_client_class = asana_client_class
@@ -16,7 +16,7 @@ cat > "${underscored_plural_name}.rb" << EOF
16
16
 
17
17
  require 'forwardable'
18
18
  require 'cache_method'
19
- require_relative 'config_loader'
19
+ require_relative 'internal/config_loader'
20
20
  require_relative 'workspaces'
21
21
  require_relative 'clients'
22
22
 
@@ -31,7 +31,7 @@ module Checkoff
31
31
  LONG_CACHE_TIME = MINUTE * 15
32
32
  SHORT_CACHE_TIME = MINUTE
33
33
 
34
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
34
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
35
35
  workspaces: Checkoff::Workspaces.new(config: config),
36
36
  clients: Checkoff::Clients.new(config: config),
37
37
  client: clients.client)
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'forwardable'
6
6
  require 'cache_method'
7
- require_relative 'config_loader'
7
+ require_relative 'internal/config_loader'
8
8
  require_relative 'workspaces'
9
9
  require_relative 'clients'
10
10
 
@@ -20,7 +20,7 @@ module Checkoff
20
20
  LONG_CACHE_TIME = MINUTE * 15
21
21
  SHORT_CACHE_TIME = MINUTE
22
22
 
23
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
23
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
24
24
  workspaces: Checkoff::Workspaces.new(config: config),
25
25
  clients: Checkoff::Clients.new(config: config),
26
26
  client: clients.client)
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ module Checkoff
7
+ module Internal
8
+ # Use the provided config from a YAML file, and fall back to env
9
+ # variable if it's not populated for a key'
10
+ class EnvFallbackConfigLoader
11
+ def initialize(config, sym, yaml_filename)
12
+ @config = config
13
+ @envvar_prefix = sym.upcase
14
+ @yaml_filename = yaml_filename
15
+ end
16
+
17
+ def [](key)
18
+ config_value = @config[key]
19
+ return config_value unless config_value.nil?
20
+
21
+ ENV[envvar_name(key)]
22
+ end
23
+
24
+ def fetch(key)
25
+ out = self[key]
26
+ return out unless out.nil?
27
+
28
+ raise KeyError,
29
+ "Please configure either the #{key} key in #{@yaml_filename} or set #{envvar_name(key)}"
30
+ end
31
+
32
+ private
33
+
34
+ def envvar_name(key)
35
+ "#{@envvar_prefix}__#{key.upcase}"
36
+ end
37
+ end
38
+
39
+ # Load configuration file
40
+ class ConfigLoader
41
+ class << self
42
+ def load(sym)
43
+ yaml_result = load_yaml_file(sym)
44
+ EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
45
+ end
46
+
47
+ private
48
+
49
+ def load_yaml_file(sym)
50
+ filename = yaml_filename(sym)
51
+ return {} unless File.exist?(filename)
52
+
53
+ YAML.load_file(filename).with_indifferent_access
54
+ end
55
+
56
+ def yaml_filename(sym)
57
+ file = "#{sym}.yml"
58
+ File.expand_path("~/.#{file}")
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ #!/bin/bash -eu
2
+
3
+ set -o pipefail
4
+
5
+ underscored_plural_name="${1:?underscored plural name of entities minus .rb}"
6
+ class_name="${2:?class name without Checkoff:: prefix}"
7
+
8
+ cat > "${underscored_plural_name}.rb" << EOF
9
+ #!/usr/bin/env ruby
10
+
11
+ # frozen_string_literal: true
12
+
13
+ module Checkoff
14
+ module Internal
15
+ class ${class_name}
16
+ def initialize(_deps = {}); end
17
+ end
18
+ end
19
+ end
20
+ EOF
21
+
22
+ git add "${underscored_plural_name}.rb"
@@ -14,7 +14,7 @@ module Checkoff
14
14
 
15
15
  attr_reader :projects
16
16
 
17
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
17
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
18
18
  projects: Checkoff::Projects.new(config: config))
19
19
  @config = config
20
20
  @projects = projects
@@ -23,9 +23,9 @@ module Checkoff
23
23
  # Given a 'My Tasks' project object, pull all tasks, then provide
24
24
  # a Hash of tasks with section name -> task list of the
25
25
  # uncompleted tasks.
26
- def tasks_by_section_for_my_tasks(project)
26
+ def tasks_by_section_for_my_tasks(project, extra_fields: [])
27
27
  raw_tasks = projects.tasks_from_project(project,
28
- extra_fields: ['assignee_section.name'])
28
+ extra_fields: extra_fields + ['assignee_section.name'])
29
29
  active_tasks = projects.active_tasks(raw_tasks)
30
30
  by_my_tasks_section(active_tasks)
31
31
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'config_loader'
3
+ require_relative 'internal/config_loader'
4
4
  require_relative 'workspaces'
5
5
  require_relative 'clients'
6
6
  require 'cache_method'
@@ -23,7 +23,7 @@ module Checkoff
23
23
  LONG_CACHE_TIME = MINUTE * 15
24
24
  SHORT_CACHE_TIME = MINUTE * 5
25
25
 
26
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
26
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
27
27
  workspaces: Checkoff::Workspaces.new(config: config),
28
28
  clients: Checkoff::Clients.new(config: config),
29
29
  client: clients.client)
@@ -17,7 +17,7 @@ module Checkoff
17
17
 
18
18
  attr_reader :projects, :workspaces, :time, :my_tasks
19
19
 
20
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
20
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
21
21
  projects: Checkoff::Projects.new(config: config),
22
22
  workspaces: Checkoff::Workspaces.new(config: config),
23
23
  clients: Checkoff::Clients.new(config: config),
@@ -38,12 +38,12 @@ module Checkoff
38
38
 
39
39
  # Given a workspace name and project name, then provide a Hash of
40
40
  # tasks with section name -> task list of the uncompleted tasks
41
- def tasks_by_section(workspace_name, project_name)
41
+ def tasks_by_section(workspace_name, project_name, extra_fields: [])
42
42
  project = project_or_raise(workspace_name, project_name)
43
43
  if project_name == :my_tasks
44
- my_tasks.tasks_by_section_for_my_tasks(project)
44
+ my_tasks.tasks_by_section_for_my_tasks(project, extra_fields: extra_fields)
45
45
  else
46
- tasks_by_section_for_project(project)
46
+ tasks_by_section_for_project(project, extra_fields: extra_fields)
47
47
  end
48
48
  end
49
49
 
@@ -87,9 +87,9 @@ module Checkoff
87
87
 
88
88
  # Given a project object, pull all tasks, then provide a Hash of
89
89
  # tasks with section name -> task list of the uncompleted tasks
90
- def tasks_by_section_for_project(project)
90
+ def tasks_by_section_for_project(project, extra_fields: [])
91
91
  # print("project: #{project}")
92
- raw_tasks = projects.tasks_from_project(project)
92
+ raw_tasks = projects.tasks_from_project(project, extra_fields: extra_fields)
93
93
  # print("raw_tasks[0]: #{raw_tasks[0]}")
94
94
  active_tasks = projects.active_tasks(raw_tasks)
95
95
  by_section(active_tasks, project.gid)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require_relative 'config_loader'
4
+ require_relative 'internal/config_loader'
5
5
  require_relative 'projects'
6
6
 
7
7
  module Checkoff
@@ -13,7 +13,7 @@ module Checkoff
13
13
 
14
14
  extend Forwardable
15
15
 
16
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
16
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
17
17
  projects: Checkoff::Projects.new(config: config))
18
18
  @projects = projects
19
19
  end
data/lib/checkoff/tags.rb CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'forwardable'
6
6
  require 'cache_method'
7
- require_relative 'config_loader'
7
+ require_relative 'internal/config_loader'
8
8
  require_relative 'workspaces'
9
9
  require_relative 'clients'
10
10
 
@@ -20,7 +20,7 @@ module Checkoff
20
20
  LONG_CACHE_TIME = MINUTE * 15
21
21
  SHORT_CACHE_TIME = MINUTE
22
22
 
23
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
23
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
24
24
  workspaces: Checkoff::Workspaces.new(config: config),
25
25
  clients: Checkoff::Clients.new(config: config),
26
26
  client: clients.client)
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'forwardable'
6
+ require 'cache_method'
7
+ require 'json'
8
+ require_relative 'internal/config_loader'
9
+
10
+ # https://developers.asana.com/docs/task-selectors
11
+
12
+ module Checkoff
13
+ # Filter lists of tasks using declarative selectors.
14
+ class TaskSelectors
15
+ MINUTE = 60
16
+ HOUR = MINUTE * 60
17
+ DAY = 24 * HOUR
18
+ REALLY_LONG_CACHE_TIME = HOUR * 1
19
+ LONG_CACHE_TIME = MINUTE * 15
20
+ SHORT_CACHE_TIME = MINUTE
21
+
22
+ def initialize(_deps = {}); end
23
+
24
+ # @param [Hash<Symbol, Object>] task_selector Filter based on
25
+ # description. Examples: {tag: 'foo'} {:not {tag: 'foo'} (:tag 'foo')
26
+ def filter_via_task_selector(task, task_selector)
27
+ return true if task_selector == []
28
+
29
+ return !filter_via_task_selector(task, task_selector.fetch(1)) if fn?(task_selector, :not)
30
+
31
+ return filter_via_task_selector(task, task_selector.fetch(1)).nil? if fn?(task_selector, :nil?)
32
+
33
+ return contains_tag?(task, task_selector.fetch(1)) if fn?(task_selector, :tag)
34
+
35
+ return custom_field_value(task, task_selector.fetch(1)) if fn?(task_selector, :custom_field_value)
36
+
37
+ raise "Syntax issue trying to handle #{task_selector}"
38
+ end
39
+
40
+ private
41
+
42
+ def contains_tag?(task, tag_name)
43
+ task.tags.map(&:name).include? tag_name
44
+ end
45
+
46
+ def custom_field_value(task, custom_field_name)
47
+ custom_field = task.custom_fields.find { |field| field.fetch('name') == custom_field_name }
48
+ return nil if custom_field.nil?
49
+
50
+ custom_field['display_value']
51
+ end
52
+
53
+ def fn?(object, fn_name)
54
+ object.is_a?(Array) && !object.empty? && [fn_name, fn_name.to_s].include?(object[0])
55
+ end
56
+
57
+ # bundle exec ./task_selectors.rb
58
+ # :nocov:
59
+ class << self
60
+ def project_name
61
+ ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
62
+ end
63
+
64
+ def workspace_name
65
+ ARGV[0] || raise('Please pass workspace name as first argument')
66
+ end
67
+
68
+ def task_selector
69
+ task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
70
+ JSON.parse(task_selector_json)
71
+ end
72
+
73
+ def run
74
+ require 'checkoff/projects'
75
+
76
+ task_selectors = Checkoff::TaskSelectors.new
77
+ extra_fields = []
78
+ projects = Checkoff::Projects.new
79
+ project = projects.project(workspace_name, project_name)
80
+ raw_tasks = projects.tasks_from_project(project, extra_fields: extra_fields)
81
+ tasks = raw_tasks.filter { |task| task_selectors.filter_via_task_selector(task, task_selector) }
82
+ puts "Results: #{tasks}"
83
+ end
84
+ end
85
+ # :nocov:
86
+ end
87
+ end
88
+
89
+ # :nocov:
90
+ abs_program_name = File.expand_path($PROGRAM_NAME)
91
+ Checkoff::TaskSelectors.run if abs_program_name == __FILE__
92
+ # :nocov:
@@ -14,7 +14,7 @@ module Checkoff
14
14
  LONG_CACHE_TIME = MINUTE * 15
15
15
  SHORT_CACHE_TIME = MINUTE * 5
16
16
 
17
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
17
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
18
18
  sections: Checkoff::Sections.new(config: config),
19
19
  clients: Checkoff::Clients.new(config: config),
20
20
  client: clients.client,
@@ -85,6 +85,16 @@ module Checkoff
85
85
  end
86
86
 
87
87
  def incomplete_dependencies?(task)
88
+ # Avoid a reundant fetch. Unfortunately, Ruby SDK allows
89
+ # dependencies to be fetched along with other attributes--but
90
+ # then doesn't use it and does another HTTP GET! At least this
91
+ # way we can skip the extra HTTP GET in the common case when
92
+ # there are no dependencies.
93
+ #
94
+ # https://github.com/Asana/ruby-asana/issues/125
95
+ already_fetched_dependencies = task.instance_variable_get(:@dependencies)
96
+ return false unless already_fetched_dependencies.nil? || already_fetched_dependencies.size.positive?
97
+
88
98
  task.dependencies.any? do |parent_task_info|
89
99
  parent_task_gid = parent_task_info.gid
90
100
  parent_task = @asana_task.find_by_id(client, parent_task_gid,
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Command-line and gem client for Asana (unofficial)
3
4
  module Checkoff
4
5
  # Version of library
5
- VERSION = '0.16.1'
6
+ VERSION = '0.18.1'
6
7
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'forwardable'
4
4
  require 'cache_method'
5
- require_relative 'config_loader'
5
+ require_relative 'internal/config_loader'
6
6
  require_relative 'clients'
7
7
 
8
8
  # https://developers.asana.com/docs/workspaces
@@ -17,7 +17,7 @@ module Checkoff
17
17
  LONG_CACHE_TIME = MINUTE * 15
18
18
  SHORT_CACHE_TIME = MINUTE
19
19
 
20
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
20
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
21
21
  clients: Checkoff::Clients.new(config: config),
22
22
  client: clients.client)
23
23
  @config = config
data/lib/checkoff.rb CHANGED
@@ -8,3 +8,4 @@ require 'checkoff/subtasks'
8
8
  require 'checkoff/tasks'
9
9
  require 'checkoff/custom_fields'
10
10
  require 'checkoff/tags'
11
+ require 'checkoff/task_selectors'
data/rakelib/console.rake CHANGED
@@ -2,5 +2,14 @@
2
2
 
3
3
  desc 'Load up checkoff in pry'
4
4
  task :console do |_t|
5
+ puts 'Example:'
6
+ puts
7
+ puts '# https://www.rubydoc.info/github/Asana/ruby-asana/master'
8
+ puts '> client = Checkoff::Clients.new.client'
9
+ puts '# https://developers.asana.com/docs/input-output-options'
10
+ puts '> workspace_gid = ENV.fetch("ASANA__DEFAULT_WORKSPACE_GID")'
11
+ puts "> task = client.tasks.find_by_id('1199961990964812', options: { fields: ['dependencies'] })"
12
+ puts '# Asana.md#ruby-asana / API shows how to manually call endpoints with a client'
13
+ puts
5
14
  exec 'pry -I lib -r checkoff'
6
15
  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.16.1
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vince Broz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-26 00:00:00.000000000 Z
11
+ date: 2022-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mdl
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: minitest-profile
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -275,10 +289,13 @@ files:
275
289
  - ".git-hooks/pre_commit/circle_ci.rb"
276
290
  - ".gitattributes"
277
291
  - ".gitignore"
292
+ - ".markdownlint_style.rb"
293
+ - ".mdlrc"
278
294
  - ".overcommit.yml"
279
295
  - ".rubocop.yml"
280
296
  - ".yamllint.yml"
281
297
  - CODE_OF_CONDUCT.md
298
+ - CONTRIBUTING.rst
282
299
  - DEVELOPMENT.md
283
300
  - Gemfile
284
301
  - Gemfile.lock
@@ -301,14 +318,16 @@ files:
301
318
  - lib/checkoff.rb
302
319
  - lib/checkoff/cli.rb
303
320
  - lib/checkoff/clients.rb
304
- - lib/checkoff/config_loader.rb
305
321
  - lib/checkoff/create-entity.sh
306
322
  - lib/checkoff/custom_fields.rb
323
+ - lib/checkoff/internal/config_loader.rb
324
+ - lib/checkoff/internal/create-class.sh
307
325
  - lib/checkoff/my_tasks.rb
308
326
  - lib/checkoff/projects.rb
309
327
  - lib/checkoff/sections.rb
310
328
  - lib/checkoff/subtasks.rb
311
329
  - lib/checkoff/tags.rb
330
+ - lib/checkoff/task_selectors.rb
312
331
  - lib/checkoff/tasks.rb
313
332
  - lib/checkoff/version.rb
314
333
  - lib/checkoff/workspaces.rb
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'active_support/core_ext/hash'
5
-
6
- module Checkoff
7
- # Use the provided config from a YAML file, and fall back to env
8
- # variable if it's not populated for a key'
9
- class EnvFallbackConfigLoader
10
- def initialize(config, sym, yaml_filename)
11
- @config = config
12
- @envvar_prefix = sym.upcase
13
- @yaml_filename = yaml_filename
14
- end
15
-
16
- def [](key)
17
- config_value = @config[key]
18
- return config_value unless config_value.nil?
19
-
20
- ENV[envvar_name(key)]
21
- end
22
-
23
- def fetch(key)
24
- out = self[key]
25
- return out unless out.nil?
26
-
27
- raise KeyError,
28
- "Please configure either the #{key} key in #{@yaml_filename} or set #{envvar_name(key)}"
29
- end
30
-
31
- private
32
-
33
- def envvar_name(key)
34
- "#{@envvar_prefix}__#{key.upcase}"
35
- end
36
- end
37
-
38
- # Load configuration file
39
- class ConfigLoader
40
- class << self
41
- def load(sym)
42
- yaml_result = load_yaml_file(sym)
43
- EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
44
- end
45
-
46
- private
47
-
48
- def load_yaml_file(sym)
49
- filename = yaml_filename(sym)
50
- return {} unless File.exist?(filename)
51
-
52
- YAML.load_file(filename).with_indifferent_access
53
- end
54
-
55
- def yaml_filename(sym)
56
- file = "#{sym}.yml"
57
- File.expand_path("~/.#{file}")
58
- end
59
- end
60
- end
61
- end