正規表現の使い方

正規表現の使い方

正規表現で便利なものや、使い方が複雑なものを備忘録としてまとめます。

キャプチャグループ

[1] pry(main)> '123456789'.gsub(/^(\d{3}).*(\d{5})$/, '\1\2')
=> "12356789"

# キャプチャ: ()で括るとその部分がキャプチャされ、キャプチャされたものは最初から順に1から番号が割り当てられ、
第二引数にその番号を上記のように記載するとキャプチャされた部分だけを取得できる。

# グループ化: ()で括るとその部分がグループ化され、グループ化された部分が1つの塊として認識される。
その塊の後に[+]などを使用すると、塊で始まりそれ以降の任意の文字が[+]部分として許容されるようになる。
下記がそのイメージ検証

# つまり()で括るとキャプチャとグループ化の2つが行われる!

[1] pry(main)> 'abcabcabc'.gsub(/^(abc)/, 'z')
=> "zabcabc"
[2] pry(main)> 'abcabcabc'.gsub(/^abc/, 'z')
=> "zabcabc"
[3] pry(main)> 'abcabcabc'.gsub(/abc/, 'z')
=> "zzz"
[4] pry(main)> 'abcabcabc'.gsub(/(abc)/, 'z')
=> "zzz"
[5] pry(main)> 'abcabcabc'.gsub(/(abc)+/, 'z')
=> "z"
[6] pry(main)> 'abcabcabc'.gsub(/abc+/, 'z')
=> "zzz"

参考記事

今日からはじめよう、正規表現 in Ruby (()を使おう(キャプチャ・グループ化)編) - woshidan's blog

nextメソッドの使い方

nextメソッドの使い方

nextメソッドの動作の認識が誤っていたので、こちらに備忘録としてまとめます。

next使うと、その要素のままでそれ以降の処理がスキップされて次の要素に移ると思っていたが、 その際は、nilになってしまう。。。
そして、returnのようにnextにも引数が使えると知った。

[1, 2].map do |value|
  next if value == 1
  value * 2
end

→[nil, 4]
[1, 2].map do |value|
  next 1 if value == 1
  value * 2
end

→[1, 4]

よくよく考えたら、mapメソッドで要素をループしており、ブロックの中での処理結果を新しい配列の要素とするからnext if value == 1がtrueになるとnextメソッドに返り値を指定していないので、nilになる。。。。

可読性の高いクリーンなコード記載方法

可読性の高いクリーンなコード記載方法
現場で1年弱働いて、可読性の高いクリーンなコード記載を学んだので、備忘録としてこちらにまとめます。

三項演算子を利用して変数に値を設定

food = 'fruit'
food == 'fruit' ? select_food_type = 'フルーツ' : select_food_type = 'フルーツ以外'

↓

food = 'fruit'
select_food_type = food == 'fruit' ? 'フルーツ' : 'フルーツ以外'

# 上記で変数foodに[fruit]が格納されていれば、select_food_typeには[フルーツ]そうでなければ[フルーツ以外]が格納される

また、3項演算子の結果がboolean値で、そのboolean値を変数に格納したい場合は、以下のように書ける

food = 'fruit'
is_fruit = food == 'fruit'

# 上記で変数foodに[fruit]が格納されていれば、is_fruitには[true]そうでなければ[false]が格納される

同じ変数を比較する際は、case文を利用

def fruit_type(fruit)
  return 'りんご' if fruit == 'apple'
  return 'オレンジ' if fruit == 'orange'
  return 'もも' if fruit == 'peach'
enddef fruit_type(fruit)
  if fruit == 'apple'
    'りんご'
  if fruit == 'orange'
    'オレンジ'
  if fruit == 'peach'
    'もも'
  end
enddef fruit_type(fruit)
  case fruit
  when 'apple'
    'りんご'
  when 'orange'
    'オレンジ'
  when 'peach'
    'もも'
  end
end

2つの変数の組み合わせで返り値を返す際にcase文を利用

def like_tuna_and_salmon(is_like_tuna, is_like_salmon)
  case [is_like_tuna, is_like_salmon]
  when [true, true]
    'マグロもサーモンも好き'
  when [true, false]
    'マグロは好きだが、サーモンは嫌い'
  when [false, true]
    'マグロは嫌いだが、サーモンは好き'
  when [false, false]
    'マグロもサーモンも嫌い'
  end
end

# 上記のように配列を使うことで、2つの変数の組み合わせを表せる

if文のネストを浅くするための[早期リターン]や[ガード節]という記法

def number_division_judge(number)
  if number.negative?
    'マイナスの値です。'
  else
    if number < 100
      '100未満の値です。'
    else
      '100以上の値です。'
    end
  end
enddef number_division_judge(number)
  return 'マイナスの値です。' if number.negative?
    
  if number < 100
    '100未満の値です。'
  else
    '100以上の値です。'
  end
enddef number_division_judge(number)
  return 'マイナスの値です。' if number.negative?
    
  number < 100 ? '100未満の値です。' : '100以上の値です。'
end

2次配列の要素を複数の変数に1度で代入する方法

[16] pry(main)> x, y = [1, 2, 3, 4, 5, 6, 7, 8, 9].partition { |number| number & 3 == 0 }
=> [[4, 8], [1, 2, 3, 5, 6, 7, 9]]
[17] pry(main)> x
=> [4, 8]
[18] pry(main)> y
=> [1, 2, 3, 5, 6, 7, 9]

スプレッド構文を使うと配列の要素が配列になっていてもflattenを使ったのと同じ状態にできる

[1] pry(main)> [
[2] pry(main)*   'a',
[3] pry(main)*   'b:cde'.scan(/(?<=:).*/),
[4] pry(main)* ]  
=> ["a", ["cde"]]
[5] pry(main)> [
[6] pry(main)*   'a',
[7] pry(main)*   *'b:cde'.scan(/(?<=:).*/),
[8] pry(main)* ]  
=> ["a", "cde"]
[9] pry(main)> [
[10] pry(main)*   'a',
[11] pry(main)*   *['cde'],
[12] pry(main)* ]  
=> ["a", "cde"]

変数の中身がStringであるものの同士の足し算は、行わない方が良い

[1] pry(main)> a = 'a'
=> "a"
[2] pry(main)> b = 'b'
=> "b"
[3] pry(main)> c = a + b
=> "ab"
[4] pry(main)> a = nil
=> nil
[5] pry(main)> c = a + b
NoMethodError: undefined method `+' for nil:NilClass
from (pry):40:in `__pry__'

# 上記のように変数が万が一nilだった場合にエラーになってしまうからだ。
以下のように記載すると良い!

[6] pry(main)> c = [a, b].join
=> "b"
[7] pry(main)> c = "#{a}#{b}"
=> "b"

sprintfよりはrjustを使ったほうが良い

# rubyのsprintfはrubyのバージョンや与える値、指定コードによって奇妙な動作を引き起こすことがあるので、rubyの標準関数であるrjustなどで同じことができるのであれば、そちらを利用したほうがバグなども発生しずらい為だ

[1] pry(main)> sprintf('%03d', '1')
=> "001"
[2] pry(main)> '1'.rjust(3, '0')
=> "001"

Rubyのbegin-end構文

# nilガードで変数に代入する際に複数行を記述する際に用いる

[1] pry(main)> def test(test1)
[2] pry(main)*   @z ||= begin
[3] pry(main)*     if test1.nil?
[4] pry(main)*       'nilです。'
[5] pry(main)*     else  
[6] pry(main)*       'nil以外です。'
[7] pry(main)*     end  
[8] pry(main)*   end  
[9] pry(main)* end  
=> :test
[10] pry(main)> test(nil)
=> "nilです。"
[11] pry(main)> @z
=> "nilです。"

参考記事

Rubyのbegin-end構文で変数を定義する - その辺にいるWebエンジニアの備忘録

What is use of ||= begin....end block in Ruby? - Stack Overflow

exitdetachの違い

% docker ps      ①動いているプロセスを確認
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

% docker ps -a              ②止まっているプロセス含めて全て確認
CONTAINER ID   IMAGE                    COMMAND                  CREATED             STATUS                           PORTS                                                  NAMES
a390a72e29d7   ubuntu                   "bash"                   About an hour ago   Exited (0) 2 minutes ago                                                                clever_neumann
4a36cf90b2e7   ubuntu                   "bash"                   About an hour ago   Exited (130) About an hour ago                                                          angry_curran
32a488ae863e   hello-world              "/hello"                 About an hour ago   Exited (0) About an hour ago                                                            clever_allen
693ca907f6fa   order_form_app           "entrypoint.sh bash …"   5 days ago          Exited (0) 24 hours ago                                                                 order_form_app_1
ae66d6f030c0   schickling/mailcatcher   "sh -c 'mailcatcher …"   5 days ago          Exited (0) 24 hours ago                                                                 order_form_mailcatcher_1
43aa51f96062   redis:7.0                "docker-entrypoint.s…"   5 days ago          Exited (0) 24 hours ago                                                                 order_form_redis_1
1e5a9d4ac473   get_info_by_sql          "docker-entrypoint.s…"   16 months ago       Exited (255) 8 weeks ago         0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   get_info_by_sql
2805557644bd   ubuntu                   "ls"                     16 months ago       Exited (0) 16 months ago                                                                confident_dewdney
1fecffe609d7   docker/getting-started   "/docker-entrypoint.…"   16 months ago       Exited (255) 8 weeks ago         0.0.0.0:80->80/tcp, :::80->80/tcp                      modest_germain

% docker restart a390a72e29d7      ③dockerのコンテナをupにしてプロセスを作る
a390a72e29d7

~ % docker ps             ④上記でプロセスを起動したので、起動したプロセスが確認できる                    
CONTAINER ID   IMAGE     COMMAND   CREATED             STATUS         PORTS     NAMES
a390a72e29d7   ubuntu    "bash"    About an hour ago   Up 5 seconds             clever_neumann

% docker exec -it a390a72e29d7 bash   ⑤起動したコンテナの中に入れる
root@a390a72e29d7:/# exit                            ⑥プロセスを終了してコンテナから抜ける
exit

% docker ps                 ⑥上記でプロセスを切ってコンテナから抜けたので、プロセスを確認すると無い
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

% docker restart a390a72e29d7      ⑦再度dockerのコンテナをupにしてプロセスを作る
a390a72e29d7

% docker ps                  ⑧上記でプロセスを起動したので、起動したプロセスが確認できる   
CONTAINER ID   IMAGE     COMMAND   CREATED             STATUS          PORTS     NAMES
a390a72e29d7   ubuntu    "bash"    About an hour ago   Up 33 seconds             clever_neumann

% docker attach a390a72e29d7       ⑨上記で起動したプロセスに入る 
root@a390a72e29d7:/# read escape sequence    ⑩ctrlキー + p + qでdetachする

% docker ps                 上記でdetachでコンテナから抜けたがプロセスが残ったままになっている
CONTAINER ID   IMAGE     COMMAND   CREATED             STATUS              PORTS     NAMES
a390a72e29d7   ubuntu    "bash"    About an hour ago   Up About a minute             clever_neumann

exitだとプロセスを切って、コンテナから抜けるがdetachだとプロセスを残したままコンテナから抜けることができる。

class SearchController < ApplicationController
  def search
    if postal_code = params[:postal_code]
      params = URI.encode_www_form({zipcode: postal_code})
      uri = URI.parse("http://zipcloud.ibsnet.co.jp/api/search?#{params}")
      response = Net::HTTP.get_response(uri)
      result = JSON.parse(response.body)
      if result["results"]
        @zipcode = result["results"][0]["zipcode"]
        @address1 = result["results"][0]["address1"]
        @address2 = result["results"][0]["address2"]
        @address3 = result["results"][0]["address3"]
      end
    end
  end
end

VS Codeの拡張機能

VS Code拡張機能

VS Code拡張機能で詰まったことがあったので、備忘録としてこちらにまとめます。

普段Railsで開発をしている時にhtmlファイルだとEmmet機能が使えるが、erbファイルだとEmmet機能が使えず不便なので調べたところ、Ruby on Rails Snippetsという拡張機能をインストールすればEmmet機能が使えることが分かった。(以下のアイコンになります。)

Image from Gyazo

上記の拡張機能をインストールしたところ問題が発生した。
元々は、以下のようにapplication.html.erbGemfileがカラーコードになっていたが、Ruby on Rails Snippetsをインストールしたところカラーコードでは無くなってしまった。

Image from Gyazo

Image from Gyazo

以下がRuby on Rails Snippetsインストール後

Image from Gyazo

Image from Gyazo

上記のようにEmmet機能は、有効になったがapplication.html.erbGemfileがカラーコードでは無くなってしまった。

色々調べたところ、VSCode Rubyという拡張機能をインストールすればカラーコードになるということが分かった。(以下のアイコンになります。)

Image from Gyazo

上記をインストールしたところ以下のようにカラーコードになって、Emmet機能も使えるようになった。

Image from Gyazo

Image from Gyazo

参考記事

mm参考書プログラミング

VSCodeの拡張機能でRailsと仲良くなる - Qiita

SendGridの導入

SendGridの導入

Railsの本番環境でメールを送信するようにする為には、一般的にSendGridorGmailを使います。
今回は、SendGridでメールを送信できるようにする為の方法を備忘録としてこちらにまとめます。

まずSendGridに登録します。
下記URLより新規登録が必要になります。
Email Delivery, API, Marketing Service | SendGrid


・Herokuのアドオン経由でも登録できますが、SendGridのアカウントが凍結される可能性が高いのでSendGridの公式から直接登録する方が良いです!!
・SendGridの登録には、フィッシング詐欺の観点などから比較的に厳しい審査があります。
SendGridを使用してメールを使うWebサイトのドメインと自分の名前が紐付いて分かるような画像などが必要になります。


SendGridのメール差出人情報を登録しておく必要があります。
まず、SendGridにログインしてAccount Detailsをクリックしてタイムゾーンとメールアドレス(ユーザー名)が正しく設定されていることを確認する。
これらの情報は、宛先リストのアップロード完了通知メールや、スケジュール配信に使われます。
以下の手順で差出人情報を登録します。
①Marketing > Sendersをクリック

②Sender Management画面の右上のCreate New Senderをクリック

③以下の情報を登録する
・From Name(From名):受信者に表示される送信者の名前
・From Email Address(Fromメールアドレス):メール送信者のメールアドレスとして表示される(自分のGmailなどを記載すると、そのGmailに認証のメールが届く)
・Reply-To Email Address(Reply-Toメールアドレス):受信者がメールの返信をする時に返信先のアドレスになる
・Company Address, City, State, Zip Code, Country(会社所在地):会社の所在地
・Nickname(一意な名前):差出人情報を識別するための名前(この名前が受信者に表示されることはない)


Fromアドレス宛に認証メールが届くので、Verify Sender Identityをクリック

これで差出人情報の登録は、終了


SendGridのAPIキーを発行します。
手順は、以下のようになります。
①SendGridのマイページでSettingsAPI Keysをクリック


Create API Keyをクリック


API Key Nameに作成するAPIキーの名前を入力
して、API Key PermissionsはデフォルトのFull Accessのままにして、Create & Viewをクリック
APIキーが発行されるので、コピーして安全な場所に保存する
Doneをクリック

これでAPIキーの発行は、終了


メール送信サーバーの設定は、以下のようになります。
コードは、以下のようになります。

[app/mailers/application_mailer.rb]

class ApplicationMailer < ActionMailer::Base
  default from: Rails.application.credentials.sendgrid.dig(:sender_address)    # 送信するアドレスを記載(このように記載している理由は、後で説明します)
  layout 'mailer'
end

念の為、開発環境でSendGridを利用する際の設定も以下に記載しておきます。

[config/environments/development.rb]

  config.action_mailer.perform_caching = false

  host = 'localhost:3000'

  config.action_mailer.default_url_options = { host: host, protocol: 'http' }

  config.action_mailer.raise_delivery_errors = true

  config.action_mailer.delivery_method = :smtp


  config.action_mailer.smtp_settings = {
    :address => 'smtp.sendgrid.net',
    :port => 587,
    :domain => 'localhost',
    :user_name => 'apikey',    #  user_nameは、apikeyにする
    :password => Rails.application.credentials.sendgrid.dig(:sendgrid_api_key),   #  パスワードには、発行したAPIキーを入力(この記載は、後で説明します)
    :authentication => :plain,
    :enable_starttls_auto => true
  }
[config/environments/production.rb]

  config.action_mailer.default_url_options = { host: '自分のアプリのURL' }

  config.action_mailer.perform_caching = false

  config.action_mailer.delivery_method = :smtp

  config.action_mailer.perform_deliveries = true

  config.action_mailer.default :charset => "utf-8"

  config.action_mailer.smtp_settings = {
    address:              'smtp.sendgrid.net',
    port:                 587,
    domain:               'heroku.com',
    user_name:            'apikey',   #  user_nameは、apikeyにする
    password:             Rails.application.credentials.sendgrid.dig(:sendgrid_api_key),    #  パスワードには、発行したAPIキーを入力(この記載は、後で説明します)
    authentication:       'plain',
    enable_starttls_auto: true }

これで使用するメールサーバーの設定は、終了


送信元のアドレスとメールサーバー設定のパスワードで、Rails.application.credentialsを用いているがこれは、config/credentials.yml.encに記載されている暗号化された値を復号して読み取る記述になる。
config/credentials.yml.encには、暗号化されたものが記載されている。
そもそもなぜ、config/credentials.yml.encのように暗号化されたものが必要になるかというと、APIキーやパスワード、メールアドレスなどの個人情報を設定ファイルなどに直接書き込むと、それをGitHubやサーバーにアップするのは、セキュリティ上危険なので、それらの値を暗号化したconfig/credentials.yml.encファイルを設置しておき、それらの値を読み込みたい時は、config/credentials.yml.encからmaster.keyで復号して読み込むようにする。
つまり、master.keyGitHubに公開したり、盗まれたりなどすると個人情報やAPIキーなどの重要情報がダダ漏れの状態になってしまう!!
その為、Railsでは通常.gitignoremaster.keyファイルが記載されて、リモートにアップされないようになっている。
以下にconfig/credentials.yml.encに値を設定して、それらを暗号化しそれを読み出すまでの流れを以下に示します。

①以下でconfig/credentials.yml.encを編集

$ EDITOR="vi" bin/rails credentials:edit

vimでファイルが開き、以下のように記載して設定する。

aws:
   access_key_id: aaabbbcccddd
   secret_access_key: eeefffggghhh

sendgrid:
   sendgrid_api_key: iiijjjkkklllmmm
   sender_address: nnnooopppqqq

secret_key_base: rrrssstttuuu

保存して閉じるとconfig/credentials.yml.encが更新されて違う暗号となる。
上記のようにconfig/credentials.yml.encに設定した値がconfig/credentials.yml.encに暗号化されて記載されるが、これを復号して読み込む方法は、以下のようになる。

[1] pry(main)> Rails.application.credentials.aws[:access_key_id]
=> aaabbbcccddd

[2] pry(main)> Rails.application.credentials.aws[:secret_access_key]
=> eeefffggghhh

[3] pry(main)> Rails.application.credentials.sendgrid[:sendgrid_api_key]
=> iiijjjkkklllmmm

[4] pry(main)> Rails.application.credentials.sendgrid[:sender_address]
=> nnnooopppqqq

[4] pry(main)> Rails.application.credentials.secret_key_base
=> rrrssstttuuu

これでAPIキーやメールアドレスを暗号化しての記載は、終了


herokuへデプロイする際は、.gitignoremaster.keyファイルを記載している為、herokuへmaster.keyがアップされないので、Rails.application.credentialsconfig/credentials.yml.encに記載されている暗号を復号できない。
herokuでは、以下のようにして復号化キーを環境変数RAILS_MASTER_KEYに登録する

heroku config:set RAILS_MASTER_KEY=********************************

ポイント
・Credentialsは、最初にconfig/master.keyに復号化キーを探索しにいき、そこになければ次に環境変数RAILS_MASTER_KEYを探索しにいく。
そこにも復号化キーがなければ復号化に失敗し、nilが返却される。
config/credentials.yml.encは、secret_key_baseによって暗号化される。
Credentialsにはsecret_key_baseが初めから登録されている。
これは、Cookieの暗号化に使用される文字列になります。
使わないからと言って削除しないように気を付けてください。

参考記事

[【Rails7】開発環境、本番環境でメール送信できるようにする(SendGrid, Gmail) | こばっちブログ

Rails5.2から追加された credentials.yml.enc のキホン - Qiita

Rails5.2でSendGridをつかってメールを送信する設定 | 煩悩八百万クリエイター戦記

本番環境とcredentials.yml.enc - Qiita

【Rails】Credentialsを使用した機密情報の保護 - AUTOVICE | 坂井光太郎のポートフォリオサイト

Heroku Addons で SendGrid を使ってる方が必要な年内対応 - ボクココ

SendGridとの連携 - ドキュメント | SendGrid

差出人情報(Senders)の管理 - ドキュメント | SendGrid