checkoff 0.10.0 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 121d92b56f0936e25ff98efd5f601e9fea6cf6a71191f0ba7a2803785bcc1feb
4
- data.tar.gz: e3b871d18fa8263e0d61df92e22b59a369b5f78e527a8da57ac0fb472db5c34d
3
+ metadata.gz: 0f9082aa251f94c438c29019a88db01de1732564c237bb3dff8d4aef6ce8eea6
4
+ data.tar.gz: 90cc397c4381839ddc1ba55c0096e84e7baf735653d2aa46267a1e52dd684603
5
5
  SHA512:
6
- metadata.gz: '0862ee2cd9aababfb015020a1c55e6f7a5027bc001b2ec6317b661b7407fcb095bb981f7d44ad251a50166edf674bc798f3629b986cece29eb30286f7be09fd0'
7
- data.tar.gz: ebaed1748125699b93f3804ee79d2c33fdf560aa232e904a69fb1e55ad3520802096b35ea9a921e5b70c67f37a6c78ce3444606333e6ed81ca6f7e4912ae61b6
6
+ metadata.gz: 21438c7ac67f3e782a1fee1c7adae43baa13df126904668dc6784cfab27b57d4abea67380af34560ff834e6b6a00a1fcd50ce12f171781991c273cc8bd9ce10e
7
+ data.tar.gz: 863441bc08bd83be385bb8b5806ad37a720a421cdfab44efcb9c31e528b649ee11a6e0d392401e4fb2931ff4c6f4a7934d2b652df4fb200c8286bc758c9ad7ba
data/.circleci/config.yml CHANGED
@@ -19,7 +19,7 @@ commands:
19
19
  export PATH="${HOME}/.pyenv/bin:${PATH}"
20
20
  export PATH="${HOME}/.rbenv/bin:${HOME}/.rbenv/shims:${PATH}"
21
21
  export PATH="${HOME}/project/node_modules/.bin:${PATH}"
22
- eval "$(pyenv init -)"
22
+ eval "$(pyenv init --path)"
23
23
  eval "$(pyenv virtualenv-init -)"
24
24
  eval "$(rbenv init -)"
25
25
  export BUNDLE_PATH=vendor/bundle
@@ -97,11 +97,12 @@ jobs:
97
97
  steps:
98
98
  - set_up_environment
99
99
  - run_with_languages:
100
- label: Run tests
100
+ label: Test
101
101
  command: |
102
- make test
102
+ bundle exec rake citest
103
103
  # https://github.com/bluelabsio/records-mover/blob/master/Makefile#L25
104
104
  git status --porcelain coverage/.last_run.json
105
+ git diff coverage/.last_run.json
105
106
  test -z "$(git status --porcelain coverage/.last_run.json)"
106
107
 
107
108
  workflows:
data/.envrc CHANGED
@@ -1,2 +1,2 @@
1
1
  PATH_add bin
2
- eval $(with-op op-env sh -e ASANA__PERSONAL_ACCESS_TOKEN)
2
+ eval $(with-op op-env sh -e ASANA__PERSONAL_ACCESS_TOKEN -e ASANA__DEFAULT_ASSIGNEE_GID)
data/.gitignore CHANGED
@@ -58,4 +58,5 @@ requirements_dev.txt.installed
58
58
  /coverage/.resultset.json.lock
59
59
  /coverage/assets/
60
60
  /coverage/index.html
61
+ /coverage/lcov/
61
62
  /pkg
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- checkoff (0.10.0)
4
+ checkoff (0.11.0)
5
5
  activesupport
6
6
  asana (> 0.10.0)
7
7
  cache_method
8
8
  dalli
9
+ gli
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
@@ -27,15 +28,19 @@ GEM
27
28
  cache_method (0.2.7)
28
29
  cache (>= 0.2.1)
29
30
  childprocess (4.0.0)
30
- concurrent-ruby (1.1.8)
31
+ concurrent-ruby (1.1.9)
31
32
  dalli (2.7.11)
32
- docile (1.3.4)
33
- faraday (1.4.1)
33
+ docile (1.4.0)
34
+ faraday (1.4.2)
35
+ faraday-em_http (~> 1.0)
36
+ faraday-em_synchrony (~> 1.0)
34
37
  faraday-excon (~> 1.1)
35
38
  faraday-net_http (~> 1.0)
36
39
  faraday-net_http_persistent (~> 1.1)
37
40
  multipart-post (>= 1.2, < 3)
38
41
  ruby2_keywords (>= 0.0.4)
42
+ faraday-em_http (1.0.0)
43
+ faraday-em_synchrony (1.0.0)
39
44
  faraday-excon (1.1.0)
40
45
  faraday-net_http (1.0.1)
41
46
  faraday-net_http_persistent (1.1.0)
@@ -44,6 +49,7 @@ GEM
44
49
  faraday_middleware-multi_json (0.0.6)
45
50
  faraday_middleware
46
51
  multi_json
52
+ gli (2.20.0)
47
53
  i18n (1.8.10)
48
54
  concurrent-ruby (~> 1.0)
49
55
  imagen (0.1.8)
@@ -62,9 +68,10 @@ GEM
62
68
  multi_json (~> 1.3)
63
69
  multi_xml (~> 0.5)
64
70
  rack (>= 1.2, < 3)
65
- overcommit (0.57.0)
71
+ overcommit (0.58.0)
66
72
  childprocess (>= 0.6.3, < 5)
67
73
  iniparse (~> 1.4)
74
+ rexml (~> 3.2)
68
75
  parallel (1.20.1)
69
76
  parser (3.0.1.1)
70
77
  ast (~> 2.4.1)
@@ -73,7 +80,7 @@ GEM
73
80
  rake (13.0.3)
74
81
  regexp_parser (2.1.1)
75
82
  rexml (3.2.5)
76
- rubocop (1.14.0)
83
+ rubocop (1.15.0)
77
84
  parallel (~> 1.10)
78
85
  parser (>= 3.0.0.0)
79
86
  rainbow (>= 2.2.2, < 4.0)
@@ -96,6 +103,7 @@ GEM
96
103
  simplecov-html (~> 0.11)
97
104
  simplecov_json_formatter (~> 0.1)
98
105
  simplecov-html (0.12.3)
106
+ simplecov-lcov (0.8.0)
99
107
  simplecov_json_formatter (0.1.3)
100
108
  tzinfo (2.0.4)
101
109
  concurrent-ruby (~> 1.0)
@@ -118,13 +126,14 @@ DEPENDENCIES
118
126
  checkoff!
119
127
  minitest-profile
120
128
  mocha
121
- overcommit
129
+ overcommit (>= 0.58.0)
122
130
  rake (~> 13.0)
123
131
  rubocop
124
132
  rubocop-minitest
125
133
  rubocop-rake
126
134
  simplecov
135
+ simplecov-lcov
127
136
  undercover
128
137
 
129
138
  BUNDLED WITH
130
- 2.2.11
139
+ 2.2.19
data/Makefile CHANGED
@@ -1,4 +1,4 @@
1
- .PHONY: clean test help quality localtest spec feature
1
+ .PHONY: clean test help quality localtest
2
2
  .DEFAULT_GOAL := default
3
3
 
4
4
  define PRINT_HELP_PYSCRIPT
@@ -32,7 +32,10 @@ Gemfile.lock.installed: Gemfile.lock
32
32
 
33
33
  bundle_install: Gemfile.lock.installed ## Install Ruby dependencies
34
34
 
35
- clean: ## remove all built artifacts
35
+ clear_metrics: ## remove or reset result artifacts created by tests and quality tools
36
+ bundle exec rake clear_metrics
37
+
38
+ clean: clear_metrics ## remove all built artifacts
36
39
 
37
40
  quality: ## run precommit quality checks
38
41
  bundle exec overcommit --run
@@ -40,7 +43,8 @@ quality: ## run precommit quality checks
40
43
  test: ## Run lower-level tests
41
44
  @bundle exec rake test
42
45
 
43
- localtest: test quality feature ## run default local actions
46
+ localtest: ## run default local actions
47
+ @bundle exec rake localtest
44
48
 
45
49
  update_from_cookiecutter: ## Bring in changes from template project used to create this repo
46
50
  bundle exec overcommit --uninstall
data/checkoff.gemspec CHANGED
@@ -26,16 +26,21 @@ Gem::Specification.new do |spec|
26
26
  spec.add_runtime_dependency 'asana', '>0.10.0'
27
27
  spec.add_runtime_dependency 'cache_method'
28
28
  spec.add_runtime_dependency 'dalli'
29
+ spec.add_runtime_dependency 'gli'
29
30
 
30
31
  spec.add_development_dependency 'bump'
31
32
  spec.add_development_dependency 'bundler'
32
33
  spec.add_development_dependency 'minitest-profile'
33
34
  spec.add_development_dependency 'mocha'
34
- spec.add_development_dependency 'overcommit'
35
+ # 0.58.0 and 0.57.0 don't seem super compatible with signatures, and
36
+ # magit doesn't seem to want to use the bundled version at the moment,
37
+ # so let's favor the more recent version...
38
+ spec.add_development_dependency 'overcommit', ['>=0.58.0']
35
39
  spec.add_development_dependency 'rake', '~> 13.0'
36
40
  spec.add_development_dependency 'rubocop'
37
41
  spec.add_development_dependency 'rubocop-minitest'
38
42
  spec.add_development_dependency 'rubocop-rake'
39
43
  spec.add_development_dependency 'simplecov'
44
+ spec.add_development_dependency 'simplecov-lcov'
40
45
  spec.add_development_dependency 'undercover'
41
46
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "result": {
3
- "line": 93.15,
4
- "branch": 61.66
3
+ "line": 94.61,
4
+ "branch": 71.42
5
5
  }
6
6
  }
data/exe/checkoff CHANGED
@@ -4,5 +4,4 @@
4
4
  require 'checkoff/cli'
5
5
 
6
6
  CacheMethod.config.storage = Dalli::Client.new('memcached:11211')
7
- cli = Checkoff::CLI.new
8
- puts cli.run(ARGV)
7
+ exit(Checkoff::CheckoffGLIApp.run(ARGV))
data/fix.sh CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  set -o pipefail
4
4
 
5
+ apt_upgraded=0
6
+
7
+ update_apt() {
8
+ if [ "${apt_upgraded}" = 0 ]
9
+ then
10
+ sudo apt-get update -y
11
+ apt_upgraded=1
12
+ fi
13
+ }
14
+
5
15
  install_rbenv() {
6
16
  if [ "$(uname)" == "Darwin" ]
7
17
  then
@@ -77,6 +87,8 @@ ensure_dev_library() {
77
87
 
78
88
  ensure_ruby_build_requirements() {
79
89
  ensure_dev_library readline/readline.h readline libreadline-dev
90
+ ensure_dev_library zlib.h zlib zlib1g-dev
91
+ ensure_dev_library openssl/ssl.h openssl libssl-dev
80
92
  }
81
93
 
82
94
  # You can find out which feature versions are still supported / have
@@ -92,7 +104,17 @@ ensure_ruby_versions() {
92
104
 
93
105
  for ver in $ruby_versions
94
106
  do
95
- rbenv install -s "${ver}"
107
+ # These CFLAGS can be retired once 2.6.7 is no longer needed :
108
+ #
109
+ # https://github.com/rbenv/ruby-build/issues/1747
110
+ # https://github.com/rbenv/ruby-build/issues/1489
111
+ # https://bugs.ruby-lang.org/issues/17777
112
+ if [ "${ver}" == 2.6.7 ]
113
+ then
114
+ CFLAGS="-Wno-error=implicit-function-declaration" rbenv install -s "${ver}"
115
+ else
116
+ rbenv install -s "${ver}"
117
+ fi
96
118
  done
97
119
  }
98
120
 
@@ -101,7 +123,17 @@ ensure_bundle() {
101
123
  #
102
124
  # https://app.circleci.com/pipelines/github/apiology/source_finder/21/workflows/88db659f-a4f4-4751-abc0-46f5929d8e58/jobs/107
103
125
  set_rbenv_env_variables
104
- bundle --version >/dev/null 2>&1 || gem install bundler
126
+ bundle --version >/dev/null 2>&1 || gem install --no-document bundler
127
+ bundler_version=$(bundle --version | cut -d ' ' -f3)
128
+ bundler_version_major=$(cut -d. -f1 <<< "${bundler_version}")
129
+ bundler_version_minor=$(cut -d. -f2 <<< "${bundler_version}")
130
+ # Version 2.1 of bundler seems to have some issues with nokogiri:
131
+ #
132
+ # https://app.asana.com/0/1107901397356088/1199504270687298
133
+ if [ "${bundler_version_major}" == 2 ] && [ "${bundler_version_minor}" -lt 2 ]
134
+ then
135
+ gem install --no-document bundler
136
+ fi
105
137
  make bundle_install
106
138
  # https://bundler.io/v2.0/bundle_lock.html#SUPPORTING-OTHER-PLATFORMS
107
139
  #
@@ -138,8 +170,9 @@ WARNING: seems you still have not added 'pyenv' to the load path.
138
170
  # Load pyenv automatically by adding
139
171
  # the following to ~/.bashrc:
140
172
 
141
- export PATH="$HOME/.pyenv/bin:$PATH"
142
- eval "$(pyenv init -)"
173
+ export PYENV_ROOT="${HOME}/.pyenv"
174
+ export PATH="${PYENV_ROOT}/bin:$PATH"
175
+ eval "$(pyenv init --path)"
143
176
  eval "$(pyenv virtualenv-init -)"
144
177
  EOF
145
178
  fi
@@ -153,8 +186,9 @@ set_pyenv_env_variables() {
153
186
  #
154
187
  # https://app.circleci.com/pipelines/github/apiology/cookiecutter-pypackage/15/workflows/10506069-7662-46bd-b915-2992db3f795b/jobs/15
155
188
  set +u
156
- export PATH="${HOME}/.pyenv/bin:$PATH"
157
- eval "$(pyenv init -)"
189
+ export PYENV_ROOT="${HOME}/.pyenv"
190
+ export PATH="${PYENV_ROOT}/bin:$PATH"
191
+ eval "$(pyenv init --path)"
158
192
  eval "$(pyenv virtualenv-init -)"
159
193
  set -u
160
194
  }
@@ -179,7 +213,7 @@ install_package() {
179
213
  HOMEBREW_NO_AUTO_UPDATE=1 brew install "${homebrew_package}"
180
214
  elif type apt-get >/dev/null 2>&1
181
215
  then
182
- sudo apt-get update -y
216
+ update_apt
183
217
  sudo apt-get install -y "${apt_package}"
184
218
  else
185
219
  >&2 echo "Teach me how to install packages on this plaform"
@@ -192,6 +226,7 @@ ensure_python_build_requirements() {
192
226
  ensure_dev_library bzlib.h bzip2 libbz2-dev
193
227
  ensure_dev_library openssl/ssl.h openssl libssl-dev
194
228
  ensure_dev_library ffi.h libffi libffi-dev
229
+ ensure_dev_library sqlite3.h sqlite3 libsqlite3-dev
195
230
  }
196
231
 
197
232
  # You can find out which feature versions are still supported / have
data/lib/checkoff/cli.rb CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'ostruct'
6
6
  require 'dalli'
7
+ require 'gli'
7
8
  require 'cache_method'
8
9
  require_relative 'workspaces'
9
10
  require_relative 'projects'
@@ -11,41 +12,44 @@ require_relative 'tasks'
11
12
  require_relative 'sections'
12
13
 
13
14
  module Checkoff
14
- # Provide ability for CLI to pull Asana items
15
- # rubocop:disable Metrics/ClassLength
16
- class CLI
17
- attr_reader :sections, :stderr
18
-
19
- def initialize(config: Checkoff::ConfigLoader.load(:asana),
20
- workspaces: Checkoff::Workspaces.new(config: config),
15
+ # CLI subcommand that shows tasks in JSON form
16
+ class ViewSubcommand
17
+ def initialize(workspace_name, project_name, section_name,
18
+ task_name,
19
+ config: Checkoff::ConfigLoader.load(:asana),
21
20
  projects: Checkoff::Projects.new(config: config),
22
21
  sections: Checkoff::Sections.new(config: config,
23
22
  projects: projects),
24
- tasks: Checkoff::Tasks.new(config: config),
25
- stderr: $stderr,
26
- kernel: Kernel)
27
- @workspaces = workspaces
28
- @projects = projects
23
+ tasks: Checkoff::Tasks.new(config: config,
24
+ sections: sections),
25
+ stderr: $stderr)
26
+ @workspace_name = workspace_name
27
+ @stderr = stderr
28
+ validate_and_assign_project_name(project_name)
29
+ @section_name = section_name
30
+ @task_name = task_name
29
31
  @sections = sections
30
32
  @tasks = tasks
31
- @kernel = kernel
32
- @stderr = stderr
33
33
  end
34
34
 
35
- def task_to_hash(task)
36
- task_out = {
37
- name: task.name,
38
- }
39
- if task.due_on
40
- task_out[:due] = task.due_on
41
- elsif task.due_at
42
- task_out[:due] = task.due_at
35
+ def run
36
+ if section_name.nil?
37
+ run_on_project(workspace_name, project_name)
38
+ elsif task_name.nil?
39
+ run_on_section(workspace_name, project_name, section_name)
40
+ else
41
+ run_on_task(workspace_name, project_name, section_name, task_name)
43
42
  end
44
- task_out
45
43
  end
46
44
 
47
- def tasks_to_hash(tasks)
48
- tasks.map { |task| task_to_hash(task) }
45
+ private
46
+
47
+ def validate_and_assign_project_name(project_name)
48
+ @project_name = if project_name.start_with? ':'
49
+ project_name[1..].to_sym
50
+ else
51
+ project_name
52
+ end
49
53
  end
50
54
 
51
55
  def run_on_project(workspace, project)
@@ -63,80 +67,89 @@ module Checkoff
63
67
  tasks_to_hash(tasks).to_json
64
68
  end
65
69
 
66
- def quickadd(workspace_name, task_name)
67
- workspace = @workspaces.workspace_by_name(workspace_name)
68
- @tasks.add_task(task_name,
69
- workspace_gid: workspace.gid)
70
+ def run_on_task(workspace, project, section, task_name)
71
+ section = nil if section == ''
72
+ task = tasks.task(workspace, project, task_name, section_name: section)
73
+ task_to_hash(task).to_json
70
74
  end
71
75
 
72
- def validate_args!(args)
73
- return unless args.length < 2 || !%w[view quickadd].include?(args[0])
74
-
75
- output_help
76
- exit(1)
76
+ def task_to_hash(task)
77
+ task_out = {
78
+ name: task.name,
79
+ }
80
+ if task.due_on
81
+ task_out[:due] = task.due_on
82
+ elsif task.due_at
83
+ task_out[:due] = task.due_at
84
+ end
85
+ task_out
77
86
  end
78
87
 
79
- def parse_view_args(subargs, args)
80
- subargs.workspace = args[1]
81
- subargs.project = args[2]
82
- subargs.section = args[3]
88
+ def tasks_to_hash(tasks)
89
+ tasks.map { |task| task_to_hash(task) }
83
90
  end
84
91
 
85
- def parse_quickadd_args(subargs, args)
86
- subargs.workspace = args[1]
87
- subargs.task_name = args[2]
88
- end
92
+ attr_reader :workspace_name, :project_name, :section_name, :task_name, :sections, :tasks, :stderr
93
+ end
89
94
 
90
- # rubocop:disable Metrics/MethodLength
91
- def parse_args(args)
92
- mode = args[0]
93
- subargs = OpenStruct.new
94
- case mode
95
- when 'view'
96
- parse_view_args(subargs, args)
97
- when 'quickadd'
98
- parse_quickadd_args(subargs, args)
99
- else
100
- raise
101
- end
102
- [mode, subargs]
95
+ # CLI subcommand that creates a task
96
+ class QuickaddSubcommand
97
+ def initialize(workspace_name, task_name,
98
+ config: Checkoff::ConfigLoader.load(:asana),
99
+ workspaces: Checkoff::Workspaces.new(config: config),
100
+ tasks: Checkoff::Tasks.new(config: config))
101
+ @workspace_name = workspace_name
102
+ @task_name = task_name
103
+ @workspaces = workspaces
104
+ @tasks = tasks
103
105
  end
104
- # rubocop:enable Metrics/MethodLength
105
-
106
- def output_help
107
- stderr.puts 'View tasks:'
108
- stderr.puts " #{$PROGRAM_NAME} view workspace project [section]"
109
- stderr.puts " #{$PROGRAM_NAME} quickadd workspace task_name"
110
- stderr.puts
111
- stderr.puts "'project' can be set to a project name, or :my_tasks, " \
112
- ":my_tasks_upcoming, :my_tasks_new, or :my_tasks_today"
106
+
107
+ def run
108
+ workspace = @workspaces.workspace_by_name(workspace_name)
109
+ @tasks.add_task(task_name,
110
+ workspace_gid: workspace.gid)
113
111
  end
114
112
 
115
- def view(workspace_name, project_name, section_name)
116
- if project_name.nil?
117
- stderr.puts 'Please specify a project name'
118
- exit(1)
119
- end
120
- project_name = project_name[1..].to_sym if project_name.start_with? ':'
121
- if section_name.nil?
122
- run_on_project(workspace_name, project_name)
123
- else
124
- run_on_section(workspace_name, project_name, section_name)
113
+ private
114
+
115
+ attr_reader :workspace_name, :task_name
116
+ end
117
+
118
+ # Provide ability for CLI to pull Asana items
119
+ class CheckoffGLIApp
120
+ extend GLI::App
121
+
122
+ program_desc 'Command-line client for Asana (unofficial)'
123
+
124
+ subcommand_option_handling :normal
125
+ arguments :strict
126
+
127
+ desc 'Add a short task to Asana'
128
+ arg 'workspace'
129
+ arg 'task_name'
130
+ command :quickadd do |c|
131
+ c.action do |_global_options, _options, args|
132
+ workspace_name = args.fetch(0)
133
+ task_name = args.fetch(1)
134
+
135
+ QuickaddSubcommand.new(workspace_name, task_name).run
125
136
  end
126
137
  end
127
138
 
128
- def run(args)
129
- validate_args!(args)
130
- command, subargs = parse_args(args)
131
- case command
132
- when 'view'
133
- view(subargs.workspace, subargs.project, subargs.section)
134
- when 'quickadd'
135
- quickadd(subargs.workspace, subargs.task_name)
136
- else
137
- raise
139
+ desc 'Output representation of Asana tasks'
140
+ arg 'workspace'
141
+ arg 'project'
142
+ arg 'section', :optional
143
+ arg 'task_name', :optional
144
+ command :view do |c|
145
+ c.action do |_global_options, _options, args|
146
+ workspace_name = args.fetch(0)
147
+ project_name = args.fetch(1)
148
+ section_name = args[2]
149
+ task_name = args[3]
150
+
151
+ puts ViewSubcommand.new(workspace_name, project_name, section_name, task_name).run
138
152
  end
139
153
  end
140
154
  end
141
- # rubocop:enable Metrics/ClassLength
142
155
  end
@@ -13,10 +13,6 @@ module Checkoff
13
13
  @yaml_filename = yaml_filename
14
14
  end
15
15
 
16
- def envvar_name(key)
17
- "#{@envvar_prefix}__#{key.upcase}"
18
- end
19
-
20
16
  def [](key)
21
17
  config_value = @config[key]
22
18
  return config_value unless config_value.nil?
@@ -31,25 +27,35 @@ module Checkoff
31
27
  raise KeyError,
32
28
  "Please configure either the #{key} key in #{@yaml_filename} or set #{envvar_name(key)}"
33
29
  end
30
+
31
+ private
32
+
33
+ def envvar_name(key)
34
+ "#{@envvar_prefix}__#{key.upcase}"
35
+ end
34
36
  end
35
37
 
36
38
  # Load configuration file
37
39
  class ConfigLoader
38
- def self.yaml_filename(sym)
39
- file = "#{sym}.yml"
40
- File.expand_path("~/.#{file}")
41
- end
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
42
45
 
43
- def self.load_yaml_file(sym)
44
- filename = yaml_filename(sym)
45
- return {} unless File.exist?(filename)
46
+ private
46
47
 
47
- YAML.load_file(filename).with_indifferent_access
48
- end
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
49
54
 
50
- def self.load(sym)
51
- yaml_result = load_yaml_file(sym)
52
- EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
55
+ def yaml_filename(sym)
56
+ file = "#{sym}.yml"
57
+ File.expand_path("~/.#{file}")
58
+ end
53
59
  end
54
60
  end
55
61
  end
@@ -22,8 +22,6 @@ module Checkoff
22
22
  LONG_CACHE_TIME = MINUTE * 15
23
23
  SHORT_CACHE_TIME = MINUTE * 5
24
24
 
25
- # XXX: Move low-level functions private
26
-
27
25
  def initialize(config: Checkoff::ConfigLoader.load(:asana),
28
26
  asana_client: Asana::Client,
29
27
  workspaces: Checkoff::Workspaces.new(config: config))
@@ -32,10 +30,41 @@ module Checkoff
32
30
  @workspaces = workspaces
33
31
  end
34
32
 
33
+ # Returns Asana Ruby API Client object
35
34
  def client
36
35
  @workspaces.client
37
36
  end
38
37
 
38
+ # pulls an Asana API project class given a name
39
+ def project(workspace_name, project_name)
40
+ if project_name.is_a?(Symbol) && project_name.to_s.start_with?('my_tasks')
41
+ my_tasks(workspace_name)
42
+ else
43
+ projects = projects_by_workspace_name(workspace_name)
44
+ projects.find do |project|
45
+ project.name == project_name
46
+ end
47
+ end
48
+ end
49
+ cache_method :project, LONG_CACHE_TIME
50
+
51
+ # find uncompleted tasks in a list
52
+ def active_tasks(tasks)
53
+ tasks.select { |task| task.completed_at.nil? }
54
+ end
55
+
56
+ # pull task objects from a named project
57
+ def tasks_from_project(project, only_uncompleted: true, extra_fields: [])
58
+ options = task_options
59
+ options[:completed_since] = '9999-12-01' if only_uncompleted
60
+ options[:project] = project.gid
61
+ options[:options][:fields] += extra_fields
62
+ client.tasks.find_all(**options).to_a
63
+ end
64
+ cache_method :tasks_from_project, SHORT_CACHE_TIME
65
+
66
+ private
67
+
39
68
  def projects
40
69
  client.projects
41
70
  end
@@ -56,22 +85,6 @@ module Checkoff
56
85
  projects.find_by_id(gid)
57
86
  end
58
87
 
59
- def project(workspace_name, project_name)
60
- if project_name.is_a?(Symbol) && project_name.to_s.start_with?('my_tasks')
61
- my_tasks(workspace_name)
62
- else
63
- projects = projects_by_workspace_name(workspace_name)
64
- projects.find do |project|
65
- project.name == project_name
66
- end
67
- end
68
- end
69
- cache_method :project, LONG_CACHE_TIME
70
-
71
- def active_tasks(tasks)
72
- tasks.select { |task| task.completed_at.nil? }
73
- end
74
-
75
88
  def task_options
76
89
  {
77
90
  per_page: 100,
@@ -81,14 +94,5 @@ module Checkoff
81
94
  },
82
95
  }
83
96
  end
84
-
85
- def tasks_from_project(project, only_uncompleted: true, extra_fields: [])
86
- options = task_options
87
- options[:completed_since] = '9999-12-01' if only_uncompleted
88
- options[:project] = project.gid
89
- options[:options][:fields] += extra_fields
90
- client.tasks.find_all(**options).to_a
91
- end
92
- cache_method :tasks_from_project, SHORT_CACHE_TIME
93
97
  end
94
98
  end
@@ -23,6 +23,71 @@ module Checkoff
23
23
  @time = time
24
24
  end
25
25
 
26
+ # Given a list of tasks, pull a Hash of tasks with section name -> task list
27
+ def by_section(tasks, project_gid)
28
+ by_section = {}
29
+ tasks.each do |task|
30
+ file_task_by_section(by_section, task, project_gid)
31
+ end
32
+ by_section
33
+ end
34
+ cache_method :by_section, LONG_CACHE_TIME
35
+
36
+ # Given a project object, pull all tasks, then provide a Hash of
37
+ # tasks with section name -> task list of the uncompleted tasks
38
+ def tasks_by_section_for_project(project)
39
+ raw_tasks = projects.tasks_from_project(project)
40
+ active_tasks = projects.active_tasks(raw_tasks)
41
+ by_section(active_tasks, project.gid)
42
+ end
43
+
44
+ # Given a workspace name and project name, then provide a Hash of
45
+ # tasks with section name -> task list of the uncompleted tasks
46
+ def tasks_by_section(workspace_name, project_name)
47
+ project = project_or_raise(workspace_name, project_name)
48
+ case project_name
49
+ when :my_tasks, :my_tasks_new, :my_tasks_today, :my_tasks_upcoming
50
+ user_task_list_by_section(workspace_name, project_name)
51
+ else
52
+ tasks_by_section_for_project(project)
53
+ end
54
+ end
55
+ cache_method :tasks_by_section, SHORT_CACHE_TIME
56
+
57
+ # XXX: Rename to section_tasks
58
+ #
59
+ # Pulls task objects from a specified section
60
+ def tasks(workspace_name, project_name, section_name)
61
+ tasks_by_section(workspace_name, project_name).fetch(section_name, [])
62
+ end
63
+ cache_method :tasks, SHORT_CACHE_TIME
64
+
65
+ # Pulls just names of tasks from a given section.
66
+ def section_task_names(workspace_name, project_name, section_name)
67
+ tasks = tasks(workspace_name, project_name, section_name)
68
+ if tasks.nil?
69
+ by_section = tasks_by_section(workspace_name, project_name)
70
+ desc = "#{workspace_name} | #{project_name} | #{section_name}"
71
+ raise "Could not find task names for #{desc}. " \
72
+ "Valid sections: #{by_section.keys}"
73
+ end
74
+ tasks.map(&:name)
75
+ end
76
+ cache_method :section_task_names, SHORT_CACHE_TIME
77
+
78
+ # returns if a task's due field is at or before the current date/time
79
+ def task_due?(task)
80
+ if task.due_at
81
+ Time.parse(task.due_at) <= time.now
82
+ elsif task.due_on
83
+ Date.parse(task.due_on) <= time.today
84
+ else
85
+ true # set a due date if you don't want to do this now
86
+ end
87
+ end
88
+
89
+ private
90
+
26
91
  def_delegators :@projects, :client
27
92
 
28
93
  def file_task_by_section(by_section, task, project_gid)
@@ -35,27 +100,12 @@ module Checkoff
35
100
  by_section[current_section] << task
36
101
  end
37
102
 
38
- def by_section(tasks, project_gid)
39
- by_section = {}
40
- tasks.each do |task|
41
- file_task_by_section(by_section, task, project_gid)
42
- end
43
- by_section
44
- end
45
- cache_method :by_section, LONG_CACHE_TIME
46
-
47
103
  def legacy_tasks_by_section_for_project(project)
48
104
  raw_tasks = projects.tasks_from_project(project)
49
105
  active_tasks = projects.active_tasks(raw_tasks)
50
106
  legacy_by_section(active_tasks)
51
107
  end
52
108
 
53
- def tasks_by_section_for_project(project)
54
- raw_tasks = projects.tasks_from_project(project)
55
- active_tasks = projects.active_tasks(raw_tasks)
56
- by_section(active_tasks, project.gid)
57
- end
58
-
59
109
  def legacy_file_task_by_section(current_section, by_section, task)
60
110
  if task.name =~ /:$/
61
111
  current_section = task.name
@@ -120,35 +170,6 @@ module Checkoff
120
170
  end
121
171
  end
122
172
 
123
- def tasks_by_section(workspace_name, project_name)
124
- project = project_or_raise(workspace_name, project_name)
125
- case project_name
126
- when :my_tasks, :my_tasks_new, :my_tasks_today, :my_tasks_upcoming
127
- user_task_list_by_section(workspace_name, project_name)
128
- else
129
- tasks_by_section_for_project(project)
130
- end
131
- end
132
- cache_method :tasks_by_section, SHORT_CACHE_TIME
133
-
134
- # XXX: Rename to section_tasks
135
- def tasks(workspace_name, project_name, section_name)
136
- tasks_by_section(workspace_name, project_name).fetch(section_name, [])
137
- end
138
- cache_method :tasks, SHORT_CACHE_TIME
139
-
140
- def section_task_names(workspace_name, project_name, section_name)
141
- tasks = tasks(workspace_name, project_name, section_name)
142
- if tasks.nil?
143
- by_section = tasks_by_section(workspace_name, project_name)
144
- desc = "#{workspace_name} | #{project_name} | #{section_name}"
145
- raise "Could not find task names for #{desc}. " \
146
- "Valid sections: #{by_section.keys}"
147
- end
148
- tasks.map(&:name)
149
- end
150
- cache_method :section_task_names, SHORT_CACHE_TIME
151
-
152
173
  def user_task_list_migrated_to_real_sections?(workspace_name)
153
174
  workspace = workspaces.workspace_by_name(workspace_name)
154
175
  result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
@@ -156,16 +177,6 @@ module Checkoff
156
177
  result.migration_status != 'not_migrated'
157
178
  end
158
179
 
159
- def task_due?(task)
160
- if task.due_at
161
- Time.parse(task.due_at) <= time.now
162
- elsif task.due_on
163
- Date.parse(task.due_on) <= time.today
164
- else
165
- true # set a due date if you don't want to do this now
166
- end
167
- end
168
-
169
180
  def project_task_names(workspace_name, project_name)
170
181
  by_section = tasks_by_section(workspace_name, project_name)
171
182
  by_section.flat_map do |section_name, tasks|
@@ -11,17 +11,7 @@ module Checkoff
11
11
 
12
12
  extend Forwardable
13
13
 
14
- def file_task_by_section(current_section, by_section, task)
15
- if task.name =~ /:$/
16
- current_section = task.name
17
- by_section[current_section] = []
18
- else
19
- by_section[current_section] ||= []
20
- by_section[current_section] << task
21
- end
22
- [current_section, by_section]
23
- end
24
-
14
+ # pulls a Hash of subtasks broken out by section
25
15
  def by_section(tasks)
26
16
  current_section = nil
27
17
  by_section = {}
@@ -37,5 +27,18 @@ module Checkoff
37
27
  task.subtasks(projects.task_options)
38
28
  end
39
29
  cache_method :raw_subtasks, LONG_CACHE_TIME
30
+
31
+ private
32
+
33
+ def file_task_by_section(current_section, by_section, task)
34
+ if task.name =~ /:$/
35
+ current_section = task.name
36
+ by_section[current_section] = []
37
+ else
38
+ by_section[current_section] ||= []
39
+ by_section[current_section] << task
40
+ end
41
+ [current_section, by_section]
42
+ end
40
43
  end
41
44
  end
@@ -5,7 +5,7 @@
5
5
  require_relative 'sections'
6
6
 
7
7
  module Checkoff
8
- # Pull things from 'my tasks' in Asana
8
+ # Pull tasks from Asana
9
9
  class Tasks
10
10
  MINUTE = 60
11
11
  HOUR = MINUTE * 60
@@ -22,6 +22,22 @@ module Checkoff
22
22
  @asana_task = asana_task
23
23
  end
24
24
 
25
+ # Pull a specific task by name
26
+ def task(workspace_name, project_name, task_name,
27
+ section_name: :unspecified,
28
+ only_uncompleted: true)
29
+ project = projects.project(workspace_name, project_name)
30
+ tasks = if section_name == :unspecified
31
+ projects.tasks_from_project(project,
32
+ only_uncompleted: only_uncompleted)
33
+ else
34
+ @sections.tasks(workspace_name, project_name, section_name)
35
+ end
36
+ tasks.find { |task| task.name == task_name }
37
+ end
38
+
39
+ private
40
+
25
41
  def client
26
42
  @sections.client
27
43
  end
@@ -30,13 +46,6 @@ module Checkoff
30
46
  @projects ||= @sections.projects
31
47
  end
32
48
 
33
- def task(workspace_name, project_name, task_name, only_uncompleted: true)
34
- project = projects.project(workspace_name, project_name)
35
- tasks = projects.tasks_from_project(project,
36
- only_uncompleted: only_uncompleted)
37
- tasks.find { |task| task.name == task_name }
38
- end
39
-
40
49
  def tasks_minus_sections(tasks)
41
50
  @sections.by_section(tasks).values.flatten
42
51
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Checkoff
4
- VERSION = '0.10.0'
4
+ # Version of library
5
+ VERSION = '0.11.0'
5
6
  end
@@ -9,6 +9,7 @@ module Checkoff
9
9
  @asana_client = asana_client
10
10
  end
11
11
 
12
+ # Returns Asana Ruby API Client object
12
13
  def client
13
14
  @client ||= @asana_client.new do |c|
14
15
  c.authentication :access_token, @config.fetch(:personal_access_token)
@@ -17,14 +18,17 @@ module Checkoff
17
18
  end
18
19
  end
19
20
 
20
- def default_workspace_gid
21
- @config.fetch(:default_workspace_gid)
22
- end
23
-
21
+ # Pulls an Asana workspace object
24
22
  def workspace_by_name(workspace_name)
25
23
  client.workspaces.find_all.find do |workspace|
26
24
  workspace.name == workspace_name
27
25
  end || raise("Could not find workspace named [#{workspace_name}]")
28
26
  end
27
+
28
+ private
29
+
30
+ def default_workspace_gid
31
+ @config.fetch(:default_workspace_gid)
32
+ end
29
33
  end
30
34
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  desc 'Run tasks to be done during a continuous integration (CI) build'
4
- task ci: :localtest
4
+ task citest: %i[clear_metrics test undercover]
@@ -1,8 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Rebaseline quality thresholds to last commit'
3
+ desc 'Ensure that any locally ratcheted coverage metrics are cleared back ' \
4
+ 'to git baseline'
4
5
  task :clear_metrics do |_t|
5
6
  ret =
6
7
  system('git checkout coverage/.last_run.json')
7
8
  raise unless ret
9
+
10
+ # Without this old lines which are removed are still counted,
11
+ # leading to inconsistent coverage percentages between runs.
12
+ #
13
+ # need to save coverage/.last_run.json
14
+ ret =
15
+ system('rm -fr coverage/assets coverage/.*.json.lock coverage/lcov/* coverage/index.html coverage/.resultset.json')
16
+ raise unless ret
8
17
  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.10.0
4
+ version: 0.11.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: 2021-05-11 00:00:00.000000000 Z
11
+ date: 2021-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gli
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bump
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +142,14 @@ dependencies:
128
142
  requirements:
129
143
  - - ">="
130
144
  - !ruby/object:Gem::Version
131
- version: '0'
145
+ version: 0.58.0
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - ">="
137
151
  - !ruby/object:Gem::Version
138
- version: '0'
152
+ version: 0.58.0
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: rake
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,20 @@ dependencies:
206
220
  - - ">="
207
221
  - !ruby/object:Gem::Version
208
222
  version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: simplecov-lcov
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
209
237
  - !ruby/object:Gem::Dependency
210
238
  name: undercover
211
239
  requirement: !ruby/object:Gem::Requirement
@@ -281,7 +309,7 @@ files:
281
309
  - metrics/rubocop_high_water_mark
282
310
  - metrics/scalastyle_high_water_mark
283
311
  - metrics/shellcheck_high_water_mark
284
- - rakelib/ci.rake
312
+ - rakelib/citest.rake
285
313
  - rakelib/clear_metrics.rake
286
314
  - rakelib/default.rake
287
315
  - rakelib/gem_tasks.rake
@@ -310,7 +338,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
310
338
  - !ruby/object:Gem::Version
311
339
  version: '0'
312
340
  requirements: []
313
- rubygems_version: 3.1.2
341
+ rubygems_version: 3.0.3.1
314
342
  signing_key:
315
343
  specification_version: 4
316
344
  summary: Command-line and gem client for Asana (unofficial)