smart_todo 1.4.3 → 1.5.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/.devcontainer/devcontainer.json +25 -0
- data/.github/workflows/build.yml +1 -2
- data/.rubocop.yml +2 -0
- data/Gemfile.lock +20 -16
- data/bin/rubocop +4 -2
- data/exe/smart_todo +1 -1
- data/lib/smart_todo/cli.rb +25 -2
- data/lib/smart_todo/dispatchers/base.rb +23 -21
- data/lib/smart_todo/dispatchers/output.rb +3 -1
- data/lib/smart_todo/dispatchers/slack.rb +6 -4
- data/lib/smart_todo/events/date.rb +14 -12
- data/lib/smart_todo/parser/metadata_parser.rb +13 -8
- data/lib/smart_todo/slack_client.rb +2 -1
- data/lib/smart_todo/version.rb +1 -1
- data/lib/smart_todo_cop.rb +5 -1
- data/smart_todo.gemspec +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4daea874da20235bc9b20d315058644bafb73e78b14c6c266f6252f4b346bcf1
|
4
|
+
data.tar.gz: feb305070b73a03e2481515a914cca2fcaad264f3871cd65e242bf58ccb3612f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fcba36662ce881114aafa3f5bba320d767c3e299a1d9d91f06a505107815d631c008ed87a1e853d1bb2766c1e9bdce756125ccc9c5ed65917264f962da62814
|
7
|
+
data.tar.gz: 981c7785a6c5e4d2805d004f6ec1411a953ec9d68005314bc8fe52bf1deb5f6c036a73ea4a08cf3346a120f765971c4fbb0049bdc4ebf97c867bf7e14f7690b8
|
@@ -0,0 +1,25 @@
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
|
3
|
+
{
|
4
|
+
"name": "Ruby",
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:0-3-bullseye",
|
7
|
+
"features": {
|
8
|
+
"ghcr.io/devcontainers/features/github-cli:1": {}
|
9
|
+
},
|
10
|
+
|
11
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
12
|
+
// "features": {},
|
13
|
+
|
14
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
15
|
+
// "forwardPorts": [],
|
16
|
+
|
17
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
18
|
+
"postCreateCommand": "bundle install",
|
19
|
+
|
20
|
+
// Configure tool-specific properties.
|
21
|
+
// "customizations": {},
|
22
|
+
|
23
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
24
|
+
// "remoteUser": "root"
|
25
|
+
}
|
data/.github/workflows/build.yml
CHANGED
@@ -12,7 +12,7 @@ jobs:
|
|
12
12
|
name: Ruby ${{ matrix.version }}
|
13
13
|
strategy:
|
14
14
|
matrix:
|
15
|
-
version: [
|
15
|
+
version: [3.0, 3.1, 3.2]
|
16
16
|
|
17
17
|
steps:
|
18
18
|
- uses: actions/checkout@v2
|
@@ -24,4 +24,3 @@ jobs:
|
|
24
24
|
- name: Run Tests
|
25
25
|
run: |
|
26
26
|
bundle exec rake test
|
27
|
-
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
smart_todo (1.
|
4
|
+
smart_todo (1.5.0)
|
5
5
|
rexml
|
6
6
|
|
7
7
|
GEM
|
@@ -13,30 +13,34 @@ GEM
|
|
13
13
|
crack (0.4.5)
|
14
14
|
rexml
|
15
15
|
hashdiff (1.0.1)
|
16
|
+
json (2.6.3)
|
16
17
|
minitest (5.14.4)
|
17
|
-
parallel (1.
|
18
|
-
parser (3.
|
18
|
+
parallel (1.23.0)
|
19
|
+
parser (3.2.2.3)
|
19
20
|
ast (~> 2.4.1)
|
21
|
+
racc
|
20
22
|
public_suffix (4.0.6)
|
21
|
-
|
23
|
+
racc (1.7.0)
|
24
|
+
rainbow (3.1.1)
|
22
25
|
rake (13.0.6)
|
23
|
-
regexp_parser (2.
|
26
|
+
regexp_parser (2.8.1)
|
24
27
|
rexml (3.2.5)
|
25
|
-
rubocop (1.
|
28
|
+
rubocop (1.52.1)
|
29
|
+
json (~> 2.3)
|
26
30
|
parallel (~> 1.10)
|
27
|
-
parser (>= 3.
|
31
|
+
parser (>= 3.2.2.3)
|
28
32
|
rainbow (>= 2.2.2, < 4.0)
|
29
33
|
regexp_parser (>= 1.8, < 3.0)
|
30
|
-
rexml
|
31
|
-
rubocop-ast (>= 1.
|
34
|
+
rexml (>= 3.2.5, < 4.0)
|
35
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
32
36
|
ruby-progressbar (~> 1.7)
|
33
|
-
unicode-display_width (>=
|
34
|
-
rubocop-ast (1.
|
35
|
-
parser (>= 3.
|
36
|
-
rubocop-shopify (2.
|
37
|
-
rubocop (~> 1.
|
38
|
-
ruby-progressbar (1.
|
39
|
-
unicode-display_width (2.
|
37
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
38
|
+
rubocop-ast (1.29.0)
|
39
|
+
parser (>= 3.2.1.0)
|
40
|
+
rubocop-shopify (2.14.0)
|
41
|
+
rubocop (~> 1.51)
|
42
|
+
ruby-progressbar (1.13.0)
|
43
|
+
unicode-display_width (2.4.2)
|
40
44
|
webmock (3.11.2)
|
41
45
|
addressable (>= 2.3.6)
|
42
46
|
crack (>= 0.3.2)
|
data/bin/rubocop
CHANGED
@@ -9,8 +9,10 @@
|
|
9
9
|
#
|
10
10
|
|
11
11
|
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
13
|
-
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
13
|
+
"../../Gemfile",
|
14
|
+
Pathname.new(__FILE__).realpath,
|
15
|
+
)
|
14
16
|
|
15
17
|
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
18
|
|
data/exe/smart_todo
CHANGED
data/lib/smart_todo/cli.rb
CHANGED
@@ -9,21 +9,35 @@ module SmartTodo
|
|
9
9
|
class CLI
|
10
10
|
def initialize
|
11
11
|
@options = {}
|
12
|
+
@errors = []
|
12
13
|
end
|
13
14
|
|
14
15
|
# @param args [Array<String>]
|
15
16
|
def run(args = ARGV)
|
16
17
|
paths = define_options.parse!(args)
|
17
18
|
validate_options!
|
19
|
+
|
18
20
|
paths << "." if paths.empty?
|
19
21
|
|
20
22
|
paths.each do |path|
|
21
23
|
normalize_path(path).each do |file|
|
22
24
|
parse_file(file)
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
$stdout.print(".")
|
27
|
+
$stdout.flush
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if @errors.empty?
|
32
|
+
0
|
33
|
+
else
|
34
|
+
$stderr.puts "There were errors while checking for TODOs:\n"
|
35
|
+
|
36
|
+
@errors.each do |error|
|
37
|
+
$stderr.puts error
|
26
38
|
end
|
39
|
+
|
40
|
+
1
|
27
41
|
end
|
28
42
|
end
|
29
43
|
|
@@ -71,8 +85,17 @@ module SmartTodo
|
|
71
85
|
event_message = nil
|
72
86
|
event_met = todo_node.metadata.events.find do |event|
|
73
87
|
event_message = Events.public_send(event.method_name, *event.arguments)
|
88
|
+
rescue => e
|
89
|
+
message = "Error while parsing #{file} on event `#{event.method_name}` with arguments #{event.arguments}: " \
|
90
|
+
"#{e.message}"
|
91
|
+
|
92
|
+
@errors << message
|
93
|
+
|
94
|
+
nil
|
74
95
|
end
|
75
96
|
|
97
|
+
@errors.concat(todo_node.metadata.errors)
|
98
|
+
|
76
99
|
dispatcher.new(event_message, todo_node, file, @options).dispatch if event_met
|
77
100
|
end
|
78
101
|
end
|
@@ -3,29 +3,31 @@
|
|
3
3
|
module SmartTodo
|
4
4
|
module Dispatchers
|
5
5
|
class Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
class << self
|
7
|
+
# Factory pattern to retrieve the right dispatcher class.
|
8
|
+
#
|
9
|
+
# @param dispatcher [String]
|
10
|
+
#
|
11
|
+
# @return [Class]
|
12
|
+
def class_for(dispatcher)
|
13
|
+
case dispatcher
|
14
|
+
when "slack"
|
15
|
+
Slack
|
16
|
+
when nil, "output"
|
17
|
+
Output
|
18
|
+
end
|
17
19
|
end
|
18
|
-
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
# Subclasses should define what options from the CLI they need in order
|
22
|
+
# to properly deliver the message. For instance the Slack dispatcher
|
23
|
+
# requires an API key.
|
24
|
+
#
|
25
|
+
# @param _options [Hash]
|
26
|
+
#
|
27
|
+
# @return void
|
28
|
+
def validate_options!(_options)
|
29
|
+
raise(NotImplemetedError, "subclass responsability")
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
31
33
|
# @param event_message [String] the success message associated
|
@@ -5,10 +5,12 @@ module SmartTodo
|
|
5
5
|
# Dispatcher that sends TODO reminders on Slack. Assignees can be either individual
|
6
6
|
# (using the associated slack email address) or a channel.
|
7
7
|
class Slack < Base
|
8
|
-
|
9
|
-
|
8
|
+
class << self
|
9
|
+
def validate_options!(options)
|
10
|
+
options[:slack_token] ||= ENV.fetch("SMART_TODO_SLACK_TOKEN") { raise(ArgumentError, "Missing :slack_token") }
|
10
11
|
|
11
|
-
|
12
|
+
options.fetch(:fallback_channel) { raise(ArgumentError, "Missing :fallback_channel") }
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
# Make a Slack API call to dispatch the message to each assignee
|
@@ -35,7 +37,7 @@ module SmartTodo
|
|
35
37
|
|
36
38
|
client.post_message(user.dig("user", "id"), slack_message(user, assignee))
|
37
39
|
rescue SlackClient::Error => error
|
38
|
-
if ["users_not_found", "channel_not_found"].include?(error.error_code)
|
40
|
+
if ["users_not_found", "channel_not_found", "is_archived"].include?(error.error_code)
|
39
41
|
user = { "user" => { "id" => @options[:fallback_channel] }, "fallback" => true }
|
40
42
|
else
|
41
43
|
raise(error)
|
@@ -6,20 +6,22 @@ module SmartTodo
|
|
6
6
|
module Events
|
7
7
|
# An event that check if the passed date is passed
|
8
8
|
class Date
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
class << self
|
10
|
+
# @param on_date [String] a string parsable by Time.parse
|
11
|
+
# @return [String, false]
|
12
|
+
def met?(on_date)
|
13
|
+
if Time.now >= Time.parse(on_date)
|
14
|
+
message(on_date)
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
# @param on_date [String]
|
21
|
+
# @return [String]
|
22
|
+
def message(on_date)
|
23
|
+
"We are past the *#{on_date}* due date and your TODO is now ready to be addressed."
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -18,10 +18,12 @@ module SmartTodo
|
|
18
18
|
|
19
19
|
# This class is used to parse the ruby TODO() comment.
|
20
20
|
class MetadataParser < Ripper
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
class << self
|
22
|
+
# @param source [String] the actual Ruby code
|
23
|
+
def parse(source)
|
24
|
+
sexp = new(source).parse
|
25
|
+
Visitor.new.tap { |v| v.process(sexp) }
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
# @return [Array] an Array of Array
|
@@ -78,11 +80,12 @@ module SmartTodo
|
|
78
80
|
end
|
79
81
|
|
80
82
|
class Visitor
|
81
|
-
attr_reader :events, :assignees
|
83
|
+
attr_reader :events, :assignees, :errors
|
82
84
|
|
83
85
|
def initialize
|
84
86
|
@events = []
|
85
87
|
@assignees = []
|
88
|
+
@errors = []
|
86
89
|
end
|
87
90
|
|
88
91
|
# Iterate over each tokens returned from the parser and call
|
@@ -104,9 +107,11 @@ module SmartTodo
|
|
104
107
|
# @param method_node [MethodNode]
|
105
108
|
# @return [void]
|
106
109
|
def on_todo_event(method_node)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
+
if method_node.is_a?(MethodNode)
|
111
|
+
events << method_node
|
112
|
+
else
|
113
|
+
errors << "Incorrect `:on` event format: #{method_node}"
|
114
|
+
end
|
110
115
|
end
|
111
116
|
|
112
117
|
# @param assignee [String]
|
@@ -82,7 +82,7 @@ module SmartTodo
|
|
82
82
|
slack_response!(response)
|
83
83
|
end
|
84
84
|
|
85
|
-
#
|
85
|
+
# Check if the response to Slack was a 200 and the Slack API request was successful
|
86
86
|
#
|
87
87
|
# @param response [Net::HTTPResponse] a net Net::HTTPResponse subclass
|
88
88
|
# (Net::HTTPOK, Net::HTTPNotFound ...)
|
@@ -92,6 +92,7 @@ module SmartTodo
|
|
92
92
|
# @raise [SlackClient::Error] in case Slack returns a { ok: false } in the body
|
93
93
|
def slack_response!(response)
|
94
94
|
raise(Net::HTTPError.new("Request to slack failed", response)) unless response.code_type < Net::HTTPSuccess
|
95
|
+
|
95
96
|
body = JSON.parse(response.body)
|
96
97
|
|
97
98
|
if body["ok"]
|
data/lib/smart_todo/version.rb
CHANGED
data/lib/smart_todo_cop.rb
CHANGED
@@ -18,8 +18,12 @@ module RuboCop
|
|
18
18
|
def investigate(processed_source)
|
19
19
|
processed_source.comments.each do |comment|
|
20
20
|
next unless /^#\sTODO/ =~ comment.text
|
21
|
+
|
21
22
|
metadata = metadata(comment.text)
|
22
|
-
|
23
|
+
|
24
|
+
if metadata.errors.any?
|
25
|
+
add_offense(comment, message: "Invalid TODO format: #{metadata.errors.join(", ")}. #{HELP}")
|
26
|
+
elsif !smart_todo?(metadata)
|
23
27
|
add_offense(comment)
|
24
28
|
elsif (methods = invalid_event_methods(metadata.events)).any?
|
25
29
|
add_offense(comment, message: "Invalid event method(s): #{methods.join(", ")}. #{HELP}")
|
data/smart_todo.gemspec
CHANGED
@@ -24,6 +24,8 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.metadata["changelog_uri"] = spec.homepage + "/releases"
|
25
25
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
26
26
|
|
27
|
+
spec.required_ruby_version = ">= 3.0.0"
|
28
|
+
|
27
29
|
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
28
30
|
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
29
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_todo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rexml
|
@@ -91,6 +91,7 @@ executables:
|
|
91
91
|
extensions: []
|
92
92
|
extra_rdoc_files: []
|
93
93
|
files:
|
94
|
+
- ".devcontainer/devcontainer.json"
|
94
95
|
- ".github/workflows/build.yml"
|
95
96
|
- ".github/workflows/cla.yml"
|
96
97
|
- ".github/workflows/rubocop.yml"
|
@@ -142,14 +143,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
143
|
requirements:
|
143
144
|
- - ">="
|
144
145
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
146
|
+
version: 3.0.0
|
146
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
148
|
requirements:
|
148
149
|
- - ">="
|
149
150
|
- !ruby/object:Gem::Version
|
150
151
|
version: '0'
|
151
152
|
requirements: []
|
152
|
-
rubygems_version: 3.
|
153
|
+
rubygems_version: 3.4.13
|
153
154
|
signing_key:
|
154
155
|
specification_version: 4
|
155
156
|
summary: Enhance todo's comments in your codebase.
|