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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b21d53a3957ea19430b3726fd82fefeceef262330ead8eb9ef1ef087acd29bb4
4
- data.tar.gz: 3927e36c7c059e90613ceec82e8d00efc8c7e6ceceb6183d05a5cd59c466086c
3
+ metadata.gz: 4daea874da20235bc9b20d315058644bafb73e78b14c6c266f6252f4b346bcf1
4
+ data.tar.gz: feb305070b73a03e2481515a914cca2fcaad264f3871cd65e242bf58ccb3612f
5
5
  SHA512:
6
- metadata.gz: f92dd70f82426b84d8a651a9963dffbf6b66017c484b25371b68c16495bf2cdb28faba9fdb31a0ab56ffbb5f848f1a877375379c4c94ca946bb16d9630d7d075
7
- data.tar.gz: 69aa57c5583a6adba8320783171eeaef3814e9e758c81b3c81bbd455c3d238d72d145be44c5fa6732764d7561beac2c19181f8344462f209346572bb23f6bf3d
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
+ }
@@ -12,7 +12,7 @@ jobs:
12
12
  name: Ruby ${{ matrix.version }}
13
13
  strategy:
14
14
  matrix:
15
- version: [2.5, 2.6, 2.7, 3.0]
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
@@ -3,5 +3,7 @@ inherit_gem:
3
3
 
4
4
  AllCops:
5
5
  TargetRubyVersion: 3.0
6
+ NewCops: disable
7
+ SuggestExtensions: false
6
8
  Exclude:
7
9
  - vendor/**/*
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_todo (1.4.3)
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.21.0)
18
- parser (3.0.2.0)
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
- rainbow (3.0.0)
23
+ racc (1.7.0)
24
+ rainbow (3.1.1)
22
25
  rake (13.0.6)
23
- regexp_parser (2.1.1)
26
+ regexp_parser (2.8.1)
24
27
  rexml (3.2.5)
25
- rubocop (1.23.0)
28
+ rubocop (1.52.1)
29
+ json (~> 2.3)
26
30
  parallel (~> 1.10)
27
- parser (>= 3.0.0.0)
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.12.0, < 2.0)
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 (>= 1.4.0, < 3.0)
34
- rubocop-ast (1.13.0)
35
- parser (>= 3.0.1.1)
36
- rubocop-shopify (2.3.0)
37
- rubocop (~> 1.22)
38
- ruby-progressbar (1.11.0)
39
- unicode-display_width (2.1.0)
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("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
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
@@ -9,4 +9,4 @@ if ENV["ENABLE_SMART_TODO"] && !ARGV.include?("--dispatcher")
9
9
  ARGV << "--dispatcher" << "slack"
10
10
  end
11
11
 
12
- SmartTodo::CLI.new.run
12
+ exit SmartTodo::CLI.new.run
@@ -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
- STDOUT.print(".")
25
- STDOUT.flush
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
- # Factory pattern to retrive the right dispatcher class.
7
- #
8
- # @param dispatcher [String]
9
- #
10
- # @return [Class]
11
- def self.class_for(dispatcher)
12
- case dispatcher
13
- when "slack"
14
- Slack
15
- when nil, "output"
16
- Output
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
- # Subclasses should define what options from the CLI they need in order
21
- # to properly deliver the message. For instance the Slack dispatcher
22
- # requires an API key.
23
- #
24
- # @param _options [Hash]
25
- #
26
- # @return void
27
- def self.validate_options!(_options)
28
- raise(NotImplemetedError, "subclass responsability")
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
@@ -4,7 +4,9 @@ module SmartTodo
4
4
  module Dispatchers
5
5
  # A simple dispatcher that will output the reminder.
6
6
  class Output < Base
7
- def self.validate_options!(_); end
7
+ class << self
8
+ def validate_options!(_); end
9
+ end
8
10
 
9
11
  # @return void
10
12
  def dispatch
@@ -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
- def self.validate_options!(options)
9
- options[:slack_token] ||= ENV.fetch("SMART_TODO_SLACK_TOKEN") { raise(ArgumentError, "Missing :slack_token") }
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
- options.fetch(:fallback_channel) { raise(ArgumentError, "Missing :fallback_channel") }
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
- # @param on_date [String] a string parsable by Time.parse
10
- # @return [String, false]
11
- def self.met?(on_date)
12
- if Time.now >= Time.parse(on_date)
13
- message(on_date)
14
- else
15
- false
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
- # @param on_date [String]
20
- # @return [String]
21
- def self.message(on_date)
22
- "We are past the *#{on_date}* due date and your TODO is now ready to be addressed."
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
- # @param source [String] the actual Ruby code
22
- def self.parse(source)
23
- sexp = new(source).parse
24
- Visitor.new.tap { |v| v.process(sexp) }
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
- return unless method_node.is_a?(MethodNode)
108
-
109
- events << method_node
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
- # Chech if the response to Slack was a 200 and the Slack API request was successful
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"]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmartTodo
4
- VERSION = "1.4.3"
4
+ VERSION = "1.5.0"
5
5
  end
@@ -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
- if !smart_todo?(metadata)
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.3
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-01-10 00:00:00.000000000 Z
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: '0'
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.3.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.