質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Redmine

Redmineは、プロジェクトのタスク管理、進捗管理、情報共有が可能な、 オープンソースプロジェクト管理ソフトウェアです。

Q&A

解決済

1回答

2701閲覧

undefined method `map' for nil:NilClassが解決できない

toritoner

総合スコア1

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Redmine

Redmineは、プロジェクトのタスク管理、進捗管理、情報共有が可能な、 オープンソースプロジェクト管理ソフトウェアです。

0グッド

0クリップ

投稿2020/06/16 07:01

編集2020/06/17 08:09

前提・実現したいこと

Redmine-slackプラグインをbitnami版Redmineに導入し、
カスタマイズして、slack上でメンションが飛ぶようにしようとしています。

発生している問題・エラーメッセージ

Completed 500 Internal Server Error in 272ms (ActiveRecord: 87.5ms) NoMethodError (undefined method `map' for nil:NilClass): plugins/redmine_slack/lib/redmine_slack/listener.rb:207:in `getSlackID' plugins/redmine_slack/lib/redmine_slack/listener.rb:15:in `redmine_slack_issues_new_after_save' lib/redmine/hook.rb:61:in `block (2 levels) in call_hook' lib/redmine/hook.rb:61:in `each' lib/redmine/hook.rb:61:in `block in call_hook' lib/redmine/hook.rb:58:in `tap' lib/redmine/hook.rb:58:in `call_hook' plugins/redmine_slack/lib/redmine_slack/issue_patch.rb:20:in `create_from_issue' app/models/issue.rb:209:in `create_or_update' app/controllers/issues_controller.rb:130:in `create' lib/redmine/sudo_mode.rb:63:in `sudo_mode'

該当のソースコード

ruby

1require 'httpclient' 2require 'slack' 3 4class SlackListener < Redmine::Hook::Listener 5 def redmine_slack_issues_new_after_save(context={}) 6 issue = context[:issue] 7 8 channel = channel_for_project issue.project 9 url = url_for_project issue.project 10 11 return unless channel and url 12 return if issue.is_private? 13 14 if issue.assigned_to 15 assigned_slackId = getSlackID issue.assigned_to.login 16 msg = "<@#{assigned_slackId}> [#{escape issue.project}] Assigned by #{escape issue.author} <#{object_url issue}|#{escape issue}>#{mentions issue.description}" 17 else 18 msg = "[#{escape issue.project}] Created by #{escape issue.author} <#{object_url issue}|#{escape issue}>#{mentions issue.description}" 19 end 20 21 attachment = {} 22 attachment[:text] = escape issue.description if issue.description 23 attachment[:fields] = [{ 24 :title => I18n.t("field_status"), 25 :value => escape(issue.status.to_s), 26 :short => true 27 }, { 28 :title => I18n.t("field_priority"), 29 :value => escape(issue.priority.to_s), 30 :short => true 31 }, { 32 :title => I18n.t("field_assigned_to"), 33 :value => escape(issue.assigned_to.to_s), 34 :short => true 35 }] 36 37 attachment[:fields] << { 38 :title => I18n.t("field_watcher"), 39 :value => escape(issue.watcher_users.join(', ')), 40 :short => true 41 } if Setting.plugin_redmine_slack['display_watchers'] == 'yes' 42 43 # directSpeak issue, msg, channel, attachment, url 44 speak msg, channel, attachment, url 45 end 46 47 def redmine_slack_issues_edit_after_save(context={}) 48 issue = context[:issue] 49 journal = context[:journal] 50 51 channel = channel_for_project issue.project 52 url = url_for_project issue.project 53 original_issue = context[:params][:original_issue] 54 55 return unless channel and url and Setting.plugin_redmine_slack['post_updates'] == '1' 56 return if issue.is_private? 57 return if journal.private_notes? 58 59 if issue.assigned_to.login == journal.user.login 60 msg = "[#{escape issue.project}] Updated by #{escape journal.user.to_s} <#{object_url issue}|#{escape issue}>#{mentions issue.notes}" 61 else 62 assigned_slackId = getSlackID issue.assigned_to.login 63 msg = "<@#{assigned_slackId}> [#{escape issue.project}] Updated by #{escape journal.user.to_s} <#{object_url issue}|#{escape issue}>#{mentions issue.notes}" 64 end 65 66 attachment = {} 67 attachment[:text] = escape journal.notes if journal.notes 68 attachment[:fields] = journal.details.map { |d| detail_to_field d } 69 70 # directSpeak issue, msg, channel, attachment, url 71 speak msg, channel, attachment, url 72 end 73 74 def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context={}) 75 issue = context[:issue] 76 journal = issue.current_journal 77 changeset = context[:changeset] 78 79 channel = channel_for_project issue.project 80 url = url_for_project issue.project 81 82 return unless channel and url and issue.save 83 return if issue.is_private? 84 85 if issue.assigned_to.login == journal.user.login 86 msg = "Updated by #{escape journal.user.to_s} <#{object_url issue}|#{escape issue}>" 87 else 88 assigned_slackId = getSlackID issue.assigned_to.login 89 msg = "<@#{assigned_slackId}> Updated by #{escape journal.user.to_s} <#{object_url issue}|#{escape issue}>" 90 end 91 92 repository = changeset.repository 93 94 if Setting.host_name.to_s =~ /\A(https?\://)?(.+?)(\:(\d+))?(/.+)?\z/i 95 host, port, prefix = $2, $4, $5 96 revision_url = Rails.application.routes.url_for( 97 :controller => 'repositories', 98 :action => 'revision', 99 :id => repository.project, 100 :repository_id => repository.identifier_param, 101 :rev => changeset.revision, 102 :host => host, 103 :protocol => Setting.protocol, 104 :port => port, 105 :script_name => prefix 106 ) 107 else 108 revision_url = Rails.application.routes.url_for( 109 :controller => 'repositories', 110 :action => 'revision', 111 :id => repository.project, 112 :repository_id => repository.identifier_param, 113 :rev => changeset.revision, 114 :host => Setting.host_name, 115 :protocol => Setting.protocol 116 ) 117 end 118 119 attachment = {} 120 attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{escape changeset.comments}>") 121 attachment[:fields] = journal.details.map { |d| detail_to_field d } 122 123 # directSpeak issue, msg, channel, attachment, url 124 speak msg, channel, attachment, url 125 end 126 127 def controller_wiki_edit_after_save(context = { }) 128 return unless Setting.plugin_redmine_slack['post_wiki_updates'] == '1' 129 130 project = context[:project] 131 page = context[:page] 132 133 user = page.content.author 134 project_url = "<#{object_url project}|#{escape project}>" 135 page_url = "<#{object_url page}|#{page.title}>" 136 comment = "[#{project_url}] Wiki #{page_url} updated by *#{user}*" 137 if page.content.version > 1 138 comment << " [<#{object_url page}/diff?version=#{page.content.version}|difference>]" 139 end 140 141 channel = channel_for_project project 142 url = url_for_project project 143 144 attachment = nil 145 if not page.content.comments.empty? 146 attachment = {} 147 attachment[:text] = "#{escape page.content.comments}" 148 end 149 150 speak comment, channel, attachment, url 151 end 152 153 def speak(msg, channel, attachment=nil, url=nil) 154 url = Setting.plugin_redmine_slack['slack_url'] if not url 155 username = Setting.plugin_redmine_slack['username'] 156 icon = Setting.plugin_redmine_slack['icon'] 157 158 params = { 159 :text => msg, 160 :link_names => 1, 161 } 162 163 params[:username] = username if username 164 params[:channel] = channel if channel 165 166 params[:attachments] = [attachment] if attachment 167 168 if icon and not icon.empty? 169 if icon.start_with? ':' 170 params[:icon_emoji] = icon 171 else 172 params[:icon_url] = icon 173 end 174 end 175 176 end 177 178 def getSlackID(user_name) ←207行目 179 @token = "<token>" 180 181 Slack.configure {|config| config.token = @token } 182 client = Slack::Client.new 183 184 client.users_list['members'].map{ |v| 185 if v['real_name'] == "#{user_name}" 186 return "#{v['id']}" 187 end 188 } 189 end 190 191private 192 def escape(msg) 193 msg.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;") 194 end 195 196 def url_for_project(proj) 197 return nil if proj.blank? 198 199 cf = ProjectCustomField.find_by_name("Slack URL") 200 201 return [ 202 (proj.custom_value_for(cf).value rescue nil), 203 (url_for_project proj.parent), 204 Setting.plugin_redmine_slack['slack_url'], 205 ].find{|v| v.present?} 206 end 207 208 def channel_for_project(proj) 209 return nil if proj.blank? 210 211 cf = ProjectCustomField.find_by_name("Slack Channel") 212 213 val = [ 214 (proj.custom_value_for(cf).value rescue nil), 215 (channel_for_project proj.parent), 216 Setting.plugin_redmine_slack['channel'], 217 ].find{|v| v.present?} 218 219 # Channel name '-' is reserved for NOT notifying 220 return nil if val.to_s == '-' 221 val 222 end 223 224 225 def mentions text 226 return nil if text.nil? 227 names = extract_usernames text 228 names.present? ? "\nTo: " + names.join(', ') : nil 229 end 230 231 def extract_usernames text = '' 232 if text.nil? 233 text = '' 234 end 235 236 # slack usernames may only contain lowercase letters, numbers, 237 # dashes and underscores and must start with a letter or number. 238 text.scan(/@[a-z0-9][a-z0-9_\-]*/).uniq 239 end 240end

試したこと

本エラーについていくつか調べてみましたが、
ruby初心者のため、
子のソースを具体的にどう修正するかまでたどり着けませんでした。

補足情報(FW/ツールのバージョンなど)

https://github.com/sciyoshi/redmine-slack

https://oratio.hatenablog.com/entry/2020/01/06/201852
こちらのブログを参考に実装しています。掲載したコードはほぼ同じです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2020/06/16 09:08

これだか長いと 207行目がどこなのか確定が大変です。 その行の後ろに `#### ← 207行` とでも入れてください。
toritoner

2020/06/17 08:11

ご指摘ありがとうございます。お手数をおかけしてすみません。遅くなってしまいましたが追加しました。
guest

回答1

0

ベストアンサー

client.users_list['members'].map{ |v| ここかな?
だとすると

client = Slack::Client.new client.users_list['members'].map{ |v|

client はその直前でnewされてます。つまりものはあるがデータはみな空。
ですので、client.users_list['members'] も空 => nil。

Slack.configureからclientを取り出す所を調べなおしてください。

投稿2020/06/16 13:06

winterboum

総合スコア23329

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toritoner

2020/06/18 10:19

ありがとうございました! ログでclientの中身を確認したところ、 "error"=>"missing_scope"が吐かれていることが確認でき、 slackAPI側の設定に抜けがあることがわかりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問