tokite 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd4cf7e3df9af634d1a79b71996448181b6d3992
4
- data.tar.gz: 99655b09461cd497c09704ae861a43f72b4bd429
3
+ metadata.gz: e5713f325c58d3d8111e0a7d23474ff6f5102a15
4
+ data.tar.gz: c8c4507ea06af90e808469887e83dc8038af430e
5
5
  SHA512:
6
- metadata.gz: b1faa8d9d5bef41d2fe78b5929d4f9d7e307bb616c827e179d755a5d3787cb48953d86b16afe0ccc39a1b1c22de5627561c803fd917c8229166be1f4c9bb2ca2
7
- data.tar.gz: 55b16a25db1f016a4e94f116d88d07124f475d790acdd5e9dc1367da895a10189cd38c71665235f8b75ce3da8fcfecd23340ede0fd4af359241fb7d9ebe8714b
6
+ metadata.gz: 8f13bf68e97bf9ce0b3ff43f1a6326964433545e1b4c6a6456da5371eecda96aa67f88d18e50c5591356cf7a0455f1a8557085f18703deb009eac3d808d9d602
7
+ data.tar.gz: 106be23f58cb08f37146689762decbd6e5d05a902c46ed03afb58392574c88b5715ee8fd0d7bb0a57bdc0d09fcb9c2fbd67f6bc59c5e9e4198afd723d5a98b09
@@ -2,8 +2,8 @@ module Tokite
2
2
  class ApplicationController < ActionController::Base
3
3
  protect_from_forgery with: :exception
4
4
 
5
- helper_method :current_user
6
-
5
+ helper_method :current_user, :current_user_token
6
+
7
7
  before_action :require_login
8
8
 
9
9
  private
@@ -11,10 +11,18 @@ module Tokite
11
11
  def current_user
12
12
  @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
13
13
  end
14
-
14
+
15
+ def current_user_token
16
+ current_user&.token
17
+ end
18
+
15
19
  def require_login
16
20
  return if current_user
17
21
  redirect_to sign_in_path
18
22
  end
23
+
24
+ def octokit_client
25
+ @octokit_client ||= Octokit::Client.new(access_token: current_user_token, auto_paginate: true)
26
+ end
19
27
  end
20
28
  end
@@ -5,6 +5,7 @@ module Tokite
5
5
  GITHUB_EVENT_HEADER = "X-GitHub-Event"
6
6
 
7
7
  def create
8
+ logger.debug("Hook triggered: #{github_event}")
8
9
  Hook.fire!(github_event, request.request_parameters)
9
10
  render plain: "ok"
10
11
  end
@@ -0,0 +1,41 @@
1
+ module Tokite
2
+ class RepositoriesController < ApplicationController
3
+ before_action :require_github_token, only: [:new, :create, :destroy]
4
+
5
+ def index
6
+ @repositories = Repository.all
7
+ end
8
+
9
+ def new
10
+ github_repos = octokit_client.repositories.select{|r| r.permissions.admin }.delete_if(&:fork)
11
+ @repositories = github_repos.map do |repo|
12
+ Repository.new(name: repo.full_name, url: repo.html_url)
13
+ end
14
+ Repository.all.pluck(:name).each do |existing_name|
15
+ @repositories.delete_if {|repo| repo.name == existing_name }
16
+ end
17
+ end
18
+
19
+ def create
20
+ params[:names].each do |name|
21
+ github_repo = octokit_client.repository(name)
22
+ Repository.hook!(octokit_client, github_repo)
23
+ end
24
+ flash[:info] = "Import repositories."
25
+ redirect_to repositories_path
26
+ end
27
+
28
+ def destroy
29
+ repo = Repository.find(params[:id])
30
+ repo.unhook!(octokit_client)
31
+ flash[:info] = "Unhook repository #{repo.name}"
32
+ redirect_to repositories_path
33
+ end
34
+
35
+ private
36
+
37
+ def require_github_token
38
+ redirect_to repositories_path unless current_user_token
39
+ end
40
+ end
41
+ end
@@ -5,49 +5,48 @@ module Tokite
5
5
  end
6
6
 
7
7
  def new
8
- @rule = rule_user.rules.build
8
+ @rule_user = User.find(params[:user_id])
9
+ @rule = @rule_user.rules.build
9
10
  end
10
11
 
11
12
  def create
12
- @rule = rule_user.rules.new(rule_params)
13
+ @rule_user = User.find(params[:user_id])
14
+ @rule = @rule_user.rules.new(rule_params)
13
15
  if @rule.save
14
16
  flash[:info] = "Rule created."
15
- redirect_to edit_user_rule_path(params[:user_id], @rule)
17
+ redirect_to user_path(@rule.user_id)
16
18
  else
17
19
  render "new"
18
20
  end
19
21
  end
20
22
 
21
23
  def edit
22
- @rule = rule_user.rules.find(params[:id])
24
+ @rule_user = nil
25
+ @rule = Rule.find(params[:id])
23
26
  end
24
27
 
25
28
  def update
26
- @rule = rule_user.rules.find(params[:id])
29
+ @rule = Rule.find(params[:id])
27
30
  @rule.assign_attributes(rule_params)
28
31
  if @rule.save
29
32
  flash[:info] = "Rule updated."
30
- redirect_to edit_user_rule_path(params[:user_id], @rule)
33
+ redirect_to user_path(@rule.user_id)
31
34
  else
32
35
  render "edit"
33
36
  end
34
37
  end
35
-
38
+
36
39
  def destroy
37
- @rule = rule_user.rules.find(params[:id])
40
+ @rule = Rule.find(params[:id])
38
41
  @rule.destroy!
39
42
  flash[:info] = "Rule deleted."
40
- redirect_to user_rules_path(params[:user_id])
43
+ redirect_to user_path(@rule.user_id)
41
44
  end
42
-
45
+
43
46
  private
44
-
45
- def rule_user
46
- @rule_user ||= User.find(params[:user_id])
47
- end
48
-
47
+
49
48
  def rule_params
50
- params.require(:rule).permit(:name, :query, :channel, :icon_emoji, :additional_text)
49
+ params.require(:rule).permit(:user_id, :name, :query, :channel, :icon_emoji, :additional_text)
51
50
  end
52
51
  end
53
52
  end
@@ -7,18 +7,7 @@ module Tokite
7
7
 
8
8
  def create
9
9
  auth = request.env["omniauth.auth"]
10
- user = User.find_or_initialize_by(
11
- provider: auth[:provider],
12
- uid: auth[:uid],
13
- )
14
- unless user.persisted?
15
- user.update!(
16
- name: auth[:info][:name],
17
- email: auth[:info][:email],
18
- image_url: auth[:info][:image],
19
- )
20
- end
21
-
10
+ user = User.login!(auth)
22
11
  session[:user_id] = user.id
23
12
  redirect_to root_path
24
13
  end
@@ -0,0 +1,14 @@
1
+ module Tokite
2
+ class TransfersController < ApplicationController
3
+ def new
4
+ @transfer = Transfer.new(rule_id: params[:rule_id])
5
+ @rule = @transfer.rule
6
+ @users = User.where.not(id: @rule.user_id)
7
+ end
8
+
9
+ def create
10
+ @transfer = Transfer.create!(rule_id: params[:rule_id], user_id: params[:transfer][:user_id])
11
+ redirect_to user_path(@transfer.rule.user_id)
12
+ end
13
+ end
14
+ end
@@ -26,5 +26,9 @@ module Tokite
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ def show_admin_menu?
31
+ params[:admin]
32
+ end
29
33
  end
30
34
  end
@@ -0,0 +1,41 @@
1
+ module Tokite
2
+ class Repository < ApplicationRecord
3
+ def self.hook!(octokit_client, github_repo)
4
+ repo_name = github_repo.full_name
5
+ existing_hook = find_hook(octokit_client, repo_name)
6
+ if existing_hook
7
+ octokit_client.edit_hook(repo_name, existing_hook.id, "web", hook_config, hook_options)
8
+ else
9
+ octokit_client.create_hook(repo_name, "web", hook_config, hook_options)
10
+ end
11
+ create!(name: repo_name, url: github_repo.html_url)
12
+ end
13
+
14
+ def self.find_hook(octokit_client, repo_name)
15
+ octokit_client.hooks(repo_name).find {|hook| hook.config.url == hooks_url }
16
+ end
17
+
18
+ def self.hook_config
19
+ {
20
+ content_type: "json",
21
+ url: hooks_url,
22
+ }
23
+ end
24
+
25
+ def self.hook_options
26
+ {
27
+ events: ["*"]
28
+ }
29
+ end
30
+
31
+ def self.hooks_url
32
+ Tokite::Engine.routes.url_helpers.hooks_url
33
+ end
34
+
35
+ def unhook!(octokit_client)
36
+ hook = self.class.find_hook(octokit_client, name)
37
+ octokit_client.remove_hook(name, hook.id) if hook
38
+ destroy!
39
+ end
40
+ end
41
+ end
@@ -5,7 +5,8 @@ module Tokite
5
5
  belongs_to :user
6
6
 
7
7
  validate :validate_query
8
-
8
+ validate :validate_user_id
9
+
9
10
  # TODO: Performance
10
11
  def self.matched_rules(event)
11
12
  Rule.all.to_a.select do |rule|
@@ -30,11 +31,11 @@ module Tokite
30
31
  end
31
32
 
32
33
  def rule_name_link
33
- "<#{Tokite::Engine.routes.url_helpers.edit_user_rule_url(user, self)}|#{name}>"
34
+ "<#{Tokite::Engine.routes.url_helpers.edit_rule_url(self)}|#{name}>"
34
35
  end
35
36
 
36
37
  def user_link
37
- "<#{Tokite::Engine.routes.url_helpers.user_rules_url(user)}|#{user.name}>"
38
+ "<#{Tokite::Engine.routes.url_helpers.user_url(user)}|#{user.name}>"
38
39
  end
39
40
 
40
41
  def validate_query
@@ -42,5 +43,11 @@ module Tokite
42
43
  rescue SearchQuery::ParseError => e
43
44
  errors.add(:query, e.message)
44
45
  end
46
+
47
+ def validate_user_id
48
+ unless User.find_by(id: user_id)
49
+ errors.add(:user_id, "Unknown user_id: #{user_id}")
50
+ end
51
+ end
45
52
  end
46
53
  end
@@ -0,0 +1,5 @@
1
+ module Tokite
2
+ class SecureUserToken < ApplicationRecord
3
+ belongs_to :user
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ module Tokite
2
+ class Transfer
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :rule_id, :user_id
6
+ attr_reader :rule
7
+
8
+ def self.create!(attributes)
9
+ transfer = new(attributes)
10
+ transfer.rule.update!(user_id: transfer.user_id)
11
+
12
+ return transfer
13
+ end
14
+
15
+ def rule
16
+ @rule ||= Rule.find(rule_id)
17
+ end
18
+ end
19
+ end
@@ -1,13 +1,36 @@
1
1
  module Tokite
2
2
  class User < ApplicationRecord
3
3
  has_many :rules, dependent: :destroy
4
-
4
+
5
+ has_one :secure_user_token
6
+
7
+ delegate :token, to: :secure_user_token, allow_nil: true
8
+
9
+ def self.login!(auth)
10
+ user = find_or_initialize_by(
11
+ provider: auth[:provider],
12
+ uid: auth[:uid],
13
+ )
14
+ if user.persisted?
15
+ user.secure_user_token.update!(token: auth[:credentials][:token])
16
+ else
17
+ user.assign_attributes(
18
+ name: auth[:info][:name],
19
+ image_url: auth[:info][:image],
20
+ )
21
+ user.build_secure_user_token(token: auth[:credentials][:token])
22
+ transaction do
23
+ user.save!
24
+ end
25
+ end
26
+ user
27
+ end
28
+
5
29
  def self.create_group_user!(name)
6
30
  uuid = SecureRandom.uuid.tr("-", "")
7
31
  create!(
8
32
  provider: "GROUP",
9
33
  uid: uuid,
10
- email: uuid,
11
34
  image_url: "",
12
35
  name: name
13
36
  )
@@ -16,5 +39,9 @@ module Tokite
16
39
  def group_user?
17
40
  provider == "GROUP"
18
41
  end
42
+
43
+ def name_with_provider
44
+ "#{name} (#{provider})"
45
+ end
19
46
  end
20
47
  end
@@ -1,5 +1,5 @@
1
1
  !!!
2
- %html
2
+ %html{lang: :en}
3
3
  %head
4
4
  %title Tokite
5
5
  = csrf_meta_tags
@@ -15,6 +15,9 @@
15
15
  = nav_list_item "Top", root_path, ["top"]
16
16
  = nav_list_item "Users", users_path, ["users"]
17
17
  = nav_list_item "Rules", rules_path, ["rules"]
18
+ = nav_list_item "Repositories", repositories_path, ["repositories"]
19
+ .nav-right
20
+ %span.nav-item.is-tab= current_user.name_with_provider
18
21
 
19
22
  %section.section
20
23
  - if flash[:info]
@@ -0,0 +1,13 @@
1
+ - if current_user_token
2
+ = link_to "Watch new repositories", new_repository_path, class: "button is-primary"
3
+
4
+ .vspace-40
5
+
6
+ %h1.title Watching repositories
7
+
8
+ %table.table.is-bordered
9
+ - @repositories.each do |repo|
10
+ %tr
11
+ %td= link_to repo.name, repo.url
12
+ - if show_admin_menu?
13
+ %td= link_to "Unwatch", repo, method: :delete, class: "button is-danger", data: { confirm: "Unwatch repository?" }
@@ -0,0 +1,18 @@
1
+ - if @repositories.present?
2
+ = form_tag repositories_path do |f|
3
+ %ul
4
+ - @repositories.each do |repo|
5
+ %li.field
6
+ .control
7
+ %label.checkbox
8
+ = check_box_tag "names[]", repo.name, false, id: "names_#{sanitize_to_id(repo.name)}", multiple: true
9
+ = repo.name
10
+ = link_to "@", repo.url
11
+
12
+ .vspace-40
13
+
14
+ .field
15
+ .control
16
+ = submit_tag "Import repositories", class: "button is-primary"
17
+ - else
18
+ No importable repositories.
@@ -4,10 +4,11 @@
4
4
  = form_text_field f, :channel, size: 40, class: "input"
5
5
  = form_text_field f, :icon_emoji, size: 40, class: "input"
6
6
  = form_text_field f, :additional_text, size: 40, class: "input"
7
- .field
8
- = f.submit class: "button is-primary"
7
+ .field.columns
8
+ .column.is-8= f.submit "Update", class: "button is-primary"
9
9
  - if @rule.persisted?
10
- = link_to "Delete rule", user_rule_path(@rule_user, @rule), method: :delete, data: { confirm: %(Delete "#{@rule.name}"?) }, class: "button is-danger is-pulled-right"
10
+ .column.is-2= link_to "Transfer", new_rule_transfers_path(@rule), class: "button is-primary"
11
+ .column.is-2= link_to "Delete", rule_path(@rule), method: :delete, data: { confirm: %(Delete "#{@rule.name}"?) }, class: "button is-danger"
11
12
 
12
13
  .message.vspace-40
13
14
  .message-header
@@ -2,7 +2,7 @@
2
2
  %h3 Rules
3
3
  %ul
4
4
  - user.rules.order(:id).each do |rule|
5
- %li= link_to rule.name, edit_user_rule_path(user, rule)
5
+ %li= link_to rule.name, edit_rule_path(rule)
6
6
 
7
7
  .content
8
8
  = link_to "New Rule", new_user_rule_path(user), class: "button is-primary"
@@ -1 +1 @@
1
- = link_to "Login with Google+", "/auth/google_oauth2"
1
+ = link_to "Login with GitHub", "/auth/github"
@@ -0,0 +1,8 @@
1
+ %h1.title
2
+ Transfer
3
+ = link_to @rule.name, edit_rule_path(@rule)
4
+
5
+ = form_for [@rule, @transfer] do |f|
6
+ = f.collection_select :user_id, @users, :id, :name_with_provider
7
+ .field.columns
8
+ .column.is-8= f.submit "Transfer", class: "button is-primary"
@@ -8,7 +8,7 @@
8
8
  %tr
9
9
  %td= user.id
10
10
  %td
11
- = link_to user.name, user_path(user)
11
+ = link_to user.name_with_provider, user_path(user)
12
12
 
13
13
  = link_to "Create group", users_path, method: :post, data: { confirm: %(Create Group?) }, class: "button is-primary"
14
14
 
@@ -0,0 +1,7 @@
1
+ require "octokit"
2
+
3
+ if ENV["GITHUB_HOST"].present?
4
+ Octokit.configure do |c|
5
+ c.api_endpoint = URI.join(ENV["GITHUB_HOST"], "/api/v3/").to_s
6
+ end
7
+ end
@@ -1,7 +1,16 @@
1
- require "omniauth-google-oauth2"
1
+ require "omniauth-github"
2
2
 
3
3
  Tokite::Engine.config.middleware.use OmniAuth::Builder do
4
- provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"], {
5
- hd: ENV["GOOGLE_HOSTED_DOMAIN"].present? ? ENV["GOOGLE_HOSTED_DOMAIN"] : nil,
6
- }
4
+ options = { scope: "repo,write:repo_hook" }
5
+ host = ENV["GITHUB_HOST"]
6
+ if host.present?
7
+ options.merge!(
8
+ client_options: {
9
+ site: "#{host}/api/v3",
10
+ authorize_url: "#{host}/login/oauth/authorize",
11
+ token_url: "#{host}/login/oauth/access_token",
12
+ }
13
+ )
14
+ end
15
+ provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], options
7
16
  end
data/config/routes.rb CHANGED
@@ -3,12 +3,15 @@ Tokite::Engine.routes.draw do
3
3
 
4
4
  resources :hooks, only: %w(create)
5
5
  resources :users, only: %w(index show create edit update destroy) do
6
- resources :rules, only: %w(new create edit update destroy)
6
+ resources :rules, only: %w(new create edit update destroy), shallow: true do
7
+ resource :transfers, only: %w(new create)
8
+ end
7
9
  end
8
10
  resources :rules, only: %w(index)
11
+ resources :repositories, only: %w(index new create destroy)
9
12
 
10
13
  get "sign_in", to: "sessions#new", as: "sign_in"
11
- get "auth/google_oauth2/callback", to: "sessions#create"
14
+ get "auth/github/callback", to: "sessions#create"
12
15
  delete "sign_out", to: "sessions#destroy", as: "sign_out"
13
16
 
14
17
  get "site/sha", to: "sha#show"
@@ -12,12 +12,7 @@ namespace :tokite do
12
12
  yml = Rails.root.join("config/database.yml").to_s
13
13
  sh "bundle", "exec", "ridgepole", "-c", yml, "-E", Rails.env, *args
14
14
  end
15
-
16
- desc "Export current schema"
17
- task :export do
18
- ridgepole_exec("--export", app_path("schema/Schemafile"), "--split")
19
- end
20
-
15
+
21
16
  desc "Apply Schemafile"
22
17
  task :apply do
23
18
  ridgepole_exec("--file", app_path("schema/Schemafile"), "-a")
@@ -30,10 +25,21 @@ namespace :tokite do
30
25
 
31
26
  desc "Install schema"
32
27
  task :install do
28
+ tokite_schema_dir = app_path("schema/tokite")
29
+ mkdir(tokite_schema_dir) unless Dir.exist?(tokite_schema_dir)
30
+
33
31
  schema_dir = app_path("schema")
34
32
  mkdir(schema_dir) unless Dir.exist?(schema_dir)
35
- Dir.glob("#{engine_path("schema")}/*").each do |f|
36
- cp f, schema_dir
33
+
34
+ Dir.glob("#{engine_path("schema")}/*").each do |src_path|
35
+ basename = File.basename(src_path)
36
+ if File.exist?(File.join(tokite_schema_dir, basename))
37
+ puts "Skip install schema #{src_path}"
38
+ else
39
+ puts "Install schema #{src_path}"
40
+ cp src_path, tokite_schema_dir
41
+ cp src_path, schema_dir
42
+ end
37
43
  end
38
44
  end
39
45
  end
@@ -1,3 +1,3 @@
1
1
  module Tokite
2
- VERSION = '0.1.5'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tokite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hogelog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2017-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -81,7 +81,21 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: omniauth-google-oauth2
84
+ name: omniauth-github
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: octokit
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - ">="
@@ -179,10 +193,12 @@ files:
179
193
  - app/assets/stylesheets/tokite/application.scss
180
194
  - app/controllers/tokite/application_controller.rb
181
195
  - app/controllers/tokite/hooks_controller.rb
196
+ - app/controllers/tokite/repositories_controller.rb
182
197
  - app/controllers/tokite/rules_controller.rb
183
198
  - app/controllers/tokite/sessions_controller.rb
184
199
  - app/controllers/tokite/sha_controller.rb
185
200
  - app/controllers/tokite/top_controller.rb
201
+ - app/controllers/tokite/transfers_controller.rb
186
202
  - app/controllers/tokite/users_controller.rb
187
203
  - app/helpers/tokite/application_helper.rb
188
204
  - app/jobs/tokite/application_job.rb
@@ -194,13 +210,18 @@ files:
194
210
  - app/models/tokite/hook_event/issue_comment.rb
195
211
  - app/models/tokite/hook_event/issues.rb
196
212
  - app/models/tokite/hook_event/pull_request.rb
213
+ - app/models/tokite/repository.rb
197
214
  - app/models/tokite/revision.rb
198
215
  - app/models/tokite/rule.rb
199
216
  - app/models/tokite/search_query.rb
217
+ - app/models/tokite/secure_user_token.rb
218
+ - app/models/tokite/transfer.rb
200
219
  - app/models/tokite/user.rb
201
220
  - app/views/layouts/tokite/application.html.haml
202
221
  - app/views/layouts/tokite/mailer.html.erb
203
222
  - app/views/layouts/tokite/mailer.text.erb
223
+ - app/views/tokite/repositories/index.html.haml
224
+ - app/views/tokite/repositories/new.html.haml
204
225
  - app/views/tokite/rules/_form.html.haml
205
226
  - app/views/tokite/rules/_rules.html.haml
206
227
  - app/views/tokite/rules/edit.html.haml
@@ -208,10 +229,12 @@ files:
208
229
  - app/views/tokite/rules/new.html.haml
209
230
  - app/views/tokite/sessions/new.html.haml
210
231
  - app/views/tokite/top/show.html.haml
232
+ - app/views/tokite/transfers/new.html.haml
211
233
  - app/views/tokite/users/_form.html.haml
212
234
  - app/views/tokite/users/edit.html.haml
213
235
  - app/views/tokite/users/index.html.haml
214
236
  - app/views/tokite/users/show.html.haml
237
+ - config/initializers/octokit.rb
215
238
  - config/initializers/omniauth.rb
216
239
  - config/initializers/slack_notifier.rb
217
240
  - config/routes.rb