20200426のRubyに関する記事は30件です。

【Rails】deviseを用いたユーザー新規登録機能の実装(基礎〜少し応用)

はじめに

Ruby on Rails を用いてWEBアプリを作成中です。
ユーザー新規登録の機能を実装するために、「devise」というgemを用いることにしました。
備忘もかねて、本文に実装した内容を残しておきます。
errorで困ったポイントも残しておきますので、参考にしていただけると幸いです^^

専門用語や参考にさせていただいた記事は最後に記載させていただいております。
「これ、なんで記述してるんだろう?」と、私自身が初めて作成した時に感じたポイントでもありますので、
初心者の方は気になるところかとも思います。
そちらも参照していただければと思います。

今回の方法はあくまで一つの実装方法でしかなく、やり方は色々あるのと、
私もまだ未熟なため、わかりにくい記述や余分な記述もございますし、調べきれてない、試しきれてないところもございます。
共にブラッシュアップしていければとも思ってますので、ご指摘もいただければと思います。

記載しきれなかった部分は、随時更新か、別途記事を用意いたします。

実装の目標

今回は以下の画像のような表示を目標として実装しました。
全てを解説するのは大変だったので、「ニックネーム」「メールアドレス」「パスワード」「生年月日」のみに絞っておりますのでご了承ください。
カラムのカスタマイズ、エラー表示、パスワードの表示/非表示、生年月日の選択など、「devise」の基本だけでなく、少し工夫が必要な内容も網羅してるかと思いますので、参考になれば幸いです。
Screenshot from Gyazo
Screenshot from Gyazo

1. deviseのインストール手順

 1-1. Gemfileに追記

(前略)
gem 'devise'
(後略)

※注意
「development、test、production」のgroup内に記述すると特定の環境でのみ使用する設定となりますので、group外に記述するようにしてください。

 1-2. コマンドの実行(インストール)

$ bundle install
$ rails g devise:install

2. Userモデルを作成

インストール後、モデルとテーブルを作成するために、以下をターミナルで実行してください。
通常のモデル作成とはコマンドが違うので注意してください。

$ rails g devise user 

マイグレーションファイルとモデルファイルが出来るので、以下のように記述ください。

2020*****_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :nickname, null: false
      t.string :email, null: false, default: ""
      t.string :password, null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.date :birthday, null: false

      # 〜省略〜

    end

    add_index :users, :email, unique: true

    # 〜省略〜

  end
end
app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable,  :validatable

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  VALID_PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i

  # 〜省略〜

  validates :nickname, presence: true, length: { maximum: 20 }
  validates :email, presence: true, uniqueness: true, format: { with: VALID_EMAIL_REGEX }
  validates :password, presence: true, length: { minimum: 7 }, format: { with: VALID_PASSWORD_REGEX }
  validates :birthday, presence: true

  # 〜省略〜

end

モデル「user.rb」について
「VALID_EMAIL_REGEX」「VALID_PASSWORD_REGEX」は正規表現によって、特定の文字を弾くようにしております。
「presence: true」は記述することで、空で登録することを弾くようにしてます。
「length: { maximum: 20 }」と「length: { minimum: 7 }」は文字数制限です。

ここで、以下の「devise.rb」に記述されてる「config.password_length = 6..128」についても、次のように編集しておきます。
この数字は文字制限を表してます(つまり、minimumが「6」です)。
理由はモデル「user.rb」より、今回passwordは7文字以上としているため、minimumが「7」である必要があります。
また、こちらを編集後はサーバーの再起動をしないと、反映されませんのでご注意を。

「devise.rb」の「config.password_length = 6..128」を編集した理由は、そのままにするとモデル「user.rb」側のバリデーション「length: { minimum: 7 }」と「devise.rb」側のバリデーション「config.password_length = 6..128」が共存することになるため、2つのバリデーションに引っかかって、2つのエラー表示されてしまうからです。
どちらか片方を削除する方法もありかと思いますが、経験もかねて、どちらも残す方法しか試せておりません。

config/initializers/devise.rb
# frozen_string_literal: true

# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
  Rails.application.credentials[:secret_key_base]

  # 〜省略〜

  config.password_length = 7..128

  # 〜省略〜

end

最後にmigrationファイルをデータベースに反映するために、以下のコマンドを実行。

$ rails db:migrate

3. コントローラーの編集

Viewなどを先に提示してもよかったのですが、エラー表示などについての説明を一緒にした方が良いと判断し、ルーティングやコントローラーの設定を先に説明いたします。

まずはコントローラーから。
今回はユーザー新規登録(サインアップ/sign_up)の実装についてですので、コントローラーは「application_controller.rb」のみで大丈夫です。
以下のように記載します。

「configure_permitted_parameters」メソッドの定義をしてますが、deviseをインストールすることでdevise_parameter_sanitizerのpermitメソッドが使えるようになります。これがストロングパラメータに該当する機能です。サインアップ時に入力された「nickname、email、password、birthday」のキーの内容の保存を許可しています。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    added_attrs = [ :nickname, :email, :password, :birthday ]
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
  end
end

4. ルーティングの編集

以下のように記載してください。

注意点としては、「routes.rb」の順番です。
「users/:id」が「users/sign_in」を包括してエラーが発生しないように、「devise_for :users」を「resources :users」より先に書く必要があります。

また、deviseでサインアップする際に、例えばパスワードを忘れて保存しようとしてエラーが発生すると、親ページにリダイレクトされます。
つまり、サインアップページ「/users/sign_up」でエラーが発生した場合、「/users」にリダイレクトされてしまいます。
そのまま登録するとルーティングエラーが表示されます。

これを回避するために、「devise_scope :users」以下の記述を追記して、任意のルーティングをさせています。
方法はいくつかありますが、今回は手っ取り早そうです。

config/routes.rb
Rails.application.routes.draw do

  devise_for :users

  devise_scope :users do
    get '/users', to: redirect("/users/sign_up")
  end

  # 〜省略〜

end

5. Veiwの編集

まずは、以下のコマンドを実行してください。Modelに対応するViewが生成されます。
Modelには「users」などを入れる文献が多ので、以下は「$ rails g devise:views users」で実行してます。
私が学んだ某スクールで最初に学んだ時は「devise」でしたので、ユーザーマイページと分けるために、私の場合は「devise」で生成してますが。

$ rails g devise:views Model

※生成されるViewファイル
 app/views/users/confirmations/new.html.erb
 app/views/users/mailer/confirmation_instructions.html.erb
 app/views/users/mailer/password_change.html.erb
 app/views/users/mailer/reset_password_instructions.html.erb
 app/views/users/mailer/unlock_instructions.html.erb
 app/views/users/passwords/edit.html.erb
 app/views/users/passwords/new.html.erb
 app/views/users/registrations/edit.html.erb
 app/views/users/registrations/new.html.erb
 app/views/users/sessions/new.html.erb
 app/views/users/shared/_links.html.erb
 app/views/users/unlocks/new.html.erb

 

 5-1. HTML/HAMLの記述

「registrations/new.html.erb」をユーザー新規登録ページとして編集していきます。
また、以下の通り、HTMLからHAMLに変換して記述してます。

app/views/users/registrations/new.haml.erb
.signup__main
    .signup__main__content
      %h2.signup__main__content__head
        会員情報入力

      = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|

     # 〜省略〜

              .field
                .field-label 
                  = f.label 'ニックネーム'
                %span.form-require 必須
                .field-input
                  = f.text_field :nickname, class: "field-input-full", autofocus: true, autocomplete: "nickname", placeholder: "例)メルカリ太郎"
                .input-error
                  = resource.errors.full_messages_for(:nickname)[0]

              .field
                .field-label
                  = f.label 'メールアドレス'
                %span.form-require 必須
                .field-input
                  = f.email_field :email, class: "field-input-full", autofocus: true, autocomplete: "email", placeholder: "PC・携帯どちらでも可"
                .input-error
                  = resource.errors.full_messages_for(:email)[0]

              .field
                .field-label 
                  = f.label :password, 'パスワード'
                  %span.form-require 必須
                .field-input.toggle
                  = f.password_field :password, class: "field-input-full", autocomplete: "off", autocomplete: "password", placeholder: "7文字以上の半角英数字", id:'password'
                  .checkbox-field
                    %input#js-passcheck.checkbox.js-password-toggle{type: "checkbox"}
                    %label.btn-label.js-password-label{for: "js-passcheck"}
                      %i.fas.fa-eye-slash{style: "font-size:20px;color:#808080"}
                .input-error
                  = resource.errors.full_messages_for(:password)[0]
                .field-info
                  ※ 英字と数字の両方を含めて設定してください

              .field
                .field-label 
                  = f.label '生年月日'
                %span.form-require 必須
                .birthday-select
                  = raw sprintf(f.date_select( :birthday, use_two_digit_numbers: true, prompt: "--", start_year: Time.now.year, end_year: 1900, date_separator: '%s'), '年 ', '月 ') + "日"
                .input-error
                  = resource.errors.full_messages_for(:birthday)[0]

     # 〜省略〜

              .actions
                = f.submit "登録", class: 'btn'

 

 5-2. エラー表示

今回のエラー表示は、ActiveRecordのvalidatesでエラーになった時に表示されるメッセージを利用します。
上記のように、バリデーションエラーは「errors.full_messages_for(:attribute_name)」で各attributeのエラーメッセージは取得してます。

なるほど!
ん?「各attribute」って何?

まずは何も考えずに、ネット上から「devise.ja.yml」をダウンロードし、「/config/locales」に保存してください。
次に、「devise.ja.yml」に以下を追記。
ここに追記した、「birthday、nickname、email、password」が「attribute」です。

ついでに、日本語化も実施。
「ja.yml」をネットからダウンロードし、先ほどと同じく「/config/locale/」以下に入れる。
「"%{attribute}%{message}"」とあるように、バリデーションエラーの内容に合わせて、「attribute」と「message」を選択してくれる。

config/locales/devise.ja.yml
ja:
  activerecord:
    attributes:
      user:
        birthday: "生年月日"
        nickname: "ニックネーム"
        email: Eメール
        password: パスワード

   # 〜省略〜
config/locales/ja.yml
 # 〜省略〜

   errors:
     format: "%{attribute}%{message}"
     messages:
       accepted: を受諾してください
       blank: を入力してください

 # 〜省略〜

 

 5-3. パスワードの表示/非表示

「/registrations/new.haml.erb」のパスワード入力は、何もしなければ非表示です。
何かしらのアクションを起こしたら、表示出来るようにしないと、利用者にとってわかりにくかったりします。
今回は、最近よく見る「目のマーク」を押したら表示/非表示を切り替えられるようにjavascriptとSCSSを駆使して表現しました。
参考までに一部コードを提示します。
ポイントとしては、「(password).attr('type','text');」「(password).attr('type','password');」を切り替えることで、「f.password_field」が持ってる「type」を切り替えることができ、表示/非表示を切り替えることができます。

app/views/users/registrations/new.haml.erb
     # 〜省略〜

  .field-input.toggle
    = f.password_field :password, class: "field-input-full", autocomplete: "off", autocomplete: "password", placeholder: "7文字以上の半角英数字", id:'password'
    .checkbox-field
      %input#js-passcheck.checkbox.js-password-toggle{type: "checkbox"}
      %label.btn-label.js-password-label{for: "js-passcheck"}
        %i.fas.fa-eye-slash{style: "font-size:20px;color:#808080"}

     # 〜省略〜
stylesheets/modules/users.scss
    .toggle{ 
      position: relative;
      .checkbox-field{
        position: absolute;
        right: 15px;
        top: 45px;
        .checkbox {
          display: none;
        }
      }
    }
app/assets/javascripts/registrations_new.js
$(function(){
  var password = '#password';
  var passcheck = '#js-passcheck';
  $(passcheck).change(function(){
    const passwordLabel = document.querySelector('.js-password-label');
    if ($(this).prop('checked')){
      $(password).attr('type','text');
      passwordLabel.innerHTML = '<i class="fas fa-eye" style="font-size:20px;color:#808080"></i>';
    } else {
      $(password).attr('type','password');
      passwordLabel.innerHTML = '<i class="fas fa-eye-slash" style="font-size:20px;color:#808080"></i>';
    }
  })
})

 5-4. 生年月日の選択

以下の「new.haml.erb」の通り、「date_selectタグ」という方法を使って実装しました。
超簡単です。

app/views/users/registrations/new.haml.erb
     # 〜省略〜

  .birthday-select
    = raw sprintf(f.date_select( :birthday, use_two_digit_numbers: true, prompt: "--", start_year: Time.now.year, end_year: 1900, date_separator: '%s'), '年 ', '月 ') + "日"

     # 〜省略〜

ですが1点、注意点があります。
バリデーションエラーが発生すると、「field_with_errors」クラスのdivタグが挿入され、「+ "日"」が切り離されてしまい、以下の画像のようにズレてしまいます。
Screenshot from Gyazo

【バリデーションエラー発生時】app/views/users/registrations/new.haml.erb
     # 〜省略〜

  .birthday-select
    = raw sprintf(f.date_select( :birthday, use_two_digit_numbers: true, prompt: "--", start_year: Time.now.year, end_year: 1900, date_separator: '%s'), '年 ', '月 ')
 .field_with_errors
   + "日"

     # 〜省略〜

これを回避する方法はいくつかあるようですが、今回は以下のように、「config/application.rb」に「config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag }」を追記しました。
追記後、サーバーを再起動すると、追記が反映され、Railsによる自動挿入が回避できます。

config/application.rb
   # 〜省略〜

module FleamarketAppTeamA
  class Application < Rails::Application

   # 〜省略〜

    config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag }
  end
end


最後に

最後まで読んでいただきありがとうございます。
以上で、ユーザー新規登録についての実装は終わりです。

本記事はカスタムの部分で、大枠の作業フローと、小技的なところで参考にはなるかと思いますので、活用いただければ幸いです。
もっとわかりやすい記事、個々の機能で詳しく記載された記事はたくさんございますので、そこに至るまでのキッカケにもなればとも思っております。

今回私も、以下の記事を主に参考にさせていただきました。
作者の方々には感謝申し上げます。

量が多いため割愛させていただいておりますが、他にも参考にした記事はたくさんありますので、それら記事にも感謝です。
本記事を参考にされる方は、それらの記事も参考にしていただければと思います。

参考記事

・deviseについて
-> rails devise完全入門!結局deviseって何ができるの?
-> 【Rails】deviseの使い方を徹底解説!
-> Deviseでログイン機能をつくる [Ruby on Rails]
-> Deviseの設定手順をまとめてみた。 その1 導入編

・encrypted_password
->Gem Deviseによるパスワードの保存及び保安方法

・add_index
->データベースにindexを張る方法

・正規表現とは
-> https://www.megasoft.co.jp/mifes/seiki/about.html

・正規表現(ライブラリ)
-> https://github.com/kkos/oniguruma/blob/master/doc/RE.ja
-> http://k-takata.o.oo7.jp/mysoft/bregonig.html

・正規表現(確認ツール)
-> https://rubular.com/

・protect_from_forgery with: :exception(CSRF対策)
-> RailsのCSRF保護を詳しく調べてみた(翻訳)

・日本語化
-> https://qiita.com/kusu_tweet/items/b534c808ac1ee0382f05)

・エラー表示
-> ActiveRecordのvalidatesで表示されるエラーメッセージのフォーマットを変更する
-> 【Rails】バリデーションのエラーメッセージを取得・表示・日本語化する方法を完全解説!

・生年月日の選択
-> 【Rails】date_selectタグの使い方メモ
-> Railsのバリデーションエラーで、「field_with_errors」によるレイアウト崩れを防ぐ

辞書

・gem
 便利な機能をひとまとめにしたもの(ライブラリ)

・devise
 ユーザー新規登録/ログインといった認証機能を簡単に実装できるgemのこと

・encrypted_password
 暗号化されたパスワードを保存するカラム

・add_index
 特定のカラムからデータを取得する際に、テーブルの中の特定のカラムのデータを複製し検索が行いやすいようにする

・正規表現
 「検索」や「置換」で指定する文字列をパターン表現する方法で、プログラミング言語やテキストエディタなどで利用できる

・protect_from_forgery with: :exception
 セキュリティ対策。このコードがあると、Railsで生成されるすべてのフォームとAjaxリクエストにセキュリティトークンが自動的に含まれ、セキュリティトークンがマッチしない場合ははじかれる。ユーザー認証が完了したwebアプリのページに悪意のあるコードやリンクを仕込むCSRF(Cross-Site Request Forgery)という攻撃手法から保護出来る。

・before_action
 全てのアクションが実行される前に、この部分が実行される

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails開発でハマったto_modelのエラー

 railsでフリマアプリをチーム開発してる時、あまり見かけないエラーに遭遇したので、具体的な原因はよくわかっていませんが、残しておきます。
 rubyバージョン :2.5.1
 railsバージョン :5.2.4.2
 データベース   :mysql

エラーが発生したコード

_product.html.haml
 =image_tag product.images.first.images

productモデルとimageモデルが一対多の関係で結びついており、productモデルにはaccepts_nested_attributes_for :imagesが書かれています。

エラー画面

image.png

to_modelという記述はどこにもしていませんし、to_xmlというxml形式で出力させる形式のメソッドをどう使えばいいのかわかりません。

解決したコード

_product.html.haml
  = image_tag product.images.first.images.url

 このエラーの"to_model”の意味は調べてみたもののよくわかっていませんが、データの呼び出しに失敗しているエラー(このオブジェクトにはこんなメソッドは存在しないよという意味のエラー)だと思うので呼び出し方を変えたらうまくいきました。
 あまり中身のない記事ですが、参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyでBOM付UTF-8のCSV(Excelで直接読めるCSV)を出力する方法

Excelで読み取れるCSV

文字コード UTF-8 のCSVファイルをExcelで開くと、以下のように文字化けする。
image.png

文字化けをさせないためには先頭にBOM(バイトオーダーマーク)を付ける必要がある。

RubyでBOMを出力するサンプル

bom.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

# BOMを出力する
putc 0xEF
putc 0xBB
putc 0xBF
# CSVを出力する
puts 'ID,名前'
puts '1,山田'
puts '2,田中'
puts '3,坂本'

実行

./bom.rb > excel.csv

とするとExcelで読み込めるCSVファイル(excel.csv)ができる。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

競技プログラミングでよく出てくる書き方

input = gets.split.map(&:to_i)

gets・・キーボードで入力した値を文字列で取得

split・・文字列を分割して配列にする、引数を指定しないばあい、空白文字で区切られる

map・・配列の要素の数だけ繰り返し処理を行う、collectと同じ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのダウングレードでハマった話

Railsのダウングレードでハマった話

Railsのダウングレードでハマってしまった内容を記録します。
初歩的なミスですが、同じような初心者の方に参考になればと思います。

目次


動作環境

OS : macOS Mojave 10.14.6
ruby : 2.6.3p62
rails : 5.2.4.2 -> 5.2.3

問題

ローカル開発環境をRails 5.2.4.2 -> 5.2.3にダウングレードする必要がありました。
Railsをアンインストール -> 指定バージョンをインストールしても、
なかなかバージョンのダウングレードができない状況でした。

既存バージョンのRailsのアンインストール

1. gem uninstall rails 5.2.4.2
2. gem uninstall railties 5.2.4.2

指定バージョンのRailsのインストール

gem 'rails', '~> 5.2.3' # Gemfileに記載
bundle install 
rails -v #バージョン確認
Rails 5.2.4.2 #変わっていない。

原因と解決法

Gemfile内のこの~>部分がある場合は指定されたバージョン以上で最新のものが利用されるとのこと。
つまり 5.2.3を利用するなら~>を使わずに記載します。

*バージョン指定についてはこちらの記事が参考になりました。
https://qiita.com/awakia/items/5745938c192ca1139c63

gem 'rails', '~> 5.2.3' # 5.2.3以上5.3.0未満のなかで最新のもの。5.2.4.2がインストールされてしまう。
gem 'rails', '5.2.3' # 5.2.3を使用!
bundle install #改めてgemをインストール
rails -v 
Rails 5.2.3 #指定のバージョンに変わりました。

学んだこと

今回の件で以下のことを学びました。
1. gemfile~>は記載されたバージョン以上で最新のものがインストールされる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Capistranoを噛み砕く

近況報告

 実はコーヒーマイスターを前職で取得した経緯がありまして,若干のコーヒーの知識があるので,少しひけらかしたいと思います。まず,コーヒーってどんな飲み物のイメージがあります?多分,苦いイメージですよね。では,現在世界的に高品質なコーヒーはどのように評価されているでしょうか。実は苦さの評価はありません。酸味の評価が主流です。

 理由はいたって簡単。苦味は焙煎によって生まれるものであり,酸味はコーヒー豆がそもそも持っているものだからです。細かい説明はしませんが,生のコーヒー豆は食べられたものではありません,それを美味しくするために火を入れます。その時に豆の成分が焦げて苦味になります。酸味は熱によって分解されていくので,結果として深煎りのコーヒーは酸味のないものになります。一方,苦味は焙煎度合いや焙煎師の腕,設備により変化します。ということは,豆が本来持っている酸味が評価の基準になるのはまあ納得ですね。

 ただ,この評価はスペシャリティコーヒー(世界のコーヒー生産のうち10%程度に該当する)の概念が生まれた10数年前のものであり,以前は苦味,そもそもそんな評価なかったかも。。。今後も同じ評価をしていくとは限りません。やはり常に情報は更新されていきます。だから,常にアンテナを張っていないと時代遅れになっちゃいますね。全てが動いているのに自分が動かなかったら動いている相手から見たら不安定です。だから動くこと,不安定であることこそが安定ってね。

今回の目標

Capistranoで実施していることの言語化

大まかな流れ

・Capistranoについて
・Capistrano導入
・各ファイルの設定

事前準備

デプロイ環境整備
rails5.2以降ならマスターキーを本番環境に環境変数にして入れといてね
ruby 2.5.1

Capistranoについて

一言で言えば,自動デプロイツールです。

デプロイには
SSH内の情報(アプリケーション)の更新
SSH接続
アセットコンパイル更新
Unicorn再起動
といくつかコードを打ち込んでいく必要があります。ただCapistranoを導入さえできれば,
** bundle exec cap production deploy**
これだけで上の作業が終了します。
個人的にはCircleCiを勉強して導入したかったのですが,間に合わずカリキュラムのこちらを

Capistrano導入

railsにおいてCapistranoにはGemが存在します。そこからチョチョイと生成しましょう。

gemfile.
group :development, :test do
  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-unicorn'
end
terminal.
$ bundle install
$bundle exec cap install
⇨gemfileにあるものを参照してcap(istrano)の関連ファイルをインストール

これで1つのフォルダと2つのファイルが生成されます

◆capfile----capistranoで実行することを決めるファイル
◆deploy-----デプロイする環境ごとに設定されるファイル
 ・staging.rb-----ステージング環境(つまり開発環境,テスト環境)に適用される
 ・pruduction.rb--本番環境に適用される
◆deploy.rb--デプロイする内容を決めるフォルダ
capfileで実行することをきめて,残りがGithubへの接続に必要なsshキーの指定、デプロイ先のサーバのドメイン、AWSサーバへのログインユーザー名、サーバにログインしてからデプロイのために何をするか、といった設定が入るわけです。

各ファイルの設定

capfile

capfile.
require "capistrano/setup" ←準備しますー(これ抜けてたら動かない)
require "capistrano/deploy" ←デプロイ実行するねー
require 'capistrano/rbenv' ←rbenvの状態みるね
require 'capistrano/bundler' ←必要なGem確認するね
require 'capistrano/rails/assets' ←アセットファイルをコンパイルするね
require 'capistrano/rails/migrations' ←DB:migrateするね
require 'capistrano3/unicorn' ←unicorn再起動するね
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } 
⇨ディレクトリ内の.rakeを昇順で抽出

に編集
pruduction.rb

pruduction.rb
 server '12.345.678.90'←用意したElastic I, user: 'ec2-user', roles: %w{app db web} |

「どのサーバーつなぐ?」,「ユーザー名は?」「マイグレートするのは(データベース選択)」
staging.rbは今回必要ないので無視

deploy.rb

deploy.rb
# capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する
lock '<Capistranoのバージョン>'←ローカルで確認

# Capistranoのログの表示に利用する
set :application, '自身のアプリケーション名'

# どのリポジトリからアプリをpullするかを指定する
set :repo_url,  'git@github.com:<Githubのユーザー名>/<レポジトリ名>.git'

# バージョンが変わっても共通で参照するディレクトリを指定
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

set :rbenv_type, :user
set :rbenv_ruby, '<このアプリで使用しているrubyのバージョン>' #ローカルで確認

# どの公開鍵を利用してデプロイするか
set :ssh_options, auth_methods: ['publickey'],
                  keys: ['****.pem 自分のに書き換える']

# プロセス番号を記載したユニコーンファイルの場所はここだよ
set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" }

# Unicornの設定ファイルの場所(ローカルの位置と同じ)
set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" }
set :keep_releases, 5

# デプロイ処理が終わった後、Unicornを再起動するための記述
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end

に編集

◆set :xx, xx ⇨DSL(Domain-Specific Language)の一種
ある特定の処理における効率をあげるために特化した形の文法を擬似的に用意したプログラムです。
上記のset :名前, 値について、これは言わば変数のようなものです。
例えばset: Name, 'value' と定義した場合、fetch Name とすることで 'Value'が取り出せます。
また、一度setした値はdeploy.rbやproduction.rbなどの全域で取り出すことができます。
また、ファイル内には、desc '◯◯'やtask:XX doといった記述がよく見受けられます。これは、先ほどCapfileでrequireしたものに加えて追加のタスクを記述している形です。ここで記述したものもcap deploy時に実行されることとなります。

Capistranoによる自動デプロイ後のディレクトリ構成について
 一度Capistranoによる自動デプロイを実行すると、本番環境のアプリケーションのディレクトリが変化します。
Capistranoによるアプリのバックアップなど、複数のディレクトリが作成されます。その中でも特に重要なのが、releases、current、sharedディレクトリです。

releasesディレクトリ
 capistranoを通じてデプロイされたアプリは、releasesというフォルダにひとまとめにされます。
ここに過去分のアプリが残っていることにより、デプロイ時に何か問題が発生しても一つ前のバージョンに戻ったりすることができます。
そして、その過去分の保存数を指定しているのがdeploy.rbのset :keep_releasesの記述となります。今回は5つ、過去のバージョンを保存するよう設定しました。

currentディレクトリ
 releasesフォルダの中で一番新しいものが、自動的にcurrentというフォルダ内にコピーされているような状態になります。そのため、このcurrent内に入っているアプリの内容が、現在デプロイされている内容ということになります。
db:seedもここに入れないと反映されない。
sharedディレクトリ
 バージョンが変わっても共通で参照されるディレクトリが格納されるディレクトリです。具体的には、log、public、tmp、vendorディレクトリが格納されます。

config/unicorn.rb
app_path = File.expand_path('../../', __FILE__)

worker_processes 1

working_directory app_path
pid "#{app_path}/tmp/pids/unicorn.pid"
listen "#{app_path}/tmp/sockets/unicorn.sock"
stderr_path "#{app_path}/log/unicorn.stderr.log"
stdout_path "#{app_path}/log/unicorn.stdout.log"

 ↓↓↓↓↓↓↓ 以下のように変更 ↓↓↓↓↓↓

# ../が一つ増えている
app_path = File.expand_path('../../../', __FILE__)

worker_processes 1
# currentを指定
working_directory "#{app_path}/current"

# それぞれ、sharedの中を参照するよう変更
listen "#{app_path}/shared/tmp/sockets/unicorn.sock"
pid "#{app_path}/shared/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"

nginxの設定ファイル
同様に、Nginxの設定ファイルも変更が必要です。

これまでは/var/www/以下のアプリケーションに対して連携を設定していたので、/var/www/chat-space以下のcurrent、sharedなどのディレクトリと連携するように設定を変更する必要があります。全て消して編集

$ sudo vim /etc/nginx/conf.d/rails.conf
upstream app_server {
  # sharedの中を参照するよう変更
  server unix:/var/www/<アプリケーション名>/shared/tmp/sockets/unicorn.sock;
}

server {
  listen 80;
  server_name <Elastic IPを記入>;

# クライアントからアップロードされてくるファイルの容量の上限を2ギガに設定。デフォルトは1メガなので大きめにしておく
  client_max_body_size 2g;

  # currentの中を参照するよう変更
  root /var/www/<アプリケーション名>/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
    # currentの中を参照するよう変更
    root   /var/www/<アプリケーション名>/current/public;
  }

  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
}

各サーバー再起動

$ sudo service nginx reload
$ sudo service nginx restart
$ sudo service mysqld restart
$ ps aux | grep unicorn
$ kill [master number]

$ bundle exec cap production deploy

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundlerバージョン違いのエラーを解消する

Factory Botをインストールしようとしたらエラー。

version

  • ruby 2.4.9
  • Rails 5.1.1
  • factory_bot_rails ~> 4.10.0
bundle install

The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Bundler could not find compatible versions for gem "bundler":
  In Gemfile:
    rails (~> 5.1.1) was resolved to 5.1.1, which depends on
      bundler (< 2.0, >= 1.3.0)

  Current Bundler version:
    bundler (2.1.4)
This Gemfile requires a different version of Bundler.
Perhaps you need to update Bundler by running `gem install bundler`?

Could not find gem 'bundler (< 2.0, >= 1.3.0)', which is required by gem 'rails (~> 5.1.1)', in any of the sources.


bundler -v
Bundler version 2.1.4

bundler (< 2.0, >= 1.3.0)が必要なのに2.1.4とバージョンが違うからエラーになるらしい。

# bundlerをインストールできるバージョンを調べる
gem search ^bundler$ --all

*** REMOTE GEMS ***

bundler (2.1.4, 2.1.3, 2.1.2, 2.1.1, 2.1.0, 2.0.2, 2.0.1, 2.0.0, 1.17.3, 1.17.2, 1.17.1, 1.17.0, 1.16.6, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16.0, 1.15.4, 1.15.3, 1.15.2, 1.15.1, 1.15.0, 1.14.6, 1.14.5, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14.0, 1.13.7, 1.13.6, 1.13.5, 1.13.4, 1.13.3, 1.13.2, 1.13.1, 1.13.0, 1.12.6, 1.12.5, 1.12.4, 1.12.3, 1.12.2, 1.12.1, 1.12.0, 1.11.2, 1.11.1, 1.11.0, 1.10.6, 1.10.5, 1.10.4, 1.10.3, 1.10.2, 1.10.1, 1.10.0, 1.9.10, 1.9.9, 1.9.8, 1.9.7, 1.9.6, 1.9.5, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9.0, 1.8.9, 1.8.8, 1.8.7, 1.8.6, 1.8.5, 1.8.4, 1.8.3, 1.8.2, 1.8.1, 1.8.0, 1.7.15, 1.7.14, 1.7.13, 1.7.12, 1.7.11, 1.7.10, 1.7.9, 1.7.8, 1.7.7, 1.7.6, 1.7.5, 1.7.4, 1.7.3, 1.7.2, 1.7.1, 1.7.0, 1.6.9, 1.6.8, 1.6.7, 1.6.6, 1.6.5, 1.6.4, 1.6.3, 1.6.2, 1.6.1, 1.6.0, 1.5.3, 1.5.2, 1.5.1, 1.5.0, 1.3.6, 1.3.5, 1.3.4, 1.3.3, 1.3.2, 1.3.1, 1.3.0, 1.2.5, 1.2.4, 1.2.3, 1.2.2, 1.2.1, 1.2.0, 1.1.5, 1.1.4, 1.1.3, 1.1.2, 1.1.1, 1.1.0, 1.0.22, 1.0.21, 1.0.20, 1.0.18, 1.0.17, 1.0.15, 1.0.14, 1.0.13, 1.0.12, 1.0.11, 1.0.10, 1.0.9, 1.0.7, 1.0.5, 1.0.3, 1.0.2, 1.0.0, 0.9.26, 0.9.25, 0.9.24, 0.9.23, 0.9.22, 0.9.21, 0.9.20, 0.9.19, 0.9.18, 0.9.17, 0.9.16, 0.9.15, 0.9.14, 0.9.13, 0.9.12, 0.9.11, 0.9.10, 0.9.9, 0.9.8, 0.9.7, 0.9.6, 0.9.5, 0.9.4, 0.9.3, 0.9.2, 0.9.1, 0.9.0, 0.8.1, 0.8.0, 0.7.2, 0.7.1, 0.7.0, 0.6.0, 0.5.0, 0.4.1, 0.4.0, 0.3.1, 0.3.0)

# バージョン指定でインストール
gem install bundler -v 1.9.9
bundler -v

Bundler version 2.1.4

バージョンが変わらない?

こうやるとバージョンを指定して、インストールできるらしい。
エラーなくFactory Botをinstallできた。

bundle _1.9.9_ install

参考サイト
Ruby | bundler を特定のバージョンに切り替えて実行する - Qiita

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で render の後に書かれた処理は実行されるけどレスポンスを返すのは全処理が終わった後

背景

環境

➜ ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
➜ rails -v
Rails 6.0.2.2

調べた結果

  • 普通にタイムアウトした :sob:
  • render head などのレスポンス系のメソッドの後に書かれた処理も実行される
  • ただ、アクションの中で書かれたすべての処理が実行された後に response を返すっぽい
  • 処理の順番が逆になるわけではないので最後に render するのが自然
class HelloController < ApplicationController
  def hello
    render status: 200

    sleep 3.0

    slack_client = Slack::Web::Client.new(
      token: ENV['SLACK_TOKEN']
    )
    res = slack_client.chat_postMessage(
      channel: params[:channel_id],
      text: 'hello world'
    )
  end
end
  • after_action 系を使っても同じ挙動
class HelloController < ApplicationController
  after_action :slack_post

  def hello
    render status: 200
  end

  def slack_post
    sleep 3.0

    slack_client = Slack::Web::Client.new(
      token: ENV['SLACK_TOKEN']
    )
    res = slack_client.chat_postMessage(
      channel: params[:channel_id],
      text: 'hello world'
    )
  end
end
  • ただ before_action で render すると処理がそこで終わる
class HelloController < ApplicationController
  before_action :before_render

  def before_render
    render status: 200
  end

  def hello
    # ここは実行されない
    sleep 3.0

    slack_client = Slack::Web::Client.new(
      token: ENV['SLACK_TOKEN']
    )
    res = slack_client.chat_postMessage(
      channel: params[:channel_id],
      text: 'hello world'
    )
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数の Enumerable なオブジェクトで tally したい【結論】deep_each

前置き

これまで、複数の Enumerable なオブジェクトで tally するためにあれこれ考え、

複数の Enumerable なオブジェクトで tally したい【追記あり】

を書き、@obelisk68 さんと色々やり取りをして、こんな示唆もいただきました。

Enumeratorを返すArray#flatten(Ruby)

現時点でのまとめ

それで、あれこれ考えてみたところ、結局、deep_each があればいいんじゃないか。

世の中にはいろんな実装例がありますが、簡単なのは、

module Enumerable
  def deep_each(&block)
    return to_enum(__method__) unless block_given?
    each do |e|
      case e
      when Enumerable
        e.deep_each(&block)
      else
        block.call(e)
      end
    end
  end
end

これがあれば、Array 以外のオブジェクトにも対応可能です。

enums = Enumerator.new(20) do |y|
  20.times do
    y << Enumerator.new(20) do |yy|
      20.times do
        yy << Enumerator.new(20) do |yyy|
          20.times do
            yyy << rand(1..5)
          end
        end
      end
    end
  end
end

enums.deep_each.tally
=> {4=>1579, 5=>1664, 2=>1564, 3=>1586, 1=>1607} 毎回答えは変わります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】has_secure_passwordでパスワードのセキュリティを強化

目的

パスワードのセキュリティを強化する。

手段

has_secure_passwordを使ってパスワードをハッシュ化する。

・ハッシュ化は、訳のわからない暗号みたいな文字列に変換すること。

・has_secure_passwordでハッシュ化されたパスワードは、同じ文字列で生成すれば常に同じ値になるので一致しているかどうかをチェックする際に使える。

・has_secure_passwordでハッシュ化したパスワードは、元のパスワードそのものに戻すことができないので、ハッシュ化されたパスワードを奪われたとしても何の問題もないのでパスワードのセキュリティが強固になる。

手順

1.Userモデル作成時のパスワード属性をpassword_digestとする。

 ➡︎has_secure_passwordを使う際の命名規則なので必須。

2.Gemfileにbcryptというgemを追加して、インストールする。

 ➡︎bcryptというgemをインストールすることで、has_secure_passwordを使えるようになりパスワードをハッシュ化できるようになる。
 ➡︎$ bundle installコマンドを忘れずに。

3.Userモデルにhas_secure_passwordを記述。

 ➡︎Userモデル内にhas_secure_passwordを記述したことにより、password属性とpassword_confirmation属性の2つの属性が追加される。データベースのカラムとは対応していない属性。

 ➡︎どういうことかというと、この2つはユーザーに入力を求めるためだけの属性で、この2つの属性をユーザーに入力させることで、Railsはこの2つの一致をチェックした後、一致していたらそのパスワードをハッシュ化してデータベースのpassword_digest属性に格納されるようになっている。

以上の記述を行うことで、ユーザーの新規登録時に入力したパスワードはハッシュ化されるようになる。

ターミナル
  $ rails g model user ... ... password_digest:string
db/migrate/[timestamps]_create_users.rb
  class CreateUsers < ActiveRecord::Migration[5.2]
    def change
      create_table :users do |t|
        .
        .
        .
        t.string :password_digest
        .
        .
        .
      end
    end
  end
ターミナル 
  $ rails db:migrate
Gemfile
  gem 'bcrypt', '~> 3.1.7'
ターミナル
  $ bundle install
app/models/user.rb
  class User < ApplicationRecord
    has_secure_password
    .
    .
    .
  end



試しにコンソールで確認してみる。

  $ rails c --sandbox
    > user = User.new(..., password: '123456', password_confirmation: '123456')
    > user.save
    => true
 
    >user.password_digest
    => 'fflafbnalwefhhhawkleufhb'

ユーザーにpasswordとpassword_confirmationに任意の値を入力させれば、2つの属性が一致しているかどうかを検証した後に、ちゃんとハッシュ化されたパスワードがpassword_digest属性に保存されている事がわかる

参考

 現場で使える Ruby on Rails 5速習実践ガイド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ruby】paizaのための覚えておくと便利なメソッド1(ruby編)

1.読者対象

緊急事態宣言で外出禁止令が敷かれ切ない日々を送っている皆様が読者対象です。
三密を徹底的に排除するべく、飲食店関係や大好きなカラオケまでもが休業状態。
密かに楽しみにしていたフェスも、多分今年はいけないだろう。

私事で恐縮だが、暫く封印していたポケモンにも、ジュラシックワールドにも、新しいキャラが増えすぎて何だかついていけない。
そんなこともあって休日を持て余していた。

そんな時に目に入ったのが

『paiza スキルチェック』

だった。

プログラマの実力を測れるという存在だ。
いきなり高レベルの問題に取り組んでも、くじけてしまうかもしれない。
中々イケイケの言語が並んでいる。
業務で使わない言語でDランクから試してみる。
足し算のプログラムができただけでも、100点とか出るので気分は上がる

活きたプログラミング言語の勉強にいいかも!!

『緊急事態宣言でつまらない日々のお供にpaizaを』

有り余る暇な時間を問題によっては『6時間近く格闘』なんてこともざらだ。

このスキルチェックを進めるには、知っておく必要のある関数がいくつかある。
paizaスキルチェックでは、入力変数を受け取って、その内容を元に処理を記述していく
流れになる。
以下に役立ちそうなメソッドをピックアップした。(初級程度ならいけそうかも)

2.役立つメソッド一覧

(ⅰ)chompメソッド

getsで入力内容を受け取る時に、文字列の最後に改行が入っていたりする。
この改行を取り除いてくれるのがこのメソッド。

paiza_memo1.rb
dino_name1=gets
puts dino_name1+"3"
#改行を削除
puts dino_name1.chomp+"5"

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
Tyrannosaurus
Tyrannosaurus
3
Tyrannosaurus5

chompを使わないと、改行も出力されている事がお分かりだろう。
ここで最後の改行を削除することが第一歩となる。

(ⅱ)countメソッド

これが素晴らしいメソッド。
ある文字列に含まれる特定の文字列の個数を数えることができるメソッド。
中々貴重。

paiza_memo1.rb
#略

dino_name2='Tyrannosaurus'

#指定文字列の中に、nが含まれる個数
puts dino_name2.count('n')
#指定文字列の中に、oが含まれる個数
puts dino_name2.count('o')
#指定文字列の中に、xが含まれる個数
puts dino_name2.count('x')

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
・・・
2
1
0

c:\ruby_pg>

(ⅲ)sliceメソッド

文字列を部分的に抽出するメソッド。
..と...で、処理が違うので注意
slice(開始位置..終了位置)
slice(開始位置...終了位置)
⇒開始位置を含む終了位置-開始位置の長さの文字列が抽出
slice(位置):指定した位置の文字列を抽出。

paiza_memo1.rb
#略
dino_name3='IndominusRex'

puts dino_name3.slice(3)
puts dino_name3.slice(0..dino_name3.length-3)
puts dino_name3.slice(0...dino_name3.length-3)
puts dino_name3.slice(2..5)
puts dino_name3.slice(2...5)

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
o
IndominusR
Indominus
domi
dom

c:\ruby_pg>

(ⅳ)to_iメソッド

間違った例
paiza_memo1.rb
#略
numstr=gets.chomp!
puts numstr+5

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
33
paiza_memo1.rb:36:in +': no implicit conversion of Fixnum into String (TypeError)
from paiza_memo1.rb:36:in
'

c:\ruby_pg>

自動的に型を認識してくれないので、加算ができないのである

修正後

paiza_memo1.rb
numstr=gets.chomp!
puts numstr.to_i+5

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
33
38

c:\ruby_pg>

(ⅴ)gsubメソッド

gsub(変換前文字列,返還後文字列)という文字列置換メソッド。
これも出番が多い関数。

実行例

paiza_memo1.rb
#略
dino_name5=gets.chomp!
tmpstr=dino_name5
puts tmpstr.gsub(dino_name5,"IndominusRex")

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
cat
IndominusRex

c:\ruby_pg>

猫がインドミナスレックスに変身してしまった。

(ⅵ)splitメソッド

split(区切り文字)で区切り文字を指定すると、文字列を配列に変換できるメソッド。
これはもう超鉄板。

実行例

paiza_memo1.rb
dinostr="Tyrannosaurus Velociraptor Stegosaurus"

ar_dino=dinostr.split(' ')

puts ar_dino[0]
puts ar_dino[1]

実行結果

c:\ruby_pg>ruby paiza_memo1.rb
Tyrannosaurus
Velociraptor

c:\ruby_pg>

最後に

paizaにトライするのに最低限必要になりそうな関数をまとめてみました。
『ソースコードに恐竜を』をテーマに終えることが出来てよかった。。。
それでは皆様良き自粛ライフを!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML Rubyに絵文字を追加する

HTMLとRubyに?の様な絵文字を入れる方法をご紹介いたします。

絵文字の表示にはunicordを使います。

絵文字の検索はこちらで行いました。
https://lets-emoji.com/emojilist/

HTML

HTMLはで囲っている部分をそのまま貼り付けるだけです!
d2d340cbb605e7c2faeea96f0781a7f8.png

&#x1f600;

Ruby

Rubyには画像の下線部分を使います。
48f06f0d5d61a375bfe444cf5d27d630.png

下線部分を\u{}で囲むことで表示できる様になります。

\u{1F600}

最後に

Line botを作った際にRubyへの絵文字挿入を行いました。
是非ご活用ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

chmod 600 ←600ってなんやねん

近況報告

とりあえずデプロイは完了しました。readmeを書いて,投稿物を揃え,エラー対応すれば見世物としての体裁はできあがるのかな。某雑食系さんのツイートに,ポートフォリオはサーバーサイドの実力はもちろんのこと,コンテンツ力,そして見た目も評価の対象になりうるとありましたね(めっちゃ噛み砕いた)。自分のはサーバーサイドの記述,実力はまだまだですけど,見た目は手を抜かなければよくできるので,今の自分の最善を尽くした作品を作っていこうと思います。

今回の目的

chmodに続く数字を知る。

結論

ファイルへの権限の種類だよ

詳細

参考

chmodは「change mode」の略で,桁数,数字の組み合わせによって編集の権限とかを設定しています。
これにより,第三者からの不正アクセスの予防に繋がります。

桁数 意味
1桁目(百の位) 所有者のアクセス権限の範囲
2桁目(十の位) グループのアクセス権限の範囲
3桁目(一の位) その他のユーザーのアクセス権限の範囲
数字 意味
4 読み出し許可
2 書き込み許可
1 実行許可
0 権限なし

数字の足し算で権限は増えていきます。例えば全ての権限なら
4+2+1+0で7に
読み出しと書き込みだけなら4+2で6になります。
このように編集できるユーザーを適宜設定できます。

おわりに

Linuxのコマンドは他にもごっそりあるのでそれの一部と考えていただければ。

微量でも参考になったらLGTM,ご指導はコメント欄にお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜany?はpresent?より高速と言われるのか

はじめに

「present?メソッドとany?メソッドだったらany?メソッドが高速なのか・・・なぜだろう?」
この疑問を解決すべく、present?メソッドとany?メソッドの処理の違いについて調べてみました。

環境

macOS Catalina Version 10.15.4
Ruby: 2.7.0
Ruby on Rails: 6.0.2.2

present?メソッド、any?メソッドの違い

どちらもテーブルにデータが存在するかを確認するメソッドです。
けれども内部処理はどちらも異なっています。

侍エンジニアブログさんの、any?メソッド記事の一部を抜粋します。

present?メソッドとany?メソッドとの違いについて紹介します。
結論からいうとany?メソッドの方が高速です。
present? → 全てのデータを取得する
any? → 1件のみデータを取得する
Railsが実行する
SQLを比較して違いを確認してみましょう。

> Sample.where(name:"侍1").present?
Sample Load (0.4ms)  SELECT `samples`.* FROM `samples` WHERE `samples`.`name` = '侍1'
=> true
> Sample.where(name:"侍1").any?
Sample Exists (0.4ms)  SELECT  1 AS one FROM `samples` WHERE `samples`.`name` = '侍1' LIMIT 1
=> true

このように、SQLの最後にLIMIT 1 が付与されています。

any?メソッドがpresent?よりも優れているように見えます。
ではなぜこのような処理になっているのか、Ruby on Railsのコードを追ってみました。

なお、modelは侍エンジニアブログさんと同様のSampleモデル(stringのname属性のみ)を作成しています。

present?メソッドの内部処理

present?メソッド実行時のコードの場所はblank.rbとなっています。
このコードを追ってみます。

>Sample.where(name: "侍1").method(:present?).source_location
=> ["/Users/xxxxx/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/activesupport-6.0.2.2/lib/active_support/core_ext/object/blank.rb", 25]
blank.rb
def present?
  !blank?
end
relation.rb
alias :loaded? :loaded

def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
  @klass  = klass
  @table  = table
  @values = values
  @offsets = {}
  @loaded = false
  @predicate_builder = predicate_builder
  @delegate_to_klass = false
end

def records # :nodoc:
  load
  @records
end

def load(&block)
  unless loaded?
    @records = exec_queries(&block)
    @loaded = true
  end

  self
end

def blank?
  records.blank?
end

よってpresent?メソッドを呼び出した場合、クエリ未発行ならば、exec_queryメソッドでクエリを発行します。
このクエリは、指定されたテーブルの全レコード検索となります。
その後、結果に応じてblank?を行います。
もしクエリ発行済であれば、クエリを発行せずにblank?を行います。

any?の内部処理

any?メソッド実行時のコードの場所はrelation.rbとなっています。
このコードを追ってみます。

>Sample.where(name: "侍1").method(:any?).source_location
=> ["/Users/xxxxx/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/activerecord-6.0.2.2/lib/active_record/relation.rb", 277]
relation.rb
def empty?
  return @records.empty? if loaded?
  !exists?
end

def any?
  return super if block_given?
  !empty?
end

def load(&block)
  unless loaded?
    @records = exec_queries(&block)
    @loaded = true
  end

  self
end
finder_method.rb
def exists?(conditions = :none)
  if Base === conditions
    raise ArgumentError, <<-MSG.squish
      You are passing an instance of ActiveRecord::Base to `exists?`.
      Please pass the id of the object by calling `.id`.
    MSG
  end

  return false if !conditions || limit_value == 0

  if eager_loading?
    relation = apply_join_dependency(eager_loading: false)
    return relation.exists?(conditions)
  end

  relation = construct_relation_for_exists(conditions)

  skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
end

よってany?メソッドを呼び出した場合、毎回exists?メソッドにて、テーブルにレコードが1件あるかどうかクエリを発行します。
その後、結果を確認しています。
もしload変数がtrueになった場合は、クエリ発行せずに結果を確認するのみとなります。

まとめ

最初に記載した疑問の答えです。

any?メソッドがpresent?よりも優れているように見えます。
ではなぜこのような処理になっているのか、Ruby on Railsのコードを追ってみました。

present?メソッドの場合、初回のみ全レコードを取得するクエリを発行して判定します。
2回目以降の呼び出しはクエリを発行せずに判定します。

any?メソッドの場合、基本的に毎回、テーブルから1件レコードを取得するクエリを発行して判定します。

このため、全レコードを取り出さないany?メソッドが高速と言われているだと思われます。

ご指摘等あればコメントにご記載をお願い致します。

参考記事

【Rails入門】any?メソッドの便利な使い方を紹介
RailsのActiveRecord::FinderMethodsのSQLクエリ発行の有無について調べる
ActiveRecord::QueryMethodsのselectメソッドについて深掘りしてみた
週刊Railsウォッチ(20191216前編)Rails 6.0.2がリリース、平成Ruby会議01開催、古いRailsのfindメソッド置き換えほか
RailsのArelを調査してみた

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Cloud9でRuby on Railsを動かしてみた

こんにちは!モリタケンタロウです!
今回はAWSのCloud9で、Ruby on Railsを動かす方法について紹介します。

AWS Cloud9とは

AWS Cloud9については↓をご参考にどうぞ

Ruby on Railsとは

Ruby on RailsはRubyで動作するMVCモデルのフレームワークです。
Webアプリケーション開発に向いていて、世の中のあらゆるサービスがこのRuby on Railsでできています。
Railsはオワコンと言われていたりもしますが、僕が会社でRailsのWebアプリ開発をやっていたときに、勉強しやすくて書きやすかったので、とりあえず今回はRailsを使ってみようと思います。

とにかくやってみよう!

AWS Cloud9では最初からRubyもRailsもインストールされています。
ruby -vrails -vのコマンドで確認できます。
rubyver.png
確認できたら早速アプリを作成してみましょう。
rails new [アプリ名]コマンドで作ります。
newapp.png
そうすると、指定した名前のアプリのディレクトリが作成されました!
作ったアプリのディレクトリの中に入って、rails serverコマンドでアプリを動かしてみましょう。
startapp.png
動いている様子は、Preview > Preview Running Application で確認できます。
previewapp.png
上手くいくと、こんな画面が出てきます。めでたしめでたし!
yayrails.png
上手くいかないと、こんな画面が出てくるかもしれません。
noconnection.png
そんなときは、↓を参考にしてみてください。

ちょっとつまづきながらも無事に「Yay!…」という画面が出せたので、これからコツコツRails開発を始めていきます!
それでは~

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで新しいアプリを作ったらActiveRecord::ConnectionNotEstablishedが出てきた

こんにちは!モリタケンタロウです!
今回はRailsでアプリを新規作成して動かしてみたら、「ActiveRecord::ConnectionNotEstablished」というエラーが出てきたので、それを解決する方法について紹介します。

開発環境

  • ruby 2.6.3p62
  • Rails 5.0.0

エラー内容

rails serverコマンドでアプリを動かすと、画面にはこんなエラーが出てきます。
noconnection.png
ActiveRecord::ConnectionNotEstablished
No connection pool with id primary found
なにこれ!?分からん…
ということでググると、RailsのORM機能であるActiveRecordが、sqlite3の新しいバージョンに対応してないことが原因らしい。(参考:ActiveRecord::ConnectionNotEstablished No connection pool

対処内容

ということで、Railsがインストールしているパッケージを管理しているGemfileを編集します。

gem 'sqlite3', '~> 1.3.6'
  • 修正前 oldgem.png
  • 修正後 newgem.png

Gemfileを編集したら、bundle installコマンドでインストールパッケージを更新します。
bundleinstall.png
そしてrails serverコマンドでアプリを起動すると…
railsserver.png
yayrails.png
見事、アプリが正常に起動しました。めでたしめでたし!
ということで一件落着(^^)
それでは~

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】モデルのオブジェクト生成に対する制限・制御を行ってくれる機能

はじめに

備忘録

目的

データベースに不正なデータが保存されないようにすること。


手段

1.データベースカラムに対して制御を記述する。
2.モデルに対して検証を記述する。

両者の違い

データベースカラムに対する制御
 ➡︎一意性の検証

 ➡︎ユーザーへエラーメッセージを届けられない

 ➡︎モデルでは弾けないような、直接データベースに他のシステムから操作を加える作業に対しての制御

モデルに対する検証
 ➡︎データベースでは制御しきれない部分の補完

 ➡︎ユーザーへエラーメッセージを届ける事ができる

※今回は、制御に関する機能の説明のみです。

1.データベースカラムへの制御でよく行われる方法4つ

※データベースカラムへの制御はマイグレーションファイルに記述していくこと。

1.データ型を付与する。

 ➡︎データ型を付与する事で、そのデータ型以外のデータが格納されることを阻止する。
  →例えば、「名前カラムには文字列しか格納させたくない」場合には、名前カラムに文字列のデータ型を付与すると良い。

2.NOT NULL制約を付与する。

 ➡︎カラムに対して、必ず何かしらの値が格納されるようにしたい場合に空の値が格納されることを阻止する。
  →例えば、ユーザーが新規登録する際に「名前が空のまま保存されることを阻止したい」場合には、この制約を追加すると良い。

3.文字列の長さを制御する。

 ➡︎カラムに対して、文字列の長さを指定することで指定した文字列以上の長さのデータが格納されることを阻止する。
  →例えば、ユーザーが新規登録する際に「名前が20文字以上になることを阻止したい」場合には、文字列の長さを制御してあげると良い。

4.ユニークインデックスを付与する。

 ➡︎カラムに対して、ユニークインデックスを付与する事でそのデータの一意性が確保され、同じデータが格納されることを阻止する。
  →例えば、ユーザーが新規登録する際に「メールアドレスが他のユーザーと被ることを阻止したい」場合には、メールアドレスカラムに対してユニークインデックスを付与してあげると良い。

  コマンドを実行してモデルのクラスファイル(マイグレーションファイル)を作成
  $ rails g model User 
db/migrate/[timestamps]_create_users.rb
  class CreateUsers < ActiveRecord::Migration[5.2]
    def change
      create_table :users do |t|
        t.string :name, limit: 20, null: false
        t.string :email, null: false
 
        t.timestamps
        t.index :email, unique: true
      end
    end
  end
  マイグレーションファイルに変更を加えたらデータベースに変更を保存する
  $ rails db:migrate

t.string :name
 ➡︎データ型の付与

limit: 20
 ➡︎文字列の長さを制御

null: false
 ➡︎NOT NULL制約(SQLのnullとRubyのnilを混同させないこと)

t.index :email, unique: true
 ➡︎ユニークインデックス付与で一意性確保


2.モデルに対する検証方法は主に2つ

※モデルに対する検証はモデルファイルに記述していくこと。
※モデルに対する検証の仕組みは「検証結果が不正だった場合は、エラーメッセージを生成する」であることに注意。なので、例えばユーザーに新規登録をさせる場合は「検証で失敗した結果、何が原因で登録できなかったのかをユーザーに知らせる」事が目的。

1.Railsであらかじめ用意されている、検証用のヘルパーメソッドを使う。

 ➡︎モデルに対して、validatesオプションを使って検証したいヘルパーメソッドを適用させることで、メソッドの内容に準じて検証を行う事ができる。
  →例えば、ユーザーが新規登録する際に「名前未入力による登録する事を阻止したい」場合には、validatesオプションに対してそれ用のヘルパーメソッドを適用させる。
  →その他にもたくさんの検証ヘルパーが用意されている。

2.検証用のメソッドを自分で定義しちゃう。

 ➡︎モデルに対して、「どんな検証を行いたいか」「どんなエラーメッセージを表示させたいか」を考えてメソッドを定義していく。
  →例えば、ユーザーが新規登録する際「名前にハイフンを入れる事を阻止したい」ときには「名前にハイフンを含ませた場合、"名前にハイフンを入れることはできません"というエラーメッセージを生成する」といったメソッドを定義すれば良い。

 ➡︎メソッドを定義したら、validate(単数形であることに注意)オプションに適用させる。

app/models/user.rb
  class User < ApplicationRecors
    validates :name, presence: true
    validate :validate_name_not_include_hyphen
 
    def validate_name_not_include_hyphen
      errors.add(:name, 'にハイフンを入れる事はできません') if name&.include?('-')
    end
  end

validates :name, presence: true
 ➡︎Userモデルのname属性の値が入力されていることをチェックする。

validate_name_not_include_hyphen
 ➡︎メソッド名。

errors.add(:name, 'にハイフンを入れることはできません') if name&.include?('-')
 ➡︎名前にハイフンが含まれていた場合、「(名前)にハイフンを入れることはできません」というエラーメッセージを生成する。

 ➡︎nameに「&」を付けているのは、名前がnilだった場合に例外を発生させないため。nilだった場合はnilを代入させることができる。そうすることでvalidatesオプションのpresence: trueで検証させる。

※エラーメッセージを表示させるまでがゴール

エラーメッセージを生成・表示させるには(ユーザー新規登録を例にする)

1:新規登録ならユーザーコントローラのcreateアクション内で、送信されてきたデータからオブジェクトを@user.saveメソッドで保存・更新する作業を行ってあげる。
 ➡︎ビューで再利用するためにインスタンス変数でオブジェクトを定義してあげること。

2:不正なデータが送信された場合は、saveメソッドの効果により検証が実行され保存されずにfalseを返し、errorsという配列に保存に失敗したオブジェクトが格納される。
 ➡︎saveメソッドやupdate_attributesメソッドなど保存・更新時に検証を行ってくれるメソッドを適用させないと、検証を実行してくれないので注意。
 ➡︎例えば、ユーザーが名前を空のまま保存したとしたら@user.errorsの配列には「@messages ={:name=>["を入力してください"]}」という旨のエラーメッセージが格納される。

3:@user.errors.full_messagesと記述することで、errors配列に格納されているエラーメッセージを取得できる。
 ➡︎名前を入力してください

4:取得した上記のエラーメッセージをビューで表示することで、ユーザーにエラーメッセージを届ける事ができるようになる。

以上の手順を踏む必要がある。

参考

 現場で使える Ruby on Rails 5速習実践ガイド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Paizaのスキルチェック(Ruby)を解きたいけど標準入力でつまづいた

プログラミングの勉強を始めたあたりの頃、
条件分岐(if,elsif,else...)とか繰り返し処理(each...)とか色々なメソッドを学び、

「さぁこれでワイもプログラマーや!、ちょっと腕試しするか」

「スキルチェックぽち〜」

「うわ、めちゃ簡単や、ここで条件分岐して、このメソッドで一発やろ!、ワイに任せろ!」

「...」

「標準入力ってなんや...、解き方はわかるけど、値が受け取れんわ...」

って感じでpaizaのスキルチェックを始めた時に標準入力で困ったので、同じような人のために、paizaの標準入力の受け取り方を書いていきます。

標準入力の受け取り方

例えば下図のような標準入力があったとします。

標準入力
5

getsメソッドで受け取れます。

 input = gets

Paizaの標準入力は文字列で与えられます、今回は数字ですのでこのままだと計算したり大小比較ができないので、文字列から数値に変換しましょう。to_iメソッドを使うと可能です。

 input = gets.to_i

getsは複数回使うと次の標準入力を受け取ってしまうので、受け取った値を何回も使いたいときは変数に代入しておきましょう。今回はinputという名前の変数にしていますが、a = gets , number = getsとかでも大丈夫です。

1行の標準入力で複数の値を受け取る

1行に複数の値があった場合です。

Paizaだと複数の値に半角スペースを挟んだ文字列が渡されます。

標準入力
5 10 20

後ろに.chomp!.split(" ")をつけます

.chomp!が改行を消すメソッドです、これをつけないと末尾に改行の記号がくっついてしまいます。

.split(" ")が文字列を分解して配列に変換するメソッドです。引数に" "
を渡すことで半角スペースを区切りにして配列になります。

 input = gets.chomp!.split(" ")

出力してみると

p input
 => ["5", "10", "20"]

文字列でなく数値として受け取りたい場合はさらにmapメソッドを使いましょう。配列の値を一個ずつ取り出して数値に変換していきます。

 input = gets.chomp!.split(" ").map{|n| n.to_i}

出力してみると

p input
 => [5, 10, 20]

複数行の標準入力を受け取る

複数行に値があった場合です。paizaの場合大抵一つ目の標準入力にこの後の標準入力の行数を与えてくれるので、それを利用します。

今回で言うと3ですね。

標準入力
3
5
10
20

まず最初の標準入力を受け取りましょう。

N = gets.to_i

続いてこの値を用いて複数行の標準入力を受け取っていきます。

今回は5, 10, 20をそれぞれ配列に入れていく形で受け取っていきましょう。

timesメソッドを使います。

numbers = []

N.times do
  numbers << gets.to_i
end

numbersと言う空の配列を用意して、N回標準入力を受け取ってnumbersに入れていくと言う流れです。

出力すると

p numbers
=> [5, 10, 20]

これで標準入力が受け取れるので、受け取った値を使って問題を解いていけますね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】paizaのスキルチェックを解きたいけど標準入力でつまづいた

プログラミングの勉強を始めたあたりの頃、
条件分岐(if, elsif, else...)とか繰り返し処理(each...)とか色々なメソッドを学び、

「さぁこれでワイもプログラマーや!、ちょっと腕試しするか」

「スキルチェックぽち〜」

「うわ、めちゃ簡単や、ここで条件分岐して、このメソッドで一発やろ!、ワイに任せろ!」

「...」

「標準入力ってなんや...、解き方はわかるけど、値が受け取れんわ...」

って感じでpaizaのスキルチェックを始めた時に標準入力で困ったので、同じような人のために、paizaの標準入力の受け取り方を書いていきます。

標準入力の受け取り方

例えば下図のような標準入力があったとします。

標準入力
5

getsメソッドで受け取れます。

 input = gets

paizaの標準入力は文字列で与えられます、今回は数字ですのでこのままだと計算したり大小比較ができないので、文字列から数値に変換しましょう。to_iメソッドを使うと可能です。

 input = gets.to_i

getsは複数回使うと次の標準入力を受け取ってしまうので、受け取った値を何回も使いたいときは変数に代入しておきましょう。今回はinputという名前の変数にしていますが、a = gets , number = getsとかでも大丈夫です。

1行の標準入力で複数の値を受け取る

1行に複数の値があった場合です。

paizaだと複数の値に半角スペースを挟んだ文字列が渡されます。

標準入力
5 10 20

後ろに.chomp!.split(" ")をつけます

.chomp!が改行を消すメソッドです、これをつけないと末尾に改行の記号がくっついてしまいます。

.split(" ")が文字列を分解して配列に変換するメソッドです。引数に" "
を渡すことで半角スペースを区切りにして配列になります。

 input = gets.chomp!.split(" ")

出力してみると

p input
 => ["5", "10", "20"]

文字列でなく数値として受け取りたい場合はさらにmapメソッドを使いましょう。配列の値を一個ずつ取り出して数値に変換していきます。

 input = gets.chomp!.split(" ").map{|n| n.to_i}

出力してみると

p input
 => [5, 10, 20]

複数行の標準入力を受け取る

複数行に値があった場合です。paizaの場合大抵一つ目の標準入力にこの後の標準入力の行数を与えてくれるので、それを利用します。

今回で言うと3ですね。

標準入力
3
5
10
20

まず最初の標準入力を受け取りましょう。

N = gets.to_i

続いてこの値を用いて複数行の標準入力を受け取っていきます。

今回は5, 10, 20をそれぞれ配列に入れていく形で受け取っていきましょう。

timesメソッドを使います。

numbers = []

N.times do
  numbers << gets.to_i
end

numbersと言う空の配列を用意して、N回標準入力を受け取ってnumbersに入れていくと言う流れです。

出力すると

p numbers
=> [5, 10, 20]

これで標準入力が受け取れるので、受け取った値を使って問題を解いていけますね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSを用いてrailsアプリをデプロイするプロセスを頑張って噛み砕いてみるvol.6

近況報告

大海に糸を垂らして魚を釣るのと鱒がそこにいるとわかっている生簀で鱒を釣るの,どちらが先に釣れるでしょうか。価値があるから売れるのと,売れるから価値がある,どちらが始まりなのでしょうか。鶏と卵論争ではないけれども,考えていくべきことですよね。

ついに最後,Nginxですね。
まだやりたいことはあって,予定ではCapistranoとベーシック認証があるのでvol.8以上あるのか笑
さあいってみようかぁ!

今回の内容

・インフラ整備での各アクションの言語化
・コードを単に打っているだけでは理解しきれないし,他に応用できないので言語化して整理
・テックキャンプ受講生支援()

大まかな流れ&設定

Nginxインストール

事前準備

vol.1
vol.2
vol.3
vol.4
vol.5

Nginxインストール&設定

EC2.
sudo yum -y install nginx

sudo yum の解説は前の項で触れています。
インストールできたら設定を編集していきます。むやみやたらにいじられたくないファイルなので権限も強いです。sudo権限でvim形式で編集していきます。

EC2.
$ sudo vim /etc/nginx/conf.d/rails.conf
rails.conf
upstream app_server {
  server unix:/var/www/<アプリケーション名>/tmp/sockets/unicorn.sock;
}
  ⇨Unicornと連携させるための設定。アプリケーション名を自身のアプリ名に書き換えることに注意。upstreamは上流の意。ここでunixが登場していることはよくわかんなかった。

server {
⇨ {}で囲った部分をブロックと呼ぶ。サーバの設定ができる
  listen 80;
⇨このプログラムが接続を受け付けるポート番号
  server_name <Elastic IP>;
⇨接続を受け付けるリクエストURL
  client_max_body_size 2g;
⇨クライアントからアップロードされてくるファイルの容量の上限を2ギガに設定。デフォルトは1メガ
  root /var/www/<アプリケーション名>/public;
⇨接続が来た際のrootディレクトリ
  location ^~ /assets/ { ←assetファイルはここに入っているのを参照するよ
    gzip_static on; ←圧縮状態のものを配信。容量が小さくなるのでメモリの負担減らせる
    expires max; ←キャッシュの有効期限。maxは限界までの意
    add_header Cache-Control public; ←キャッシュを受け取る範囲の選択
⇨assetsファイル(CSSやJavaScriptのファイルなど)にアクセスが来た際に適用される設定
  }

  try_files $uri/index.html $uri @unicorn;
⇨ファイルの選択,右の$からアクセスされる

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
⇨Nginxからユニコーンにむけた設定
  }

  error_page 500 502 503 504 /500.html;
⇨エラーが表示された時のページ
}

と<アプリケーション名>計三箇所直したら,脱出(:wq)

補足
プロキシ
ブラウザとウェブサーバーの中間役。ウェブサーバーの情報をブラウザで写すが,プロキシはウェブサーバーの代理としてウェブサーバーからの情報を写す。 ⇨わかりやすかった説明
location unicornではHTMLメソッドの受け取る方法とか表示の仕方とか設定してるっぽい。

権限変更を行ったあと,再起動をします。

EC2.
$ cd /var/lib
$ sudo chmod -R 775 nginx ←-RはNginxに対してって意味 
$ cd ~
$ sudo service nginx restart
⇨初めにfailed出ても再起動でOKがでれば大丈夫

カリキュラムで分からなかった点
POSTメソッドを用いるとエラーが発生する可能性があるから権限変更を行うらしいのですが,このコマンドが通信量の多いPOSTメソッドのエラーを防ぐのかまだ理解できていません。

●補足chmodについて

UnicornをNginxに接続

config/unicorn.rb
listen 3000

↓以下のように修正

listen "#{app_path}/tmp/sockets/unicorn.sock"

githubに反映後,EC2に読み込み。
unicorn再起動処理をして,ブラウザにElastic IPを直打ちしてアプリケーションのルートページが表示できれば成功。うまく表示できない場合はもう一回やり方を見直す。URLが見つからないことで表示されてない可能性があるので注意して見る。

終わりに

テックキャンプのカリキュラム,はしょりすぎぃ!!!
まあ多分目的は完全理解というより大枠を捉えよ的な教科書みたいなアプローチですな。
10週間でやれることなんてたかがしれてるし,Nginxひとつとっても理解しようとすれば数百ページの本を読まないとだから無難なのかな(しかもオープンソースだから今この瞬間にアップデートされているかもしれない...)。

微量でも参考になったらLGTM,ご指導はコメント欄にお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Could not find a JavaScript runtime となる時の解決方法

【事象】
bundle exec rake db:create すると下記のエラーが出る。

rake aborted!
ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime.
See https://github.com/rails/execjs for a list of available runtimes.

【解決策】
Gemfileに下記を追加し、bundle installすると解決した。
image.png
ExecJSとは、RubyからJavaScriptコードを実行できるもの。
エラーの原因は、RubyからJavaScriptを呼び出す役割を持つ
ExecJSのランタイムが見つからないためのようなのでランタイムを追加してあげれば良いと推測。

ExecJSのことを https://github.com/rails/execjs
で見たが、gem therubyracerをインストールするとエラーになったので
また調べてgem mini_racerをインストールすると解決した。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails tutorial】 Herokuへのデプロイ&二回目以降のデプロイ方法

現在レイルズチュートリアルを進めているのでその経過をアウトプットしていきます。

今回はタイトル通りHerokuのデプロイ方法と二回目のデプロイ方法についてです。

チュートリアル通りに進めていましたが、git push heroku の部分でつまずきました。

結論から言うとconfigのdatabase.ymlの記述に問題がありました。

下の方にあるproduction: の部分を以下のように書き直します。

production:
<<: *default
adapter: postgresql
encoding: unicode
pool: 5

簡単に言うとpostgreSQLを使うという宣言をする記述のようですね。

もしチュートリアル通りにやってうまくいかないという方はここを治すとデプロイできるかもしれないです。

そして二回目以降のデプロイ方法ですが演習の部分で元々のhello worldの表示をgoodbyeアクションの結果を表示できるようにしろという問題がありますが、指示通りにそのままgoodbyeアクションを記述してターミナルに git push heroku と入力するとupdate heroku Everything と出てうまくいったような感じになりますが heroku を開くと変わらずhello worldとなっています。

これは私の場合ですが変更した結果をgit?にコミットしていなかったのが原因だったようです。

なので下のように記述しました。

git commit -a -m "Update Gemfile for Heroku"

そして再度プッシュしたところ変更が反映されていました!

今回は以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[rails] migrate:reset でのエラー

はじめに

railsでアプリを作成していた際、db:migrateをした際に、エラーが発生しめmigrate:resetをしたのですがその際初めてのエラーが発生したので、その備忘録として書きたいと思います。

最初のエラー

新しくテーブルを作成するためにマイグレーションファイルを編集して実行したところファイル内に記述していたテーブル名に記述ミスがあり、エラーが発生しました。

class CreateHoges < ActiveRecord::Migration[5.2]
  def change
    create_table :hoges do |t|
      t.references :huge
      t.text :text
      t.timestamps
    end
    add_foreign_key :hoges, :missname, column: :huge_id  # missnameが記述ミス
  end
end

そのため、記述を直し改めてマイグレーションファイルを実行したところ以下のエラーが発生しました。

rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::SQLException: table "hoges" already exists: CREATE TABLE "hoges"

どうやらテーブルは既に存在しているといって、エラーが起きているようです。
まさかと思いテーブルを確認しても出来ていません。
rails db:migrate:status で確認してもステータスは downになっていました。

原因

どうやらmigrationの際に、処理が途中まで進んで、残りでエラーが起きているときは、テーブルは生成されているけれどもテーブルを生成したmigrationは未実行のステータスになっていることがあるようです。
(create_tableのあと、add_foreign_keyしたところにエラーがあって、というパターン)

migrate:reset

ならば、一度データベースを全て削除した上で、再度実行したら直りそうです。
以下のコマンドを実行

rails db:migrate:reset

そしたら、またもやエラー

rake aborted!
ActiveRecord::NoEnvironmentInSchemaError:

Environment data not found in the schema. To resolve this issue, run:

        bin/rails db:environment:set RAILS_ENV=development

なんだこれは、と思い調べたところ、どうやら指示されるようにコマンドを入力したら直りそうです。
以下の記事が、とても参考になります。
ActiveRecord::NoEnvironmentInSchemaErrorについて

解決

まず指示されるまま以下のコマンドを実行

 rails db:environment:set

そして、再度以下のコマンドを。

rails db:migrate:reset

ようやく正常にテーブルを作成することができました。

おわり

今回はmigrate:resetで解決しましたが、もし他のテーブルに影響を与えない方法があるならば、教えていただければ幸いです。
最後まで読んでいただき、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】wheneverを用いたバッチ処理の実装

目標

登録ユーザー全員に、Googleメールを1日毎に送信する。

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

・メール送信機能を実装済み。

メール送信機能 ➡︎ https://qiita.com/matsubishi5/items/c88e8c58b1ce39302868

メール送信機能を編集

1.「login_mailer.rb」を編集

daily_mailer.rb
class DailyMailer < ApplicationMailer
    def send_mail(user)
        @user = user
        mail to: @user.email, subject: "定期配信"
    end

    def self.send_when_everyday
        @users = User.all
        @users.each do |user|
            DailyMailer.send_mail(user).deliver
        end
    end
end

2.「send_when_login.html.erb」を編集

daily_mailer/send_mail.html.erb
<%= @user.name %><p>定期配信メールです。</p>

3.「application_controller.rb」を編集

application_controller..rb
#コメントアウト又は削除
def after_sign_in_path_for(resource)
    LoginMailer.send_when_login(current_user).deliver
    root_path
end

バッチ処理を実装

1.Gemを追加

Gemfile
gem 'whenever', require: false

require: false ➡︎ アプリケーションには反映させずに、ターミナルにのみ反映させる

ターミナル
$ bundle

2.「schedule.rb」を作成

ターミナル
$ bundle exec wheneverize .

3.「schedule.rb」を編集

config/schedule.rb
env :PATH, ENV['PATH'] #絶対パスから相対パス指定
set :output, 'log/cron.log' #ログの出力先ファイルを設定
set :environment, :development #環境を設定

every 1.day do #1日毎に実行
#「DailyMailer」の、「send_when_everyday」メソッドを実行
    runner "DailyMailer.self.send_when_everyday" 
end

4.「cron」を反映

ターミナル
$ bundle exec whenever --update-crontab

バッチ処理でよく使うコマンド

$ bundle exec whenever ➡︎ cronの設定を確認

$ bundle exec whenever --update-crontab ➡︎ cronを反映

$ bundle exec whenever --clear-crontab ➡︎ cronを削除

参考サイト

https://freecamp.life/rails-whenever/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの『テスト自動化』(Minitest)について、簡単にまとめてみた。

はじめに

これは、プロを目指す人のためのRuby入門(チェリー本)を読んで理解を自分なりにまとめて書き記した物です。
個人的にわからない箇所を書き記したものとなっていますので、全て書いているわけではありません。
しかし、みんながつまずく箇所は似通っているのでは?と思うのでその点に関しては、要点を絞った内容が書けているのではと思います(たぶん)。

今回書くのは、その中でも『第3章:テストを自動化する』(Minitestの基本)についてです。

それでは、内容の方へ入りましょう!

テストを自動化する

ここで学ぶ事は、
・Minitestの基本
です。

そして、テスト自動化とは名前の通り『勝手にテストを行ってくれて、ミスがないか確認してくれるものの事』です。
ミスがないかの確認を毎回毎回行うことは効率が悪いためです。

●Minitestの基本

まずは、テスト用のフレームワークを用意します。
Rubyにおいてはそのフレームワークの一つとしてMinitestがあります。

Minitestを使うメリットは、
・Rubyと一緒にインストールされるため、セットアップが不要
・学習コストの低さ
・Railsのデフォルトのテスティングフレームワークのため、Railsを開発するときにも知識を生かしやすい。

今回のテストを行うにおいての簡単な手順としては、
1. Rubyプログラム(テストコード)を書く
2. テストコードの実行
3. テスティングフレームワークが結果をチェックして、結果報告

では、実際にテストコードを書いていってみましょう。

test_ruby.rb
require 'minitest/autorun' # Minitestのライブラリの読み込み

class RubyTest < Minitest::Test
  def test_ruby #メソッド名はtest_で始めるのが必須
    assert_equal 'RUBY', 'ruby'.upcase #この部分が検証される部分。
    #assert_equalはMinitestのメソッド。意味は後で説明
  end
end
assert_equal 期待する結果, テスト対象となる値や式

この本で紹介されているのは、以下の3つのメソッド
(他にもメソッドは存在します。知りたい方は、MinitestのAPI公式ドキュメントへ)

#①aとbが等しければテストはパス
assert_equal b, a

#②aが真であればパス
assert a

#③aが偽であればパス
assert a

そして、先ほどのtest_ruby.rbのテストを実行した結果が

$ ruby ruby_test.rb
Run options: --seed 35176

# Running:

. #←テストの進歩状況を表しています。(今回はテストメソッドが1個だけのためドットが一つだけ)

Finished in 0.000979s, 1021.4507 runs/s, 1021.4507 assertions/s. # テストの実行スピード
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

failuresとerrorsがどちらも0ならテストをパスしたことになります。
今回はテストにパスした例になりますが、次は失敗例をみてみましょう。

●テストの実行結果、失敗例

test_ruby.rb
require 'minitest/autorun' 

class RubyTest < Minitest::Test
  def test_ruby
    assert_equal 'Ruby', 'ruby'.upcase # assert_equal 'Ruby'に変更
  end
end
$ ruby ruby_test.rb
Run options: --seed 18225

# Running:

F

Failure:
RubyTest#test_ruby [ruby_test.rb:5]: ←ruby_test.rb:5からruby_test.rbの5行目でテストが失敗していることが確認できる。
Expected: "Ruby" ←期待された結果
  Actual: "RUBY" ←実際の結果


rails test ruby_test.rb:4



Finished in 0.001320s, 757.5753 runs/s, 757.5753 assertions/s.
1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

実行結果の説明をします。
今度はドットではなく、『F』に変わりましたが、これはFailureのFです。
その後は、どこで失敗したかが記載されています。

●実行結果がエラーの場合

$ ruby ruby_test.rb
Run options: --seed 35712

# Running:

E 

Finished in 0.001649s, 606.4282 runs/s, 0.0000 assertions/s.

  1) Error:
RubyTest#test_ruby:
NoMethodError: undefined method `upcase' for nil:NilClass
Did you mean?  case
    ruby_test.rb:5:in `test_ruby'

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

今度は、『F』だったところが、Errorの『E』へと変わりました。

1) Error:以降はエラー内容の詳細になります。

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
から、1件のエラーで終わったことが確認できました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsでサーバー起動に苦労した話

起こった現象

Ruby on Railsでrails new hogeとしてファイルを作ったが、run git init from "."のところで止まってしまった。
当然、rails sとしてもサーバーは起動せず、webpacker:installしろというようなエラーがでた。

現象としては下記のブログと同じ。
https://yoji4910.hatenablog.com/entry/2020/02/06/184457
ただ、このブログ記事にある通り-Gをしても治らなかった。

対処法

色々調べたら下記Qiita記事を見つけた。
https://qiita.com/libertyu/items/1eb74adc817ab8971100
この記事の通り、rails webpacker:installをしたら、下記のようなエラーが出た。

'node' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。 'nodejs' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。

というわけで下記からNode.jsをダウンロード、インストールした。
https://nodejs.org/ja/download/

こんどこそと思い、rails webpacker:installをするとYarnがないとエラーがでた。
これは上記のQiita記事と同じエラーなので、下記からYarnをダウンロード、インストールした。
https://classic.yarnpkg.com/ja/docs/install/#windows-stable

そしてもう一度rails webpacker:installをするとやっと成功した。

rails sをしてサーバー起動できた!
http://localhost:3000/
にアクセスすると、ちゃんと動いてることが確認できました。
123.png

感想

めんどくさかった。
どのRuby on Railsの記事を見ても、ほとんどがrails new hogeでファイルが作成できることしか書いてなかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

windows10(WSL2)でRailsの環境構築をしてみた~Rails導入/起動まで~(2)

はじめに

前回記事にてWSL2の導入ができましたが、今回はWSL2を用いて実際にUbuntu 18.04に環境を構築していきます。

Ruby(Rails)導入

@ksh-fthr さんの以下記事を参考にしました。
Windows10 で WSL を使って Rails 環境を構築したときのメモ
見出しRails 環境を構築しよう1~8を実施してください。

今回しようとしているのは上記の記事と若干違うので、以下の点だけ異なっています。
Ruby 2.6.6を導入
・sqlite3ではなくmysqlを導入するので7.sqliteを入れようは実施不要

WebPackerの導入

rails6からWebpackerが必須となり、Webpackerを入れないと、Railsアプリ作成の際にエラーが出てしまいます。
Ubuntu 18.04 LST にて以下コマンドを実行してください。

まず、Webpacekrをインストールするためのyarnをインストールします。
単純にapt-get install yarnだけだとバージョンが古くてWebpackerをインストールできなかったので、以下コマンドでバージョンの新しいyarnを入れます。

Ubuntu18.04
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install yarn

本命のWebpackerを導入します。

Ubuntu18.04
rails webpacker:install

mysqlの導入

今回はsqliteの代わりにmysqlを導入します。
Ubuntu 18.04 LST にて以下コマンドを実行してください。

mysqlの導入

Ubuntu18.04
sudo apt-get install mysql-server mysql-client

mysqlのバージョン確認

Ubuntu18.04
実行コマンド
mysql --version
実行結果
mysql  Ver 14.14 Distrib 5.7.29, for Linux (x86_64) using  EditLine wrapper

バージョンが表示されれば導入完了です。

mysqlにはデフォルトでrootユーザが用意されていますが、これをrails上で使用するとすると、権限の問題で弾かれてしまいます。
root以外のユーザを作成し、きちんと権限も与えてあげましょう。
以下、コマンド例になります。

ユーザ作成例(rootで実行)

CREATE USER railsuser@'localuser' IDENTIFIED BY '******(パスワード)'

権限付与例(rootで実行)

GRANT ALL ON *.* TO railsuser@'localuser';

Atom導入

今回はエディターとしてAtomを利用します。
Ubuntu 18.04 LST にて以下コマンドを実行してください。

Ubuntu18.04
sudo apt-get install mysql-server mysql-client

以下コマンドを実行すると、Atom がGUIで立ち上がってきます。

Ubuntu18.04
atom

Rails サーバ起動

Ubuntu 18.04 LST上でRailsサーバを起動していきます。

1. アプリ作成

新しいアプリ作成します。

Ubuntu18.04
rails new sample -d mysql
cd sample

2. mysql設定ファイル編集

/config/database.ymlにmysqlのログイン情報を記載します。

database.yml
# MySQL. Versions 5.5.8 and up are supported. 
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: railsuser
password: ******(パスワード)
socket: /var/run/mysqld/mysqld.sock

usernamepasswordをmysql導入時に設定した内容にしてください。

また、テスト・開発・本番用のDBはあらかじめmysqlにて作成しておいてください。

3. サーバ起動

サーバを起動します。

Ubuntu18.04
rails s

ブラウザにてlocalhost:3000に接続して以下画面が表示されたら成功です。
2020-04-26 (6).png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL2でRailsの環境構築をしてみた~WSL2導入まで~(1)

はじめに

web開発に関してはmacユーザが多いと思うが、windowsでも WSL( Windows Subsystem for Linux) なるものでLinuxでのRailsの開発環境をある程度整えることができたので、備忘として残そうと思います。

環境
Windows 10 Home 64bit

WSLを導入する

WSLの導入には@yoshigeさんの以下記事が非常に参考になりました。

まずは、以下の記事を参考にWSL1を導入し、初期設定まで済ませます。
Microsoft StoreでLinuxのディストリビューションを選んで導入することになるが、自分はUbuntu 18.04 LTS を導入しました。
初心者のためのWSL( 1 ) ~初期設定,CUI設定編~

GUIも使えた方が便利なので、以下記事を参考にGUI設定まで済ませます。
初心者のためのWSL( 2 ) ~GUI設定,デスクトップ環境設定編~

WSL2を導入する

WSL2の導入は少し複雑です。

1. Windows10 Insider Preview に登録 ⇒ ビルド更新

WSL2はビルドが18917以上でないと導入できません。

以下URLの手順に沿ってWindows10 Insider Preview に登録しビルドを更新してください。
https://insider.windows.com/ja-jp/getting-started/#register

2. ビルドを確認する

設定システムバージョン情報でOSビルド欄をみて、18917以上であることを確認してください。

3. 仮想マシン有効化

以下コマンドを管理者権限のPowerShellで実行し、再起動してください。
powershell
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

4. WSL2へ切り替え

以下、コマンドを管理者権限のPowerShellで実行してください。
ビルドが18917以上でないと、以下コマンドは実行できません。

現在のWSLバージョンを確認する

 実行コマンド
 wsl -l -v
 出力結果
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         1

WSLバージョンを2へ切り替える(結構時間がかかるかもしれません)

wsl --set-version Ubuntu 2

切り替え後のバージョンを確認する

 実行コマンド
 wsl -l -v
 出力結果
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         2

WSLのバージョンが「2」になっていることを確認出来たらWSL2の導入は完了です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

windows10(WSL2)でRailsの環境構築をしてみた~WSL2導入まで~(1)

はじめに

web開発に関してはmacユーザが多いと思いますが、windowsでも WSL( Windows Subsystem for Linux) なるものでLinuxでのRails開発環境をある程度整えることができました。
備忘として残そうと思います。

環境
Windows 10 Home 64bit

また、今回導入したものは以下となります。

導入したもの
Ruby 2.6.6
Rails 6.0.2.2
mysql for linux 14.14
Atom 1.45.0

WSLを導入する

WSLの導入には@yoshigeさんの以下記事が非常に参考になりました。

まずは、以下の記事を参考にWSL1を導入し、初期設定まで済ませます。
Microsoft StoreでLinuxのディストリビューションを選んで導入することになるが、自分はUbuntu 18.04 LTS を導入しました。
初心者のためのWSL( 1 ) ~初期設定,CUI設定編~

GUIも使えた方が便利なので、以下記事を参考にGUI設定まで済ませます。
初心者のためのWSL( 2 ) ~GUI設定,デスクトップ環境設定編~

WSL2を導入する

WSL2の導入は少し複雑です。

1. Windows10 Insider Preview に登録 ⇒ ビルド更新

WSL2はビルドが18917以上でないと導入できません。

以下URLの手順に沿ってWindows10 Insider Preview に登録しビルドを更新してください。
https://insider.windows.com/ja-jp/getting-started/#register

2. ビルドを確認する

設定システムバージョン情報でOSビルド欄をみて、18917以上であることを確認してください。

3. 仮想マシン有効化

以下コマンドを管理者権限のPowerShellで実行し、再起動してください。

Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

4. WSL2へ切り替え

以下、コマンドを管理者権限のPowerShellで実行してください。
ビルドが18917以上でないと、以下コマンドは実行できません。

現在のWSLバージョンを確認する

 実行コマンド
 wsl -l -v
 出力結果
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         1

WSLバージョンを2へ切り替える(結構時間がかかるかもしれません)

wsl --set-version Ubuntu 2

切り替え後のバージョンを確認する

 実行コマンド
 wsl -l -v
 出力結果
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         2

WSLのバージョンが「2」になっていることを確認出来たらWSL2の導入は完了です。

WSL2へ切り替えた後の設定

1. .bash_profileを編集

WSL2に切り替えた後だと、ターミナル立ち上げの際に.bashrcを読み込んでくれない仕様になっているようです。

参考
WindowsTerminalやPowerShellからWSL2を実行した際に~/.bashrcが読み込まれない

自分は.bash_profileに以下を追加することで回避しました。

if [[ -f ~/.bashrc ]] ; then
  ~/.bashrc
fi   

3. GUI設定の変更

WSL2はWSLと違い、仮想マシン上でLinuxを動作させています。

参考
https://www.atmarkit.co.jp/ait/articles/1906/14/news019.html

なのでWSLと違い、window環境 ⇔ Linux環境のIPアドレスが別々に扱われます。

以下、windowsのコマンドラインでipconfigを実行した例になります。

Wireless LAN adapter Wi-Fi:

   IPv4 アドレス . . . . . . . . . . . .: xxx.xxx.xxx.xxx
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: xxx.xxx.xxx.1

イーサネット アダプター vEthernet (WSL):

   IPv4 アドレス . . . . . . . . . . . .: yyy.yyy.yyy.yyy
   サブネット マスク . . . . . . . . . .: 255.255.240.0
   デフォルト ゲートウェイ . . . . . . .: yyy.yyy.yyy.1

新しくイーサネット アダプター vEthernet (WSL)が追加されていることがわかります。
IPが起動するたびに変更されるので、Xlaunchの待受け先もそれに伴い修正する必要があります。

GUI設定にて.bashrcにexport DISPLAY=localhost:0と記載していた部分を以下のように修正します。

export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0 

これで、IPアドレスが変更されても、設定しなおすことなくGUIが立ち上がります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Service health dashboardのRSSを、Slackに一括登録する

背景

  • Personal Health dashboardがありつつ、やはりService health dashboardの情報は、それはそれとして購読したい。
  • SlackにRSSリーダーがあるけど、1 URLずつ /feed subscribe https://.....rss と実行する必要があり拷問である。複数をベタっと貼ってもエラーになる。どうにか一括登録したい。
  • https://status.aws.amazon.com/rss/all.rss という全部入りがあるけど、使ってないリージョンのまで入ってるので、購読すると無駄にうるさい。また直近15記事しかなく、広域障害発生時は猛烈に流れて使い物にならないことが想定できる。製品ごとリージョンごとRSSで見るしかなさそう
  • ググると手製の東京リージョンOPMLとか見つかるけど、数年前のものだったりする。常に最新のRSSであってほしい

解法

  • https://status.aws.amazon.com/ からRSSのURLを、グローバルサービス、リージョナルサービスにバラして、ほしいリージョンだけにする。CLIで。
  • Slack API から /feed subscribe コマンドを実行して登録する

RSSのURLぜんぶ取ってくる

curlしたものをモゴモゴして取ってきます。

curl -s https://status.aws.amazon.com/ \
| perl -wpl -e 's|\"|\n|g' | grep "/rss/" \
| perl -wpl -e 's|^|https://status.aws.amazon.com|g' \
| sort -u \
>  all.txt

グローバルとリージョナルに分割する

URLリストを眺めると、リージョナルサービスにはリージョン名がURLに含まれており、グローバルサービスはサービス名だけであることがわかります。すっごく雑に、

  • ハイフンが複数個なければグローバルサービス
  • ハイフンが複数個あるならリージョナルサービス

とバラせそうでした。

cat all.txt \
| grep -Ev '.*-.*-.*-' > global-products.txt

cat all.txt \
| grep -E '.*-.*-.*-' > regional-products.txt

最初はハイフンの有無で分けてみたのですが、以下の例外があることがわかり、「ハイフンが複数個の有無」で、としました。

https://status.aws.amazon.com/rss/import-export.rss
https://status.aws.amazon.com/rss/management-console.rss
https://status.aws.amazon.com/rss/s3-us-standard.rss

リージョンごとに分割

せいぜい2 - 3リージョンしか使ってないでしょうから、欲しいリージョンのものだけに絞りたい、なので分割します。

rm -f regional-products.*.txt
cat regional-products.txt \
| rev | awk -F'[.-]' '{print $0 " " $2 "-" $3 "-" $4}' | rev \
| awk '{print "echo " $2 " >> regional-products." $1 ".txt"}' \
| bash

SlackのAPIトークンを発行する

ここあまり詳しくなく申し訳ないのですが https://api.slack.com/legacy/custom-integrations/legacy-tokens で生成したトークンで動いたのでヨシとしています。

手元にrubyをインストールする

2.6系で最新を選んでおけばいいのではないでしょうか。この後で使う slack-ruby-client も入れておいてください。

brew install rbenv ruby-build
rbenv install 2.6.6
rbenv global 2.6.6
gem install slack-ruby-client

Slackに一括登録するRubyスクリプトを用意する

https://gist.github.com/sasasin/28fbb22d3e40dd8518be5f2f30df5e42 をテキトーな場所に転がしてください。

以下のように動かすことができます。

export SLACK_API_TOKEN="xoxp-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
./slack_slash_command.rb "#general" "/feed" "subscribe https://status.aws.amazon.com/rss/iam.rss"

Slackに一括登録する

これまでの情報を総合して、以下のように実行することで一括登録できます。

export SLACK_API_TOKEN="xoxp-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export CHANNEL_NAME="#投稿先チャンネル名"
cat global-products.txt \
| xargs -L1 -I XXX ./slack_slash_command.rb "${CHANNEL_NAME}" "/feed" "subscribe "XXX
cat regional-products.ap-northeast-1.txt \
| xargs -L1 -I XXX ./slack_slash_command.rb "${CHANNEL_NAME}" "/feed" "subscribe "XXX
cat regional-products.us-east-1.txt \
| xargs -L1 -I XXX ./slack_slash_command.rb "${CHANNEL_NAME}" "/feed" "subscribe "XXX

CodeBuildで定期実行する

一回やってそれきりではないはず。利用するAWS製品もリージョンも増減していくでしょうから、それなり簡単に自動登録したいでしょう。CodeBuildで定期実行させましょう。

こんなbuildspecです。

version: 0.2

env:
  parameter-store:
    # 格納先のSSMパラメータストア名は好みで
    SLACK_CHANNEL_NAME: "/CodeBuild/SLACK_CHANNEL_NAME"
    SLACK_API_TOKEN: "/CodeBuild/SLACK_API_TOKEN"

phases:
  install:
    commands:
      - gem install slack-ruby-client
      - curl -s -o ./slack_slash_command.rb https://gist.githubusercontent.com/sasasin/28fbb22d3e40dd8518be5f2f30df5e42/raw/0294724cc31abeb2f02b04ac05fd95d28732d37d/slack_slash_command.rb
      - chmod +x slack_slash_command.rb

  build:
    commands:
      - |
        curl -s https://status.aws.amazon.com/ \
        | perl -wpl -e 's|\"|\n|g' | grep "/rss/" \
        | perl -wpl -e 's|^|https://status.aws.amazon.com|g' \
        | sort -u \
        >  all.txt
      - cat all.txt | grep -Ev '.*-.*-.*-' > global-products.txt
      - cat all.txt | grep -E '.*-.*-.*-' > regional-products.txt
      - |
        rm -f regional-products.*.txt
        cat regional-products.txt \
        | rev | awk -F'[.-]' '{print $0 " " $2 "-" $3 "-" $4}' | rev \
        | awk '{print "echo " $2 " >> regional-products." $1 ".txt"}' \
        | bash
      - ls -alF *.txt
      - |
        cat global-products.txt \
            regional-products.ap-northeast-1.txt \
            regional-products.us-east-1.txt \
        | while read FEED_URL; do
            ./slack_slash_command.rb "${SLACK_CHANNEL_NAME}" "/feed" "subscribe ${FEED_URL}"
            sleep 3
        done

  • コンテナイメージは ruby:2.6-buster で動作確認しています
  • チャンネル名やSlackトークンは、SSMパラメータストアなどに入れましょう。 SLACK_CHANNEL_NAME はパブリックチャンネルの場合は #general のように # を付ける必要があります
  • いますぐ使うCodeBuild を参考に、好みの周期で定期実行させます
  • 例として東京とバージニア北部リージョンで動かしてます
  • 当初 sleep 3 は入れずに動かしたら、途中から「お前やりすぎ3秒後にリトライしろ」でコケるようになったので追加してます

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む