smart_todo 1.0.2 → 1.3.1

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: d4a658f8acb049243c04ca03be1c3cf4eacd84a648955c6bc0625f9c497ffbe5
4
- data.tar.gz: 49258142b68fc748a054bb36a3c207b16bb010709a21e935a28fe4bdb379cf02
3
+ metadata.gz: dc3a4665a0110180692321fbdd788be8df4155de16bf75b2967f4c330a759e72
4
+ data.tar.gz: 414a198aa06035126890b002f6552779c73d9f6f84eeeaa904ba2132daa7d4d9
5
5
  SHA512:
6
- metadata.gz: da4d883a649e9c090eabd1415a778503d272ad36baf505688a126bc37e6b010f78a72e3da27ad0b28706ae015f48c41115e3644b68b3c590f1e69918d1b32b39
7
- data.tar.gz: 7ff862bbc35a507e220dd40aca07b1b7719e5e47270456652c4c4bf39a7cf9634ef0132379bb2f5e52038b9eafa6e5fab22f2f26948f172907577dde573f271a
6
+ metadata.gz: 8e0c57b0849a90cd1be9699d264167e7286d622eb42e02cc7dd13231ab41d76d815a455a65571f3890dda7c79dc0228692b0c72c37c50337826df137d0c0f0df
7
+ data.tar.gz: a62b94332abe655cb418d0238eaa24e86db0e48b109877b8192265a244795624a2188cffeb0be429f0204683394b992ce73a0932efc322b4c3ba589cadcd0af4
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ name: Ruby ${{ matrix.version }}
13
+ strategy:
14
+ matrix:
15
+ version: [2.5, 2.6, 2.7, 3.0]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - name: Set up Ruby ${{ matrix.version }}
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.version }}
23
+ bundler-cache: true
24
+ - name: Run Tests
25
+ run: |
26
+ bundle exec rake test
27
+
@@ -0,0 +1,22 @@
1
+ name: RuboCop
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - name: Set up Ruby 3.0
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: 3.0
15
+ bundler-cache: true
16
+ - name: Install gems
17
+ run: |
18
+ bundle config path vendor/bundle
19
+ bundle config set without 'default development test'
20
+ bundle install --jobs 4 --retry 3
21
+ - name: Run RuboCop
22
+ run: bundle exec rubocop --parallel
data/.rubocop.yml CHANGED
@@ -1,7 +1,7 @@
1
- inherit_from:
2
- - http://shopify.github.io/ruby-style-guide/rubocop.yml
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.5
5
+ TargetRubyVersion: 3.0
6
6
  Exclude:
7
7
  - vendor/**/*
data/Gemfile CHANGED
@@ -6,6 +6,6 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  gemspec
8
8
 
9
- group :development do
10
- gem 'rubocop', '~> 0.71'
9
+ group :rubocop do
10
+ gem "rubocop-shopify", require: false
11
11
  end
data/Gemfile.lock CHANGED
@@ -1,36 +1,43 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_todo (1.0.2)
4
+ smart_todo (1.3.1)
5
+ rexml
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
9
- addressable (2.6.0)
10
- public_suffix (>= 2.0.2, < 4.0)
11
- ast (2.4.0)
12
- crack (0.4.3)
13
- safe_yaml (~> 1.0.0)
14
- hashdiff (0.4.0)
15
- jaro_winkler (1.5.3)
16
- minitest (5.11.3)
17
- parallel (1.17.0)
18
- parser (2.6.3.0)
19
- ast (~> 2.4.0)
20
- public_suffix (3.1.1)
10
+ addressable (2.8.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ ast (2.4.2)
13
+ crack (0.4.5)
14
+ rexml
15
+ hashdiff (1.0.1)
16
+ minitest (5.14.4)
17
+ parallel (1.21.0)
18
+ parser (3.0.2.0)
19
+ ast (~> 2.4.1)
20
+ public_suffix (4.0.6)
21
21
  rainbow (3.0.0)
22
- rake (10.5.0)
23
- rubocop (0.71.0)
24
- jaro_winkler (~> 1.5.1)
22
+ rake (13.0.6)
23
+ regexp_parser (2.1.1)
24
+ rexml (3.2.5)
25
+ rubocop (1.23.0)
25
26
  parallel (~> 1.10)
26
- parser (>= 2.6)
27
+ parser (>= 3.0.0.0)
27
28
  rainbow (>= 2.2.2, < 4.0)
29
+ regexp_parser (>= 1.8, < 3.0)
30
+ rexml
31
+ rubocop-ast (>= 1.12.0, < 2.0)
28
32
  ruby-progressbar (~> 1.7)
29
- unicode-display_width (>= 1.4.0, < 1.7)
30
- ruby-progressbar (1.10.1)
31
- safe_yaml (1.0.5)
32
- unicode-display_width (1.6.0)
33
- webmock (3.6.0)
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)
40
+ webmock (3.11.2)
34
41
  addressable (>= 2.3.6)
35
42
  crack (>= 0.3.2)
36
43
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -39,12 +46,12 @@ PLATFORMS
39
46
  ruby
40
47
 
41
48
  DEPENDENCIES
42
- bundler (~> 1.17)
49
+ bundler (>= 1.17)
43
50
  minitest (~> 5.0)
44
- rake (~> 10.0)
45
- rubocop (~> 0.71)
51
+ rake (>= 10.0)
52
+ rubocop-shopify
46
53
  smart_todo!
47
54
  webmock
48
55
 
49
56
  BUNDLED WITH
50
- 1.17.3
57
+ 2.2.25
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="https://user-images.githubusercontent.com/8122246/61341925-b936d180-a848-11e9-95c1-0d2f398c51b1.png?raw=true" width="200">
3
3
  </h3>
4
4
 
5
- [![Build Status](https://travis-ci.com/Shopify/smart_todo.svg?branch=master)](https://travis-ci.com/Shopify/smart_todo)
5
+ [![Build Status](https://github.com/Shopify/smart_todo/workflows/CI/badge.svg)](https://github.com/Shopify/smart_todo/actions?query=workflow%3ACI)
6
6
 
7
7
  _SmartTodo_ is a library designed to assign users on TODO comments written in your codebase and help assignees be reminded when it's time to commit to their TODO.
8
8
 
data/dev.yml CHANGED
@@ -4,8 +4,12 @@ type:
4
4
  - ruby
5
5
 
6
6
  up:
7
- - ruby: 2.5.5
7
+ - ruby: 3.0.2
8
8
  - bundler
9
9
 
10
+ console:
11
+ desc: 'start a console'
12
+ run: bin/console
13
+
10
14
  test:
11
15
  bundle exec rake test
data/exe/smart_todo CHANGED
@@ -3,12 +3,10 @@
3
3
 
4
4
  $LOAD_PATH.unshift("#{__dir__}/../lib")
5
5
 
6
- require 'smart_todo'
6
+ require "smart_todo"
7
7
 
8
- unless ENV['ENABLE_SMART_TODO']
9
- puts 'Not running SmartTodo since the ENABLE_SMART_TODO ENV is not set'
10
-
11
- exit(0)
8
+ if ENV["ENABLE_SMART_TODO"] && !ARGV.include?("--dispatcher")
9
+ ARGV << "--dispatcher" << "slack"
12
10
  end
13
11
 
14
12
  SmartTodo::CLI.new.run
@@ -15,40 +15,46 @@ module SmartTodo
15
15
  def run(args = ARGV)
16
16
  paths = define_options.parse!(args)
17
17
  validate_options!
18
- paths << '.' if paths.empty?
18
+ paths << "." if paths.empty?
19
19
 
20
20
  paths.each do |path|
21
21
  normalize_path(path).each do |file|
22
22
  parse_file(file)
23
23
 
24
- STDOUT.print('.')
24
+ STDOUT.print(".")
25
25
  STDOUT.flush
26
26
  end
27
27
  end
28
28
  end
29
29
 
30
- # @raise [ArgumentError] if the +slack_token+ or the +fallback_channel+
31
- # options are not passed to the command line
30
+ # @raise [ArgumentError] In case an option needed by a dispatcher wasn't provided.
31
+ #
32
32
  # @return [void]
33
33
  def validate_options!
34
- @options[:slack_token] ||= ENV.fetch('SMART_TODO_SLACK_TOKEN') { raise(ArgumentError, 'Missing :slack_token') }
35
-
36
- @options.fetch(:fallback_channel) { raise(ArgumentError, 'Missing :fallback_channel') }
34
+ dispatcher.validate_options!(@options)
37
35
  end
38
36
 
39
37
  # @return [OptionParser] an instance of OptionParser
40
38
  def define_options
41
39
  OptionParser.new do |opts|
42
40
  opts.banner = "Usage: smart_todo [options] file_or_path1 file_or_path2 ..."
43
- opts.on('--slack_token TOKEN') do |token|
41
+ opts.on("--slack_token TOKEN") do |token|
44
42
  @options[:slack_token] = token
45
43
  end
46
- opts.on('--fallback_channel CHANNEL') do |channel|
44
+ opts.on("--fallback_channel CHANNEL") do |channel|
47
45
  @options[:fallback_channel] = channel
48
46
  end
47
+ opts.on("--dispatcher DISPATCHER") do |dispatcher|
48
+ @options[:dispatcher] = dispatcher
49
+ end
49
50
  end
50
51
  end
51
52
 
53
+ # @return [Class] a Dispatchers::Base subclass
54
+ def dispatcher
55
+ @dispatcher ||= Dispatchers::Base.class_for(@options[:dispatcher])
56
+ end
57
+
52
58
  # @param path [String] a path to a file or directory
53
59
  # @return [Array<String>] all the directories the parser should run on
54
60
  def normalize_path(path)
@@ -61,13 +67,13 @@ module SmartTodo
61
67
 
62
68
  # @param file [String] a path to a file
63
69
  def parse_file(file)
64
- Parser::CommentParser.new(File.read(file, encoding: 'UTF-8')).parse.each do |todo_node|
70
+ Parser::CommentParser.new(File.read(file, encoding: "UTF-8")).parse.each do |todo_node|
65
71
  event_message = nil
66
72
  event_met = todo_node.metadata.events.find do |event|
67
73
  event_message = Events.public_send(event.method_name, *event.arguments)
68
74
  end
69
75
 
70
- Dispatcher.new(event_message, todo_node, file, @options).dispatch if event_met
76
+ dispatcher.new(event_message, todo_node, file, @options).dispatch if event_met
71
77
  end
72
78
  end
73
79
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartTodo
4
+ module Dispatchers
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
17
+ end
18
+ end
19
+
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")
29
+ end
30
+
31
+ # @param event_message [String] the success message associated
32
+ # a specific event
33
+ # @param todo_node [SmartTodo::Parser::TodoNode]
34
+ # @param file [String] the file containing the TODO
35
+ # @param options [Hash]
36
+ def initialize(event_message, todo_node, file, options)
37
+ @event_message = event_message
38
+ @todo_node = todo_node
39
+ @options = options
40
+ @file = file
41
+ @assignees = @todo_node.metadata.assignees
42
+ end
43
+
44
+ # This method gets called when a TODO reminder is expired and needs to be delivered.
45
+ # Dispatchers should implement this method to deliver the message where they need.
46
+ #
47
+ # @return void
48
+ def dispatch
49
+ raise(NotImplemetedError, "subclass responsability")
50
+ end
51
+
52
+ private
53
+
54
+ # Prepare the content of the message to send to the TODO assignee
55
+ #
56
+ # @param user [Hash] contain information about a user
57
+ # @param assignee [String] original string handle the slack message should be sent
58
+ # @return [String]
59
+ def slack_message(user, assignee)
60
+ header = if user.key?("fallback")
61
+ unexisting_user(assignee)
62
+ else
63
+ existing_user
64
+ end
65
+
66
+ <<~EOM
67
+ #{header}
68
+
69
+ You have an assigned TODO in the `#{@file}` file.
70
+ #{@event_message}
71
+
72
+ Here is the associated comment on your TODO:
73
+
74
+ ```
75
+ #{@todo_node.comment.strip}
76
+ ```
77
+ EOM
78
+ end
79
+
80
+ # Message in case a TODO's assignee doesn't exist in the Slack organization
81
+ #
82
+ # @param user [Hash]
83
+ # @return [String]
84
+ def unexisting_user(assignee)
85
+ "Hello :wave:,\n\n`#{assignee}` had an assigned TODO but this user or channel doesn't exist on Slack anymore."
86
+ end
87
+
88
+ # Hello message for user actually existing in the organization
89
+ def existing_user
90
+ "Hello :wave:,"
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartTodo
4
+ module Dispatchers
5
+ # A simple dispatcher that will output the reminder.
6
+ class Output < Base
7
+ def self.validate_options!(_); end
8
+
9
+ # @return void
10
+ def dispatch
11
+ puts slack_message({}, nil)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartTodo
4
+ module Dispatchers
5
+ # Dispatcher that sends TODO reminders on Slack. Assignees can be either individual
6
+ # (using the associated slack email address) or a channel.
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") }
10
+
11
+ options.fetch(:fallback_channel) { raise(ArgumentError, "Missing :fallback_channel") }
12
+ end
13
+
14
+ # Make a Slack API call to dispatch the message to each assignee
15
+ #
16
+ # @raise [SlackClient::Error] in case the Slack API returns an error
17
+ # other than `users_not_found`
18
+ #
19
+ # @return [Array] Slack response for each assignee a message was sent to
20
+ def dispatch
21
+ @assignees.each do |assignee|
22
+ dispatch_one(assignee)
23
+ end
24
+ end
25
+
26
+ # Make a Slack API call to dispatch the message to the user or channel
27
+ #
28
+ # @raise [SlackClient::Error] in case the Slack API returns an error
29
+ # other than `users_not_found`
30
+ #
31
+ # @param [String] the assignee handle string
32
+ # @return [Hash] the Slack response
33
+ def dispatch_one(assignee)
34
+ user = slack_user_or_channel(assignee)
35
+
36
+ client.post_message(user.dig("user", "id"), slack_message(user, assignee))
37
+ rescue SlackClient::Error => error
38
+ if ["users_not_found", "channel_not_found"].include?(error.error_code)
39
+ user = { "user" => { "id" => @options[:fallback_channel] }, "fallback" => true }
40
+ else
41
+ raise(error)
42
+ end
43
+
44
+ client.post_message(user.dig("user", "id"), slack_message(user, assignee))
45
+ end
46
+
47
+ private
48
+
49
+ # Returns a formatted hash containing either the user id of a slack user or
50
+ # the channel the message should be sent to.
51
+ #
52
+ # @return [Hash] a suited hash containing the user ID for a given individual or a slack channel
53
+ def slack_user_or_channel(assignee)
54
+ if assignee.include?("@")
55
+ client.lookup_user_by_email(assignee)
56
+ else
57
+ { "user" => { "id" => assignee } }
58
+ end
59
+ end
60
+
61
+ # @return [SlackClient] an instance of SlackClient
62
+ def client
63
+ @client ||= SlackClient.new(@options[:slack_token])
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'time'
3
+ require "time"
4
4
 
5
5
  module SmartTodo
6
6
  module Events
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem("bundler")
4
+ require "bundler"
5
+
6
+ module SmartTodo
7
+ module Events
8
+ # An event that compare the version of a gem specified in your Gemfile.lock
9
+ # with the expected version specifiers.
10
+ class GemBump
11
+ # @param gem_name [String]
12
+ # @param requirements [Array] a list of version specifiers.
13
+ # The specifiers are the same as the one used in Gemfiles or Gemspecs
14
+ #
15
+ # @example Expecting a specific version
16
+ # GemBump.new('rails', ['6.0'])
17
+ #
18
+ # @example Expecting a version in the 5.x.x series
19
+ # GemBump.new('rails', ['> 5.2', '< 6'])
20
+ def initialize(gem_name, requirements)
21
+ @gem_name = gem_name
22
+ @requirements = Gem::Requirement.new(requirements)
23
+ end
24
+
25
+ # @return [String, false]
26
+ def met?
27
+ return error_message if spec_set[@gem_name].empty?
28
+
29
+ installed_version = spec_set[@gem_name].first.version
30
+ if @requirements.satisfied_by?(installed_version)
31
+ message(installed_version)
32
+ else
33
+ false
34
+ end
35
+ end
36
+
37
+ # Error message send to Slack in case a gem couldn't be found
38
+ #
39
+ # @return [String]
40
+ def error_message
41
+ "The gem *#{@gem_name}* is not in your dependencies, I can't determine if your TODO is ready to be addressed."
42
+ end
43
+
44
+ # @return [String]
45
+ def message(version_number)
46
+ "The gem *#{@gem_name}* was updated to version *#{version_number}* and your TODO is now ready to be addressed."
47
+ end
48
+
49
+ private
50
+
51
+ # @return [Bundler::SpecSet] an instance of Bundler::SpecSet
52
+ def spec_set
53
+ @spec_set ||= Bundler.load.specs
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'net/http'
4
- require 'json'
3
+ require "net/http"
4
+ require "json"
5
5
 
6
6
  module SmartTodo
7
7
  module Events
@@ -30,7 +30,7 @@ module SmartTodo
30
30
  if response.code_type < Net::HTTPClientError
31
31
  error_message
32
32
  elsif (gem = version_released?(response.body))
33
- message(gem['number'])
33
+ message(gem["number"])
34
34
  else
35
35
  false
36
36
  end
@@ -54,13 +54,13 @@ module SmartTodo
54
54
  # @return [true, false]
55
55
  def version_released?(gem_versions)
56
56
  JSON.parse(gem_versions).find do |gem|
57
- @requirements.satisfied_by?(Gem::Version.new(gem['number']))
57
+ @requirements.satisfied_by?(Gem::Version.new(gem["number"]))
58
58
  end
59
59
  end
60
60
 
61
61
  # @return [Net::HTTP] an instance of Net::HTTP
62
62
  def client
63
- @client ||= Net::HTTP.new('rubygems.org', Net::HTTP.https_default_port).tap do |client|
63
+ @client ||= Net::HTTP.new("rubygems.org", Net::HTTP.https_default_port).tap do |client|
64
64
  client.use_ssl = true
65
65
  end
66
66
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'net/http'
4
- require 'json'
3
+ require "net/http"
4
+ require "json"
5
5
 
6
6
  module SmartTodo
7
7
  module Events
@@ -12,7 +12,7 @@ module SmartTodo
12
12
  # with the `repos` scope in the +SMART_TODO_GITHUB_TOKEN+ environment variable
13
13
  # is required.
14
14
  class IssueClose
15
- TOKEN_ENV = 'SMART_TODO_GITHUB_TOKEN'
15
+ TOKEN_ENV = "SMART_TODO_GITHUB_TOKEN"
16
16
 
17
17
  # @param organization [String]
18
18
  # @param repo [String]
@@ -62,7 +62,7 @@ module SmartTodo
62
62
 
63
63
  # @return [Net::HTTP] an instance of Net::HTTP
64
64
  def client
65
- @client ||= Net::HTTP.new('api.github.com', Net::HTTP.https_default_port).tap do |client|
65
+ @client ||= Net::HTTP.new("api.github.com", Net::HTTP.https_default_port).tap do |client|
66
66
  client.use_ssl = true
67
67
  end
68
68
  end
@@ -72,13 +72,13 @@ module SmartTodo
72
72
  #
73
73
  # @return [true, false]
74
74
  def pull_request_closed?(pull_request)
75
- JSON.parse(pull_request)['state'] == 'closed'
75
+ JSON.parse(pull_request)["state"] == "closed"
76
76
  end
77
77
 
78
78
  # @return [Hash]
79
79
  def default_headers
80
- { 'Accept' => 'application/vnd.github.v3+json' }.tap do |headers|
81
- headers['Authorization'] = "token #{ENV[TOKEN_ENV]}" if ENV[TOKEN_ENV]
80
+ { "Accept" => "application/vnd.github.v3+json" }.tap do |headers|
81
+ headers["Authorization"] = "token #{ENV[TOKEN_ENV]}" if ENV[TOKEN_ENV]
82
82
  end
83
83
  end
84
84
  end
@@ -38,6 +38,15 @@ module SmartTodo
38
38
  GemRelease.new(gem_name, requirements).met?
39
39
  end
40
40
 
41
+ # Check if +gem_name+ was bumped to the +requirements+ expected
42
+ #
43
+ # @param gem_name [String]
44
+ # @param requirements [Array<String>] a list of version specifiers
45
+ # @return [false, String]
46
+ def gem_bump(gem_name, *requirements)
47
+ GemBump.new(gem_name, requirements).met?
48
+ end
49
+
41
50
  # Check if the issue +issue_number+ is closed
42
51
  #
43
52
  # @param organization [String] the GitHub organization name
@@ -45,7 +54,7 @@ module SmartTodo
45
54
  # @param issue_number [String, Integer]
46
55
  # @return [false, String]
47
56
  def issue_close(organization, repo, issue_number)
48
- IssueClose.new(organization, repo, issue_number, type: 'issues').met?
57
+ IssueClose.new(organization, repo, issue_number, type: "issues").met?
49
58
  end
50
59
 
51
60
  # Check if the pull request +pr_number+ is closed
@@ -55,7 +64,7 @@ module SmartTodo
55
64
  # @param pr_number [String, Integer]
56
65
  # @return [false, String]
57
66
  def pull_request_close(organization, repo, pr_number)
58
- IssueClose.new(organization, repo, pr_number, type: 'pulls').met?
67
+ IssueClose.new(organization, repo, pr_number, type: "pulls").met?
59
68
  end
60
69
  end
61
70
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ripper'
3
+ require "ripper"
4
4
 
5
5
  module SmartTodo
6
6
  module Parser