「オープンソースのコードを修正したことがあります」と言えるように、簡単なケースで「プチ成功体験」を積み上げてみる

就職活動中に「私はインフラ設計も開発も出来ます。オープンソースのコードも必要に応じて修正して使っています」と言う人が来たら「ぜひ当社へ!!」と言いたくなってしまいますよね。

しかし、あまりそういうアピールをしてくれる人を見たことが無いので、オープンソースであるRedmineのコードを例に、「オープンソースを使う際に自身でもコードを修正してみる」簡単なケースを例にご紹介させて頂きます。

「設定でこうすれば~」と言うご意見もあると思いますが、目的はプチ成功体験の積み上げですので、その点はご容赦ください。

なお、Redmineのインストールは簡単なので、オフィシャルサイトを参考に、環境準備してみてください。

やりたいこと(記事の都合上、でっち上げのやりたいことです)

Redmineで新しいチケットを発行する際に、都度選択するのは面倒なので、担当者にはデフォルトでログイン中ユーザーを入れておきたい。

修正に向けたアプローチ方法

今まで触ったことのないコードに対して、調査して修正箇所を見つけて動作確認するところまでを実施してみます。その流れは以下の通りです。

  1. Redmineのソースコードが何処にあるかを探す(修正対象を見つける)
  2. ソースコード中から、修正箇所のあたりを付ける
  3. 修正する
  4. テストする

コメント

開発系の会社へ就職すると、アプリケーションをゼロから作る事を任せてもらえることは殆どなく、大半の場合には既存のコードを修正する事です。

そして、その際の指示は「この画面にこんな機能をつけて。」と数分の指示で終わってしまうことも多々あります。そういった場合にプチ成功体験があれば、「何処を直せばいいですか?」ではなく、「午前中に修正します。午後レビューをお願いします。至らない箇所はご指導ください」と応対することが出来、先輩からの評価も上がるかと思います。

なお、仕事の場合は開発環境としてVSC等を使っていると思いますが、開発環境のセットアップまでやっていたら1つの記事に収まらないので、今回は「linux上で今動いているコードを修正」しています。

1.Redmineのソースコードが何処にあるかを探す(修正対象を見つける)

私の環境では、redmineをApacheで動かしている為、「少なくともApache設定ファイルにRedmineのコードの場所が書かれているはず」と言う推測の元、/etc/apache2/sites-availableを探してみます。

「/etc/apache2/sites-available/」は、Apacheでサイト設定をする際に、その設定を登録するディレクトリです。a2ensiteコマンドで該当サイトを有効にすると「/etc/apache2/sites-enabled」に該当サイトへのシンボリックリンクが作成され、サイトが有効になります。

と言うことで、「/etc/apache2/sites-available/」に「redmine.conf」が存在し、「/etc/apache2/sites-enabled」からシンボリックリンクが張られていることを確認した為、「redmine.confでApacheのRedmine設定をしている」事がわかりました。

ちなみに、redmine.confの中身は以下の通りでした。

LoadModule passenger_module /usr/local/lib/ruby/gems/3.1.0/gems/passenger-6.0.15/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /usr/local/lib/ruby/gems/3.1.0/gems/passenger-6.0.15
  PassengerDefaultRuby /usr/local/bin/ruby
</IfModule>

PassengerMaxPoolSize 20
PassengerMaxInstancesPerApp 4
PassengerPoolIdleTime 864000
PassengerStatThrottleRate 10

<Directory /var/www/redmine/public>
    Allow from all
    Options -MultiViews
    Require all granted
</Directory>

Alias /redmine /var/www/redmine/public
<Location /redmine>
  PassengerBaseURI /redmine
  PassengerAppRoot /var/www/redmine
</Location>

「Alias /redmine /var/www/redmine/public」や「PassengerAppRoot /var/www/redmine」と言う箇所がある事から、「/var/www/redmine」に今回の修正対象がある事がわかりました。(publicの方はrailsで静的ファイルを置く場所です。つまりjsやcssファイル、画像ファイルが置かれています)

2.ソースコード中から、修正箇所のあたりを付ける

1番でどこにあるコードを修正すべきかわかったため、今度はgrep(テキスト検索ツール)を使って修正箇所のあたりをつけます。

「初めて見るソースコード」で修正箇所を見つけるためには、修正したい場所の近場にあるキーワードでgrepするのが手っ取り早いので、下図の「自分に割り当て」をgrepキーワードにしてみます。

# まずはgrepするソースコードのディレクトリに移動
cd /var/www/redmine

# findコマンドで対象ファイルを探してgrepする
$ find ./ -type f | xargs grep "自分に割り当て"
./config/locales/ja.yml:  label_assign_to_me: 自分に割り当て


「./config/locales/ja.yml: label_assign_to_me: 自分に割り当て」と言う箇所がgrepでヒットしました。ja.ymlファイルの他にも、en.ymlファイル等があり、利用者が自身の環境に沿ってロケールを変更する為のファイルがヒットしたようです。

そして、実際にソースコード中では「label_assign_to_me: 自分に割り当て」の赤字箇所「label_assign_to_me」として書かれているだろうと推察し、そのキーワードで更にgrepします。


$ find ./ -type f | xargs grep "label_assign_to_me"
./config/locales/sl.yml:  label_assign_to_me: Assign to me
./config/locales/mk.yml:  label_assign_to_me: Assign to me
./config/locales/ko.yml:  label_assign_to_me: Assign to me
./config/locales/lt.yml:  label_assign_to_me: Paskirti man
./config/locales/eu.yml:  label_assign_to_me: Assign to me
./config/locales/es.yml:  label_assign_to_me: Assign to me
./config/locales/fr.yml:  label_assign_to_me: M'assigner
./config/locales/bs.yml:  label_assign_to_me: Assign to me
./config/locales/cs.yml:  label_assign_to_me: Přiřadit na mě
./config/locales/en-GB.yml:  label_assign_to_me: Assign to me
./config/locales/ja.yml:  label_assign_to_me: 自分に割り当て
./config/locales/vi.yml:  label_assign_to_me: Assign to me
./config/locales/zh.yml:  label_assign_to_me: 指派给我
./config/locales/th.yml:  label_assign_to_me: Assign to me
./config/locales/tr.yml:  label_assign_to_me: Assign to me
./config/locales/sv.yml:  label_assign_to_me: Assign to me
./config/locales/en.yml:  label_assign_to_me: Assign to me
./config/locales/pt-BR.yml:  label_assign_to_me: Assign to me
./config/locales/ca.yml:  label_assign_to_me: Assign to me
./config/locales/ro.yml:  label_assign_to_me: Assign to me
./config/locales/sr-YU.yml:  label_assign_to_me: Assign to me
./config/locales/ar.yml:  label_assign_to_me: Assign to me
./config/locales/he.yml:  label_assign_to_me: Assign to me
./config/locales/sr.yml:  label_assign_to_me: Assign to me
./config/locales/gl.yml:  label_assign_to_me: Asignar a min
./config/locales/et.yml:  label_assign_to_me: Assign to me
./config/locales/pt.yml:  label_assign_to_me: Assign to me
./config/locales/es-PA.yml:  label_assign_to_me: Assign to me
./config/locales/el.yml:  label_assign_to_me: Assign to me
./config/locales/sq.yml:  label_assign_to_me: Caktomani mua
./config/locales/no.yml:  label_assign_to_me: Assign to me
./config/locales/zh-TW.yml:  label_assign_to_me: 分派給我
./config/locales/mn.yml:  label_assign_to_me: Assign to me
./config/locales/hr.yml:  label_assign_to_me: Assign to me
./config/locales/fa.yml:  label_assign_to_me: Assign to me
./config/locales/bg.yml:  label_assign_to_me: Назначи на мен
./config/locales/de.yml:  label_assign_to_me: Mir zuweisen
./config/locales/fi.yml:  label_assign_to_me: Assign to me
./config/locales/pl.yml:  label_assign_to_me: Przypisz do mnie
./config/locales/lv.yml:  label_assign_to_me: Assign to me
./config/locales/id.yml:  label_assign_to_me: Assign to me
./config/locales/ru.yml:  label_assign_to_me: Назначить мне
./config/locales/az.yml:  label_assign_to_me: Assign to me
./config/locales/nl.yml:  label_assign_to_me: Assign to me
./config/locales/it.yml:  label_assign_to_me: Assign to me
./config/locales/hu.yml:  label_assign_to_me: Assign to me
./config/locales/uk.yml:  label_assign_to_me: Assign to me
./config/locales/sk.yml:  label_assign_to_me: Assign to me
./config/locales/da.yml:  label_assign_to_me: Assign to me
./app/views/issues/_attributes.html.erb:    <a class="assign-to-me-link<%= ' hidden' if @issue.assigned_to_id == User.current.id %>" href="#" data-id="<%= User.current.id %>"><%= l(:label_assign_to_me) %></a>

「英語だと「Assign to me」となるんだな~」とか思いながら、見てみると、「/config/locales/*.yml」ファイルは全部多言語対応の為のファイルの為、ご参考程度にとどめていいことがわかります。

そうすると、残ったのは「./app/views/issues/_attributes.html.erb」ですから、該当ファイルを見てみます。

# viで該当ファイルを開く
vi ./app/views/issues/_attributes.html.erb

viで検索する場合には、上図の通り、Escキーを押した後に「/検索キーワード」とすることで、ファイル内で検索が出来ます。

viで「/label_assign_to_me」として検索してみると、以下のコードが該当しました。

<% if @issue.safe_attribute? 'assigned_to_id' %>
<p>
  <%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to),
                  :include_blank => true, :required => @issue.required_attribute?('assigned_to_id') %>
  <% if @issue.assignable_users.include?(User.current) %>
    <a class="assign-to-me-link<%= ' hidden' if @issue.assigned_to_id == User.current.id %>" href="#" data-id="<%= User.current.id %>"><%= l(:label_assign_to_me) %></a>
  <% end %>
</p>
<% end %>

ここで、「この近辺のコードを修正すれば良さそう」と気付くため、元々修正したかった画面と見比べてみます。

コードと見比べながら見ると、「<%= l(:label_assign_to_me) %>」で「./config/locales/ja.yml: label_assign_to_me: 自分に割り当て」の文字列を割り当てているのだと推測できます。

そう考えると、その直前の以下コード部分で担当者を出しているとわかります。

<% if @issue.safe_attribute? 'assigned_to_id' %>
<p>
  <%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to ),
                  :include_blank => true, :required => @issue.required_attribute?('assigned_to_id') %>
  <% if @issue.assignable_users.include?(User.current) %>
    <a class="assign-to-me-link<%= ' hidden' if @issue.assigned_to_id == User.current.id %>" href="#" data-id="<%= User.current.id %>"><%= l(:label_assign_to_me) %></a>
  <% end %>
</p>
<% end %>

ぱっと見、上記コードを眺めて、「railsでformのselectタグを使っているので、それのデフォルト値を入れるオプションを探せばいいので・・・・」と考え、ググった結果「selected => User.current.id」としようかと考えました(やってみて、sudo systemctl apache2.service restartしましたが、上手くデフォルト値が入りませんでした。)。

しかし、selectのoptionを出している箇所は「principals_options_for_select」のようだとあたりをつけ、該当コードをgrepすると、「./app/helpers/application_helper.rbの中に「principals_options_for_select(collection, selected=nil)」が定義されており、かつ、2つ目の引数でselected=nilと設定されている事がわかりました。

$ find ./ -type f | xargs grep principals_options_for_select
./test/helpers/application_helper_test.rb:  def test_principals_options_for_select_with_users

 ・・・割愛・・・

./app/helpers/timelog_helper.rb:    principals_options_for_select(collection, time_entry.user_id.to_s)
./app/helpers/projects_helper.rb:    principals_options_for_select(assignable_users, project.default_assigned_to)
./app/helpers/application_helper.rb:  def principals_options_for_select(collection, selected=nil)

selected=nil」の=nilの場所は、指定されていなければnil(JAVAで言うところのnull)を代入と言う意味ですので、principals_options_for_selectの第2引数にログイン中のユーザーIDを渡せばいいこととなります。

しかし、元々は@issie.assigned_toと言うものが第二引数に入っているようです。新規作成画面の呼び出し方によって、@issue.assigned_toが入るのでしょうから、これを単純に置換しては不味いと判断し、@issue.assigned_toがnilだったらUser.current.idを入れる事とします。

※ログイン中ユーザーがUser.current.idでとれることは、コード中の少し下の方でその記載がある事から容易に推測できました。

3.修正する

ここまで推測できたので、以下の通り修正します。

  • <p>タグの後に<%= User.current.id %> / <%= @issue.assigned_to %>を書き、それらに設定している値を確認できるようにする(テスト用)
  • principals_options_for_select(@issue.assignable_users, @issue.assigned_to || User.current.id.to_s )の赤字箇所を追加。||は、前の値が偽だったら後ろの値とするという意味。
  • 最後の赤字部分はご参考情報で、ログイン中ユーザーがUser.current.idである事に気付いた箇所です
<% if @issue.safe_attribute? 'assigned_to_id' %>
<p><%= User.current.id %> / <%= @issue.assigned_to %>
  <%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to || User.current.id.to_s ),
                  :include_blank => true, :required => @issue.required_attribute?('assigned_to_id') %>
  <% if @issue.assignable_users.include?(User.current) %>
    <a class="assign-to-me-link<%= ' hidden' if @issue.assigned_to_id == User.current.id %>" href="#" data-id="<%= User.current.id %>"><%= l(:label_assign_to_me) %></a>
  <% end %>
</p>
<% end %>

ここまで推測が出来たので、上記修正をして、Apacheを再起動します。

viで保存する場合には、ESCキーで編集モードを抜けた後に「:qw」と入力し、エンターキーで保存です。

# Apache再起動
sudo systemctl  restart apache2.service

4.テストする

その後、Redmineの新規チケット登録画面に遷移し、担当者にはデフォルトでログイン中ユーザーが入っていることを確認します。

<%= User.current.id %> / <%= @issue.assigned_to %>」と書いた箇所の結果が、担当者横に出ています。User.current.idが1で、@issue.assigned_toが値無しのようです。

ここまでで標準的な経路でチケット作成した場合のデフォルトユーザーをログイン中ユーザーとすることが出来ました。

当記事では、簡単な例をご紹介させて頂きましたが、自分以外が使うソフトウェアの場合、上記のテストだけでは不十分です。以下のようなケースを考慮し、更に動作確認・必要なら修正をしてみてください。きっと面白いはずです。

動作確認例

  • チケットの新規作成画面の他の呼び出し方は?
  • @issue.assigned_toが入るケースを試していないので、そのケースは?
  • 他にどんなテストケースがある?

5.後始末

一通りの動作確認が終わったら、テスト用に入れたコード(「<%= User.current.id %> / <%= @issue.assigned_to %>」)を削除して、最終系にして保存し、Apacheを再起動させてください。

最後に・・・

パーソル総合研究所が調査した「グローバル就業実態・成長意識調査(2022年)」の「社外の学習・自己啓発への自己投資」のグラフなのですが自己投資をしている・検討中の日本人の割合が40%程度と、他の国に比べてダントツに低い事が非常に気になり、システム系の方の就職・転職時に「(プライベートでも)オープンソースのコードを修正したことがあります。」とPR出来たら結構プラス評価なのではないかと思って書かせて頂きました。

パーソル総合研究所「グローバル就業実態・成長意識調査(2022年)」より抜粋

プチ成功体験を積み重ねれば、「今回はこんなことが出来た。」「次はそれを土台にこっちもやってみよう」と前向きな好循環となると思います。今回のコード修正は非常に軽微なものですが、プライベートでもそういった活動をすることは自己投資にあたると思いますので、1人でもそう言った活動をされている方が増えれば嬉しい限りです。

「こんな試みをすると更に良いよ」等のコメントがあれば、コメントいただけますと幸甚です。

タグ . ブックマークする パーマリンク.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください