smart_todo 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module SmartTodo
7
+ # A simple client around the Slack API.
8
+ #
9
+ # @example Sending a message to a user.
10
+ # SmartTodo::SlackClient.new.post_message('#general', 'Hello!')
11
+ class SlackClient
12
+ # A generic error class raised when the Slack API returns back a 200
13
+ # but there was a problem (permission issues ...)
14
+ class Error < StandardError
15
+ attr_reader :error_code
16
+
17
+ # @param response_body [Hash] the parsed response body from Slack
18
+ def initialize(response_body)
19
+ @error_code = response_body['error']
20
+
21
+ super("Response body: #{response_body}")
22
+ end
23
+ end
24
+
25
+ # @param slack_token [String]
26
+ def initialize(slack_token)
27
+ @slack_token = slack_token
28
+ @client = Net::HTTP.new('slack.com', Net::HTTP.https_default_port).tap do |client|
29
+ client.use_ssl = true
30
+ client.read_timeout = 30
31
+ client.ssl_timeout = 15
32
+ end
33
+ end
34
+
35
+ # Retrieve the Slack ID of a user from his email
36
+ #
37
+ # @param email [String]
38
+ # @return [Hash]
39
+ #
40
+ # @raise [Net::HTTPError] in case the reques to Slack failed
41
+ # @raise [SlackClient::Error] in case Slack returs a { ok: false } in the body
42
+ #
43
+ # @see https://api.slack.com/methods/users.lookupByEmail
44
+ def lookup_user_by_email(email)
45
+ headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
46
+
47
+ request(:get, "/api/users.lookupByEmail?email=#{email}", nil, headers)
48
+ end
49
+
50
+ # Send a message to a Slack channel or to a user
51
+ #
52
+ # @param channel [String] The Slack channel or the user ID
53
+ # @param text [String] The message to send
54
+ # @return [Hash]
55
+ #
56
+ # @raise [Net::HTTPError] in case the reques to Slack failed
57
+ # @raise [SlackClient::Error] in case Slack returs a { ok: false } in the body
58
+ #
59
+ # @see https://api.slack.com/methods/chat.postMessage
60
+ def post_message(channel, text)
61
+ request(:post, '/api/chat.postMessage', JSON.dump(channel: channel, text: text))
62
+ end
63
+
64
+ private
65
+
66
+ # @param method [Symbol]
67
+ # @param endpoint [String]
68
+ # @param data [String] JSON encoded data when making a POST request
69
+ # @param headers [Hash]
70
+ #
71
+ # @raise [Net::HTTPError] in case the reques to Slack failed
72
+ # @raise [SlackClient::Error] in case Slack returs a { ok: false } in the body
73
+ def request(method, endpoint, data = nil, headers = {})
74
+ response = case method
75
+ when :post, :patch
76
+ @client.public_send(method, endpoint, data, default_headers.merge(headers))
77
+ else
78
+ @client.public_send(method, endpoint, default_headers.merge(headers))
79
+ end
80
+
81
+ slack_response!(response)
82
+ end
83
+
84
+ # Chech if the response to Slack was a 200 and the Slack API request was successful
85
+ #
86
+ # @param response [Net::HTTPResponse] a net Net::HTTPResponse subclass
87
+ # (Net::HTTPOK, Net::HTTPNotFound ...)
88
+ # @return [Hash]
89
+ #
90
+ # @raise [Net::HTTPError] in case the reques to Slack failed
91
+ # @raise [SlackClient::Error] in case Slack returs a { ok: false } in the body
92
+ def slack_response!(response)
93
+ raise(Net::HTTPError.new('Request to slack failed', response)) unless response.code_type < Net::HTTPSuccess
94
+ body = JSON.parse(response.body)
95
+
96
+ if body['ok']
97
+ body
98
+ else
99
+ raise(Error, body)
100
+ end
101
+ end
102
+
103
+ # The default headers required by Slack
104
+ #
105
+ # @return [Hash]
106
+ def default_headers
107
+ {
108
+ 'Content-Type' => 'application/json; charset=utf8',
109
+ 'Authorization' => "Bearer #{@slack_token}",
110
+ }
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartTodo
4
+ VERSION = "1.0.0"
5
+ end
data/lib/smart_todo.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "smart_todo/version"
4
+ require "smart_todo/events"
5
+
6
+ module SmartTodo
7
+ autoload :SlackClient, 'smart_todo/slack_client'
8
+ autoload :CLI, 'smart_todo/cli'
9
+ autoload :Dispatcher, 'smart_todo/dispatcher'
10
+
11
+ module Parser
12
+ autoload :CommentParser, 'smart_todo/parser/comment_parser'
13
+ autoload :TodoNode, 'smart_todo/parser/todo_node'
14
+ autoload :MetadataParser, 'smart_todo/parser/metadata_parser'
15
+ end
16
+
17
+ module Events
18
+ autoload :Date, 'smart_todo/events/date'
19
+ autoload :GemRelease, 'smart_todo/events/gem_release'
20
+ autoload :PullRequestClose, 'smart_todo/events/pull_request_close'
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_todo/parser/metadata_parser'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module SmartTodo
8
+ # A RuboCop used to restrict the usage of regular TODO comments in code.
9
+ # This Cop does not run by default. It should be added to the RuboCop host's configuration file.
10
+ #
11
+ # @see https://rubocop.readthedocs.io/en/latest/extensions/#loading-extensions
12
+ class SmartTodoCop < Cop
13
+ MSG = "Don't write regular TODO comments. Write SmartTodo compatible syntax comments." \
14
+ "For more info please look at https://github.com/shopify/smart_todo"
15
+
16
+ # @param processed_source [RuboCop::ProcessedSource]
17
+ # @return [void]
18
+ def investigate(processed_source)
19
+ processed_source.comments.each do |comment|
20
+ next unless /^#\sTODO/ =~ comment.text
21
+ next if smart_todo?(comment.text)
22
+
23
+ add_offense(comment)
24
+ end
25
+ end
26
+
27
+ # @param comment [String]
28
+ # @return [true, false]
29
+ def smart_todo?(comment)
30
+ metadata = ::SmartTodo::Parser::MetadataParser.parse(comment.gsub(/^#/, ''))
31
+
32
+ metadata.events.any?
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1 @@
1
+ # using the default shipit config
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "smart_todo/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "smart_todo"
9
+ spec.version = SmartTodo::VERSION
10
+ spec.authors = ["Shopify"]
11
+ spec.email = ["rails@shopify.com"]
12
+
13
+ spec.summary = "Enhance todo's comments in your codebase."
14
+ spec.description = <<~EOM
15
+ SmartTodo is a tool designed to assign specific users on todo's task
16
+ written in your codebase and help assignees be reminded when it's time to commit
17
+ to their todo's.
18
+ EOM
19
+ spec.homepage = "https://github.com/shopify/smart_todo"
20
+ spec.license = "MIT"
21
+
22
+ spec.metadata["homepage_uri"] = spec.homepage
23
+ spec.metadata["source_code_uri"] = spec.homepage
24
+ spec.metadata["changelog_uri"] = spec.homepage + "/blob/master/CHANGELOG.md"
25
+
26
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
27
+ %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = ['smart_todo']
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency("bundler", "~> 1.17")
34
+ spec.add_development_dependency("rake", "~> 10.0")
35
+ spec.add_development_dependency("minitest", "~> 5.0")
36
+ spec.add_development_dependency("webmock")
37
+ spec.add_development_dependency("rubocop")
38
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smart_todo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Shopify
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-07-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: |
84
+ SmartTodo is a tool designed to assign specific users on todo's task
85
+ written in your codebase and help assignees be reminded when it's time to commit
86
+ to their todo's.
87
+ email:
88
+ - rails@shopify.com
89
+ executables:
90
+ - smart_todo
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".gitignore"
95
+ - ".rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml"
96
+ - ".rubocop.yml"
97
+ - ".shopify-build/VERSION"
98
+ - ".shopify-build/smart-todo.yml"
99
+ - CODE_OF_CONDUCT.md
100
+ - Gemfile
101
+ - Gemfile.lock
102
+ - LICENSE.txt
103
+ - README.md
104
+ - Rakefile
105
+ - bin/console
106
+ - bin/rubocop
107
+ - bin/setup
108
+ - dev.yml
109
+ - exe/smart_todo
110
+ - lib/smart_todo.rb
111
+ - lib/smart_todo/cli.rb
112
+ - lib/smart_todo/dispatcher.rb
113
+ - lib/smart_todo/events.rb
114
+ - lib/smart_todo/events/date.rb
115
+ - lib/smart_todo/events/gem_release.rb
116
+ - lib/smart_todo/events/pull_request_close.rb
117
+ - lib/smart_todo/parser/comment_parser.rb
118
+ - lib/smart_todo/parser/metadata_parser.rb
119
+ - lib/smart_todo/parser/todo_node.rb
120
+ - lib/smart_todo/slack_client.rb
121
+ - lib/smart_todo/version.rb
122
+ - lib/smart_todo_cop.rb
123
+ - shipit.rubygems.yml
124
+ - smart_todo.gemspec
125
+ homepage: https://github.com/shopify/smart_todo
126
+ licenses:
127
+ - MIT
128
+ metadata:
129
+ homepage_uri: https://github.com/shopify/smart_todo
130
+ source_code_uri: https://github.com/shopify/smart_todo
131
+ changelog_uri: https://github.com/shopify/smart_todo/blob/master/CHANGELOG.md
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 2.7.6
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Enhance todo's comments in your codebase.
152
+ test_files: []