20200623のRubyに関する記事は14件です。

【Rails】 本番環境MySQLデータを開発環境に入れた後に消す方法

はじめに

本番環境にMySQLのデータを開発環境に入れる時には特にエラーもなくインプットすることができたのですが、消す時になってエラーが発生してハマってしまいました。
少し珍しいケースかもしれませんが、他の方の参考になればと思いまとめることにしました。

関連リンク

そもそも本番環境のMySQLのデータをどうやって開発環境に入れるのかという記事は下記をご参照ください。

エラー

開発環境でDB自体を削除するコマンドを実行したところ、、、

$ rails db:drop

エラーActiveRecord::ProtectedEnvironmentErrorが出ました。。。

このエラーの意味は、

本番環境のDBを操作しようとしているが問題ないですか?

という警告のようなもので、環境変数を入れると操作を続けることができるよ。と言ってます。

そもそも開発環境なんですけど、なんで本番環境と出るんですか??

rails aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database.
If you are sure you want to continue, run the same command with the environment variable:
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
bin/rails:4:in `<main>'
Tasks: TOP => db:migrate:reset => db:drop => db:check_protected_environments
(See full trace by running task with --trace)

結論

先に結論から書いてしまいますが、

Rails5では MySQLに環境変数を格納しており、本番環境のデータをそのまま開発環境に入れたことにより、MySQLの環境変数に本番環境の変数が格納されてしまったため、上記のエラーがでてしまいました。

なので、解決する方法としては、MySQLの環境変数を本番環境のものから開発環境のものに書き換えればいいということです。

解決手順

MySQLに接続します。

$ mysql -h db -u root -p

存在するデータベースを確認します。

$ show databases;

対象のデータベースを指定します。

$ use *******;

データベースないのテーブルの一覧を表示します。

$ show tables;

テーブル一覧の中にar_internal_metadataがあると思います。
この中に、環境変数を格納しています。

+---------------------------------------------------+
| Tables_in_scm_development                         |
+---------------------------------------------------+
| ar_internal_metadata                              |
| ...                                               |
+---------------------------------------------------+

ar_internal_metadataの中身をみてみると、

$ select * from ar_internal_metadata;

見つけましたproductionの文字を。

+-------------+-------------+---------------------+---------------------+
| key         | value       | created_at          | updated_at          |
+-------------+-------------+---------------------+---------------------+
| environment | production  | 2020-01-10 09:37:29 | 2020-01-10 09:37:29 |
+-------------+-------------+---------------------+---------------------+

このproductiondevelopmentへ修正します。

$ update ar_internal_metadata set value='development'

もう一度、ar_internal_metadataの中身をみてみると、

$ select * from ar_internal_metadata;

環境変数はdevelopmentに変更できました。

+-------------+-------------+---------------------+---------------------+
| key         | value       | created_at          | updated_at          |
+-------------+-------------+---------------------+---------------------+
| environment | development | 2020-01-10 09:37:29 | 2020-01-10 09:37:29 |
+-------------+-------------+---------------------+---------------------+

MySQLから抜けて

$ exit

もう一度、DBを削除するコマンドを実行。すると成功しました!

$ rails db:drop

まとめ

つらつらと手順を書いたので、人によっては理解しづらいかもしれませんが、困っている誰かの役に立てばなーと思います。

参考

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

RailsでHerokuにデプロイしたときに一部ページだけ500エラーとなるとき

マイグレーションファイルは慎重に作成し、ミスったらやり直すくらいの覚悟じゃないとHerokuにpushしてheroku run rails db:migrateしたときに沼にハマるよという話。

発生している問題

ローカル環境ではキチンと動くのに、(SQLite->Postgresへの対応などの基本的な設定を済ませてから)Herokuにデプロイするも、一部のページだけが500(Internal Server Error)となる。フロントでは「We're sorry, but something went wrong.」なので、404の「The page you were looking for doesn't exist.」とは違って内部的なエラーが発生している。
スクリーンショット 2020-06-23 21.58.17.png

heroku logs --tailでログを見ても以下の500エラーと書かれただけで何が原因か全然分からないかった。

2020-06-23T09:27:34.223359+00:00 app[web.1]: [8ee3a40d-5258-46f4-8742-48c47f28b01f]   Category Load (1.2ms)  SELECT "categories".* FROM "categories"
2020-06-23T09:27:34.474847+00:00 heroku[router]: at=info method=GET path="/categories/1/show" host=my-app.herokuapp.com request_id=8ee3a40d-5258-46f4-8742-48c47f28b01f fwd="27.81.17.54" dyno=web.1 connect=1ms service=263ms status=500 bytes=1827 protocol=https

結論

汚いマイグレーションファイルを残したままpushしてheroku run rails db:migrateしていないか疑え!

以下の記事の【1】と同じだった。
https://qiita.com/kaorioka09jm/items/9167ba77d1edf7addac2

ローカル開発中にDB作りを試行錯誤したことが原因

Herokuにデプロイした全ページが500とかならheroku run rails db:migrateしてないなど他の理由もあるが、一部のページだけ表示されない場合はDBのマイグレーションが上手くいっていない場合が多いようだ。

結局、ローカルのマイグレーションファイルを削除したり、作り直したりしても、一度Herokuにpushしてしまったマイグレーションファイルがあると、データベースをResetしてもDestroyしても、そこからheroku run rails db:migrateしてもデータベースは綺麗にならない。

そのため筆者の場合はHerokuからアプリを削除して再度pushし直す羽目になった。

マイグレーションファイルの注意点

  • マイグレーションファイルは一度しかrails db:migrateできない
  • 生成されるschema.rbを直接編集しちゃダメ(と思われる)
  • テーブルの削除なども都度マイグレーションファイルを作ってやること

そのため以下のようなマイグレーションファイルの生成は慎重に行う必要がある。もし間違ってカンマを入れたり本来integerのデータ型を間違ってtextに指定してしまったなどだけで、変なマイグレーションファイルが生成される。

// 例
rails g migration CreatePostCategoryRelations post_id:integer category_id:integer
// 間にカンマを入れたり、コロンの位置が違ったり、integerの綴りが違ったりと間違えがち

それを挽回するために別のマイグレーションファイルを作って‥なんてやってローカルでDBをぐちゃぐちゃ構築しても、それをpushした後にheroku run rails db:migrateを実行すると過去のマイグレーションファイルを順に実行するのでおかしくなる。

今回はPosticoの使用により、DBの中のあるテーブルのcategory_idカラムがStringになっていたことに気付けたので、Heroku側のDB生成が上手くいってないことに気付けた。

DBがおかしいと気付くには、rails db:migrateした際のログをめんどうでもちゃんと読んで確認するのも役立つと思う。

  • キチンと標準のテーブル+マイグレーションファイルで指定したテーブルが作成されているか?
  • 各テーブルのカラムのデータ型は正しいか?

テーブルの削除

Heroku上に正しいマイグレーションファイルをpushするには、まずローカルのマイグレーションファイルを綺麗にする必要がある。しかし、間違った記述のあるマイグレーションファイルを削除し、正しいマイグレーションファイルを作成してrails db:migrateしても、以下のように怒られて上書きはできない。

rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: table "post_category_relations" already exists

そのためテーブルの削除用のマイグレーションファイルを作成してrails db:migrateし、再度新たなテーブル作成用のマイグレーションファイルを作成してrails db:migrateする必要がある。

$ rails generate migration RemoveMyTable
20200623XXXX_remove_my_table.rb
class RemoveMyTable < ActiveRecord::Migration[6.0]
  def change
    drop_table :my_table
  end
end

https://qiita.com/kanuu/items/a9223712ee0ff8d19d56

その後、テーブルの削除などに使った余分なマイグレーションファイルを削除する。こうして綺麗なマイグレーションファイルだけが残ったので、これをまっさらなHerokuにpushしてからheroku run rails db:migrateをしてやると綺麗なデータベースができる。

考えたこと

SQLiteとPostgreSQLの互換性の問題ではないか

Herokuでは SQLite が使えないので、production環境だけ PostgreSQL を使用するのはHerokuで最初につまづくポイントだが、これの変更によって使えないメソッドがあるのではと考えた。

が、そんなことはなく、そもそもマイグレーションファイルとはSQLを書かずに、かつ言語の種類(SQLiteかPostgreSQLか等)によらずにDBを操作するためのものである。

今回はControllerの以下のincludeswhereがPostgreSQLでは使えないのでは、といった馬鹿な勘違いをしてしまった‥orz.

categories_controller.rb
def show
  @posts = Post.includes(:categories).where(post_category_relations: { category_id: @category })
end

※Gemのpgloaderを使うなどの記事を見つけたが、それはMySQLのデータをPostgreSQLのデータに移行するものであって、メソッドを変換とかではない。

push後のWarningされてる部分に問題があるのではないか

pushした後に以下のWarningが出るが今回は無関係。またこれらは、どれも特別な使い方をしない限りは、初心者は無視していい警告だ。

remote: ###### WARNING:
remote:        You set your `config.assets.compile = true` in production.
remote:        You set your `config.active_storage.service` to :local in production.
remote:        We detected that some binary dependencies required to
remote:        No Procfile detected, using the default web server.

マイグレーションファイルの理解を深めるのに役立ちそうな記事

そもそもマイグレーションとは何なのか?
https://qiita.com/sobameshi0901/items/f5823fec20e2dd78d9c4

ロールバックという手法もあるらしい?
https://qiita.com/params_bird/items/3d503b5c9f8097df147a

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

正規表現で大文字から始まる文字列を取り出す(Ruby)

今回やる事

タイトル通り、正規表現で大文字から始まる文字列を取り出します。

コード

今回書いたコードは以下の通り

# 配列から大文字から始まる文字列を抜き出すメソッド
def upperstr(array)

  # 配列の添字として使用する変数
  count = 0

  # 大文字から始まる文字列を格納する配列
  upper = []

  # eachメソッドで配列に含まれている要素を全て取り出す
  array.each{|arraystr|

    # 正規表現で先頭が大文字から始まりる文字列を抽出し、配列(upper)に格納する
    upper[count] = arraystr.slice(/^[A-Z].*/)

    # 配列の添字に1を加算する
    count += 1
  }

  # 文字列の先頭が小文字だった場合は配列にnilが格納されるため、deleteメソッドでnilを削除する。
  upper.delete(nil)

  # メソッドの返り値として、大文字から始まる文字列を格納した配列を変えす。
  return upper

end


# ユーザー用のガイド
p "文字列を半角スペースで区切って入力してください"

# コンソールから入力した文字列を変数に代入
str = gets

# 入力された文字列を半角スペースで区切って配列に代入
ary = str.split(" ")

# upperstrメソッドの返り値を変数に代入
# (upperstrメソッドは大文字から始まる文字列を取り出すメソッド)
upperary = upperstr(ary)

# ユーザー用のガイド
p "大文字から始まる文字列を取り出しました"

# eachメソッドで配列に含まれている要素を全て取り出す
upperary.each{|ustr|
  # 配列の中身を表示
  p ustr
}

正規表現

※各メソッドや処理についてはコメントに書いたので説明は割愛。
今回はupperstrメソッド内で使用しているsliceメソッドで正規表現を使っております。
噛み砕きまくって説明しますと、以下の通りです。

①コード
upper[count] = arraystr.slice(/^[A-Z].*/)

②/
/ と / で囲まれた範囲が正規表現パターンの範囲となる

③^
^の直後の文字が先頭の文字であることを表しています

④[A-Z]
AからZという意味であり、大文字であることを表しています。
^[A-Z] とすることで先頭が大文字である文字列を検索する事ができます。
小文字にしたい場合は[a-z]です。

⑤.
. は任意の1文字です。

⑥*
 は直前の文字列が0回以上繰り返すことを意味します。
.
 と書くことで、「任意の文字列が0回以上繰り返す」ということになります。
なんかわかりづらいと思いますが、「先頭の文字以外はどうでもいいよ」っていうことです。

動作確認

実際にプログラムを動かした結果を書いておきます。
入力値は「aaa AAA B b CC cc DDDDD ddddd」です。
大文字から始まる文字列を取り出すので、想定される結果は「AAA B CC DDDDD」となります。

% ruby regex.rb 
"文字列を半角スペースで区切って入力してください"
aaa AAA B b CC cc DDDDD ddddd
"大文字から始まる文字列を取り出しました"
"AAA"
"B"
"CC"
"DDDDD"

想定通りの結果が出ました!今回の記事は以上です!

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

Railsチュートリアル(第4版)メモ 第6章

Railsチュートリアル(第4版)の個人メモ
気になった部分、忘れそうな部分を記述。

  • Ruby 2.6.1
  • Rails 5.1.6

前章の最後 (5.4) でUsersコントローラを作成した。そのつづき。
6〜12章にかけて、ユーザ認証の各機能について作成していく。
6章ではこのうち、ユーザのデータモデル、保存について扱われている。

※ おわび
ナレッジになりきれていないメモをQiitaに上げるのが違うような気がしてきたが、
記事として所感を上げれば、それなりに反応があり、勉強にもなったため、お目汚しにはなりますが何卒ご容赦を。
そして最近Rails書いてないのだが下書が埋まってきたので放流。

6章 ユーザーのモデルを作成する

Railsでは認証を実装するための仕組みが既に整っているが、サービスごとに認証には多くのカスタムが必要になる。
このため、車輪の再発明にはなるが、方法を知っていればサードパーティ製の認証も実装しやすくなるので、Railsチュートリアルでは車輪を再発明している。

6.1 Userモデル

Railsは、データモデルとして扱うデフォルトのデータ構造を Model (MVCのM) と呼ぶ。
このModelとのやり取りにはRailsデフォルトでは Active Record というものが使われる。
これはSQLを意識せずにCRUDや構築を行ってくれるもの。

6.1.1 データベースの移行

データベースを永続化するためにUserモデルを作成。
Usersコントローラ作成時と同様、 $ rails g する。

注意として、
コントローラは 複数形 Users
モデルは 単数形 User

とするのが慣習。

$ rails g model User name:string email:string

DBで使う属性(DBでいうカラム名)と、型を : を挟んで渡す。

諸々生成されるが、 db/migrate/ には タイムスタンプ_create_モデル名s.rbというファイルができる。
ここでDB作成のための情報が定義される。

db/migrate/タイムスタンプ_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

idとタイムスタンプについては、特に指定なしでも自動で用意される。
クラス名の部分を見ると、 ActiveRecord::Migration[5.0] を継承しているらしい。
(Railsチュートでは5.0だが、小生の環境は5.1の表記)
timestamps は、タイムスタンプに係る2つのカラム [ created_at, updated_at ] を作成してくれる。

mysqlだとこんなかんじだろうか。

mysql
CREATE DATABASE User;
USE Users;
CREATE TABLE users(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(300),
  email VARCHAR(300),
  created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
);

sql書くたびに大文字or小文字 許さないおじさんが発生しそうだな、とビクビクしながら書いている(前職にもいた)。
正直、今の御時世、すべて小文字でも問題ないんだとおもっている。
ちなみに前職では日付はvarchar()で書いてうわなにをするやめろ

マイグレーションを適用。
友人曰く、複数人開発で特に気を遣わんと爆死するコマンドだから、と念を押された。気をつけよう。
DB側はバージョン管理されてるわけではない。

$ rails db:migrate

6.1.2 modelファイル

演習
Railsコンソールで User.new をし、どのような継承関係に有るのか確認した。

Userクラス < ApplicationRecord < ActiveRecord::Base < Object < BaseObject

6.1.3 ユーザーオブジェクトを作成する

railsコンソールのサンドボックスオプション

を使うと、DBへの変更を終了時にすべてロールバックしてくれる。

$ rails console --sandbox

この中での実験。
モデルを生成した際にuserクラスが生成され( app/model/ )、これが ActiveRecord から継承されている。

以下使ったメソッド
valid? : 有効性(Validity)の確認
save : 保存
保存した際、 create_at , update_at にタイムスタンプが押される。
戻り値に、保存ができたかどうかのbool値が帰ってくる。

create : User.new ではメモリ内に生成するだけだったが、生成と同時に保存する。

演習
User.create
user.name
ActiceSupport::timeWithZone

6.2 ユーザーを検証する

6.2.1 有効性を検証する

要はSQLでのカラム型指定みたいな話
:email に対して、 Double とか Boolean は入らんとか。
以下4点

  • 存在性 (presence)
  • 長さ (length)
  • フォーマット (format)
  • 一意性 (uniqueness)

所感

  • usersテーブルに削除フラグって要るような気がする。後で追加するのか?
  • dbのmigrateファイル、いっつも書くidとかをよしなにやってくれるのもだし、varcharとかで文字数指定しなくて良いのだけでも楽。
  • dbのmigrateファイル、変更が時系列で残ってくれるのがとても安心感ある。前職ではDBに変更掛けたときのSQLすら遺してなかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】CSVエクスポート機能の実装

目標

ezgif.com-video-to-gif.gif

開発環境

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

前提

下記実装済み。

Slim導入
Bootstrap3導入
投稿機能実装

実装

1.application.rbを編集

application.rb
require_relative 'boot'

require 'rails/all'
require 'csv' # 追記

Bundler.require(*Rails.groups)

module Bookers2Debug
  class Application < Rails::Application
    config.load_defaults 5.2
  end
end

2.コントローラーを編集

books_controller.rb
def index
  @book = Book.new
  @books = Book.all
  # 追記
  respond_to do |format|
    format.html
    format.csv do |csv|
      send_users_csv(@books)
    end
  end
end

# 追記
def send_users_csv(books)
  csv_data = CSV.generate do |csv|
    header = %w(ID 登録日 投稿者 タイトル)
    csv << header
    books.each do |book|
      values = [book.id, book.created_at, book.user.name, book.title]
      csv << values
    end
  end
  send_data(csv_data, filename: '本一覧情報')
end

① CSVファイルのヘッダーを設定する。

header = %w(ID 登録日 投稿者 タイトル)
csv << header

② CSVファイルの内容を設定する。

books.each do |book|
  values = [book.id, book.created_at, book.user.name, book.title]
  csv << values
end

③ CSVファイル名を設定する。

filename: '本一覧情報'

3.ビューを編集する。

books/index.html.slim
/ 追記
= link_to 'CSVエクスポート', books_path(format: :csv), class: 'btn btn-success'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby Silverに合格(2020年6月)したので、まとめてみます。

スペック

  • 非ITエンジニア
  • Rubyはプログラミングスクールで学習
  • IT系の資格はITパスポートのみ

 ITエンジニアになるためにプログラミングスクールに通い、二ヶ月半程RubyやRailsを学んでいたので、初学者ですが独学ではありません。

受験対策の教材

 Ruby2.1以下ではエラーになり、2.2以上ではエラーにならないコード出てくるので、手軽にバージョンを切り替えられるWandboxを使うといいかもしれません。

勉強方法

 Rubyをスクールで学んでいたので、ある程度の基礎知識はありました。そのため「Rubyがミニツク/入門コース」と「Ruby技術者認定試験合格教本(以下、教本)」を1回読んで基礎知識を広めてから、練習問題に取り掛かりました。

 練習問題のレベル感や解説は、以下のように感じました。

  • やさしい/丁寧
    • 教本の練習問題
    • Rubyがミニツク
    • Ruby技術者認定試験一問一答
  • ふつう
    • 模擬問題集PDF(Silver試験用)
    • DIVE INTO EXAM
  • むずかしい
    • RubyExamination

 やさしいは、解説が丁寧だったと感じたので、間違えたところの理解がしやすかったです。

 ふつうは、やさしいに比べて解説文が短かったので、教本の読み直しやぐぐったりコードを打つ回数が増えました。

 むずかしいは、5回やって1〜2回目は不合格、3〜5回目は合格でした。Goldの問題も含まれているらしいので難しく感じましたが、理解を深めるためにやって損はないと思います。

勉強の振り返りと反省

 コードの書き方やメソッドの種類等、スクールの学習以外で知識を吸収出来たのがとても良かったので、Ruby初学者の目標としてはSilverを取得するのはアリだと思いました。
 しかし、やるからには100点狙おうと思って勉強したのはよくなかったと思います。実務で使わない内容も多いらしいので、アプリを作る時に知っておくと便利だなと思ったのはきちんと理解し、使わなさそうなのは深掘りする必要はなかったかなと思いました。

Ruby Silverについて

  • Ruby Association Certified Ruby Programmer Silver version 2.1」が正式名称らしいです。
  • 終了後、即採点されてその場で「合格」の文字が出るので、履歴書にすぐに取得資格として書く事が出来る。
  • ここを読むとRuby SilverはITパスポートと同じレベル感なので、資格と考えるなら弱いのかもしれません。
  • Rubyの知見は広がり深まりましたが、Rubyエンジニアの方曰く実務で使う内容は少ないとのことです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

git pushで毎回ユーザー名とパスワードを入力を回避する方法 - SSH接続

Cloud9からgit pushするのに
毎回ユーザー名とパスワードを要求されるをなんとかしたくて調べてみた
(以下記事作成者に感謝)

git パスワード を毎回聞かれる問題の解決方法 - Qiita

一度ローカル環境のターミナルで設定してみて
Cloud 9からpushしてみるも認証がうまく行かず

鍵の登録はデバイスごとにする必要があります

ローカル環境で設定済みでも、
同じ手続きを再度Cloud9上で行うとSSH接続可能になりました

認証鍵の仕組みは把握していましたが
鍵?という方はこちら

GitHubでssh接続する手順~公開鍵・秘密鍵の生成から~ - Qiita

SSHの仕組みについては

プログラマーがSSH周りで知っておくと良いこと - Qiita

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

【ERROR メッセージ表示】rails 部分テンプレートでいつでも使える 簡易版

【ゴール】

errorメッセージの表示

画面収録 2020-06-23 18.49.15.mov.gif

【メリット】

■ UX向上
■ 部分テンプレートの理解度向上

【開発環境】

■ Mac OS catalina
■ Ruby on Rails (5.2.4.2)
■ Virtual Box:6.1
■ Vagrant: 2.2.7

【実装】

アプリケーション作成

※touch はカレントディレクトリ内でfile作成するコマンド

mac.terminal
$ rails new error_test
$ cd error_test
$ rails g scaffold Item name:string texte:text amount:integer
$ rails db:migrate
$ cd app/views/layouts
$ touch _error_messages.html.erb

モデル追記

※DB保存時の条件
バリデーション = 保存時の制限 を追加

models/item.rb
class Item < ApplicationRecord
  validates :name, presence: true
  validates :text, presence: true
  validates :amount, presence: true
end

ビュー追記

※パーシャル化し、凡庸性UP
「layputs/_error_messages.html.erb」に入れる事で
バリデーションを貼っていれば全てのモデルに使用可能に!!!!

layputs/_error_messages.html.erb
<% if model.errors.any? %>
  <div class="alert alert-warning">
    <ul>
      <% model.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

※追加する

Items/_form.html.erb
<%= render 'layouts/error_messages', model: f.object %>

scaffold で作成の場合は上記は不要
各 _form のviewにて下記コード記載の為(scaffold超便利ですね)

/_form.....
<% if item.errors.any? %>
<div id="error_explanation">
  <h2><%= pluralize(item.errors.count, "error") %> prohibited this item from being saved:</h2>

  <ul>
    <% item.errors.full_messages.each do |message| %>
    <li><%= message %></li>
    <% end %>
  </ul>
</div>
<% end %>

※scaffoldの元々のやつ
スクリーンショット 2020-06-23 18.46.33.png

※パーシャルしたやつ
スクリーンショット 2020-06-23 18.47.29.png

以上です。お好みでどうぞ!!!

【合わせて読みたい】

■エラーメッセージに関して
https://qiita.com/ryuuuuuuuuuu/items/1a1e53d062bff774d88a

■hidden_field
https://qiita.com/tanaka-yu3/items/0d454c5ef80f8267f09d

■アプリケーション作成
https://qiita.com/tanaka-yu3/items/3fe1ed2852c6513d3583

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

いろんなフィボナッチ数列の関数作ってみた(Ruby)

単純な再帰で書く

絶対おそい(nが0or1の深さになるまで再帰するため)。

def fib(n)
    return n if n == 0 || n == 1
    return fib(n - 1) + fib(n - 2)
end

ちょっと工夫した再帰

再帰だが、やってることはfor文と大して変わらない。
ただ再帰関数なので、超でかいnが渡されたらスタックオーバーフローするかも?

def fib(n, a, b)
    return a if n == 0
    return fib(n - 1, b, a + b)
end

forで書く

再帰じゃないのでスタックオーバーフローが発生しない…はず。
分割代入使うとキレイに書ける。
分割代入をすこれ。

def fib(n)
    a, b = 0, 1
    n.times.each { a, b = b, a + b }
    a
end

動的計画法(メモ化)で書く

TODO 誰か書いてください。

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

Chefのチュートリアル

このチュートリアルでは、Chefについて学び、Chefを使ってAlibaba Cloud上でシンプルなReact.jsサイトを設定、構築、デプロイする方法を学びます。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

前提条件

このチュートリアルに従うには、以下のものが必要です。

  • 2つのAlibaba Cloud Elastic Compute Service (ECS)インスタンスがUbuntu 16.04でインストールされており、少なくとも2GBのRAMを持っています。これらのインスタンスのうち1つはChefノードに、もう1つはChefサーバに使用されます。また、これらのインスタンスでは、ポート22、80、443が開いていることを確認してください。
  • Chefノードとサーバの両方に使用する完全修飾登録ドメイン(またはFDQN)。Alibaba Cloudドメインを購入するか、Freenomで無料で取得することができます。

Chefを知る

Chefはいくつかの異なるコンポーネントで構成されています。これらのコンポーネントを理解することが重要なので、ここではChefの3つの主要なコンポーネントを簡単に見ていきましょう。

  • Chefサーバー:このコンポーネントは設定データのハブとして機能し、設定レシピ、cookbooks、ノードに適用されるポリシー、Chefによって管理されている各登録ノードを記述するメタデータがすべて保存される中心的な場所として機能します。また、このコンポーネントは、組織内の他のすべてのマシン(またはノード)がデプロイメント設定の目的で使用する中央マシンでもあります。
  • Nodes:ノードの重要なコンポーネントは、物理的または仮想的なもので、Chefを使用して管理できるマシンと理解することができます。Chef-clientは各ノードにインストールされ、各ノードを希望の状態に設定するために使用されます。ノードはchef-clientを使用してchefサーバーと対話し、レシピ、テンプレート、ファイル配布などの設定情報を取得します。
  • Workstation: Chef ワークステーションは、Chef の設定情報を作成または編集する場所です。設定ファイルはChefサーバーにプッシュされ、任意のノードにデプロイできるようになります。ワークステーションからChefサーバーとのやりとりは、knifechefコマンドラインツールを使用して行われます。

環境の設定

いよいよ実用的なことをする時が来ました。このチュートリアルでは、環境を設定し、chef を使って React.js アプリケーションを構築するために必要なツールをインストールします。

ワークステーションの設定

ワークステーションは基本的にはChefの設定の詳細をオーサリングする場所です。ワークステーションはあなたが選択したOSであれば何でも構いません。つまり、Linux、MacOS、Windowsの全てがここで動作することになります。

1.ChefDKパッケージをインストールします。Chef DK (開発キット)には、インフラストラクチャの開発とテストに必要なすべてのツールが含まれています。このチュートリアルを書いている時点では、現在の安定版(4.0.60)を使用しています。このリンクをクリックして、お使いのOSに基づいたChef DKをダウンロードしてください。

2.Chef DKをインストールしたことを確認し、コマンドラインからChef DKにアクセスできることを確認します。これを行うには、chef —versionを実行してインストールを確認することができます。

 chef@workstation:~$ chef --version
   Chef Development Kit Version: 4.0.60
   chef-client version: 15.0.300
   delivery version: master
   berks version: 7.0.8
   kitchen version: 2.2.5
   inspec version: 4.3.2
  1. MacおよびLinuxユーザーの場合は、Chef開発キットに含まれているRubyのバージョンがデフォルトのRubyのバージョンとして設定されていることを確認する必要があります。これを行うには、以下の手順に従ってください。
  • ターミナルを開き、which rubyを実行します。以下のような結果が得られるはずです。
   chef@workstation:~$ which ruby
       /usr/bin/ruby
  • chef shell-initサブコマンドを使用して、ChefDKに含まれるRubyをシステムRubyとして設定する必要があります。ChefDKによって使用されるアプリケーションは、ワークステーション上にも存在する可能性のある他のRuby開発ツールから分離することができます。
  chef@workstation:~$ echo 'eval "$(chef shell-init bash)"' >> ~/.bash_profile && source ~/.bash_profile

zshfish、Windows PowerShell (posh) など、bash とは異なるシェルを使用している場合は、SHELL_NAME をシェルに、SHELL_PROFILE をシェルプロファイルに置き換えて、以下のコマンドを実行してください。

 chef@workstation:~$ echo 'eval "$(chef shell-init SHELL_NAME)"' >> ~/.SHELL_PROFILE && source ~/.SHELL_PROFILE
  • もう一度which rubyコマンドを実行して、Chef開発キット版のrubyを使用していることを確認すると、以下のような出力が得られるはずです。
  chef@workstation:~$ which ruby
      /opt/chefdk/embedded/bin/ruby

4.次に、ワークステーション用の react-app-repo という作業ディレクトリを作成する必要があります。このためにchef generate repo repo_nameコマンドを使用することになります。これにより、Chefが必要とするファイルやフォルダ構造が作成されます。

 chef@workstation:~$ chef generate repo react-app-repo && cd react-app-repo

5.ワークステーションの設定が完了したので、Chefサーバーの設定と設定を行います。

Chefサーバーの設定

Chefサーバーを設定するには、以下の手順に従います。

1.ChefサーバーとしてUbuntu 16.04でインストールされたAlibaba Cloud ECSインスタンスをスピンアップし、ポート80、22、443が開いていることを確認します。クラウドでChefサーバを実行している場合は、セキュリティグループを通じてこれらのポートが開いていることを確認します。

2.Chefサーバーには、解決可能なドメインとホスト名が必要です。サーバーのホスト名がパブリックホスト名と一致していることを確認する必要があります。これは、以下のコマンドを実行することで可能です。

  chef@chef_server:~$ echo YOUR_DOMAIN_NAME | xargs sudo hostname

3.学習のために、このチュートリアルのために以下のbashスクリプトを作成しました。これはChef-serverパッケージをダウンロードしてインストールする際に役立つはずです。

  • 以下のスクリプトをサーバ上のパス /tmp/installChefServer.sh にコピーしてください。
   chef@chef_server:~$ sudo vi /tmp/installChefServer.sh

  bash
      #!/usr/bin/env bash

      BOLD='\e[1m'
      GREEN='\e[92m'
      NC='\e[0m'

      updateLinuxPackages() {
        printf "${BOLD}${GREEN}=================================== Updating all packages ============================================ ${NC}\n"
        apt-get update

      }


      createDirectories() {
        if [ ! -d /creds ]; then
          mkdir /creds
        fi
        if [ ! -d /downloads ]; then
          mkdir /downloads
        fi
      }

      downloadAndInstallChefServer() {
        if [ ! -f /downloads/chef-server-core_12.19.26-1_amd64.deb ]; then
          printf "${BOLD}${GREEN}=================================== Downloading the Chef server package... ================== ${NC}\n"
          wget -nv -P /downloads https://packages.chef.io/files/stable/chef-server/12.19.26/ubuntu/16.04/chef-server-core_12.19.26-1_amd64.deb
        fi

        if [ ! $(which chef-server-ctl) ]; then
          printf "${BOLD}${GREEN}=================================== Installing Chef server =================================== ${NC}\n"
          dpkg -i /downloads/chef-server-core_12.19.26-1_amd64.deb
          chef-server-ctl reconfigure

          printf "${BOLD}${GREEN}=================================== Waiting for services ====================================== ${NC}\n"
          until (curl -D - http://localhost:8000/_status) | grep "200 OK"; do sleep 15s; done
          while (curl http://localhost:8000/_status) | grep "fail"; do sleep 15s; done

          printf "${BOLD}${GREEN}============================= Creating user ========================== ${NC}\n"
          # creating user format: chef-server-ctl user-create USER_NAME FIRST_NAME LAST_NAME EMAIL 'PASSWORD' --filename FILE_NAME
          chef-server-ctl user-create admin admin admin admin@example.com 'notsecure' --filename /creds/chefadmin.pem

          printf "${BOLD}${GREEN}============================= Creating oganization with user ========================== ${NC}\n"
          # creating org format: chef-server-ctl org-create SHORT_ORG_NAME 'FULL_ORG_NAME' --association_user USER_NAME --filename FILE_NAME
          chef-server-ctl org-create chef "Chef-learn, Inc." --association_user admin --filename organization-validator.pem

          printf "${BOLD}${GREEN}============================= Adding Web UI for chef  ========================== ${NC}\n"
          chef-server-ctl install chef-manage
          chef-server-ctl reconfigure
          chef-manage-ctl reconfigure --accept-license
        fi

        printf "${BOLD}${GREEN}==================================== Your Chef server is ready! ================================== ${NC}\n"
      }

      main() {
        updateLinuxPackages
        createDirectories
        downloadAndInstallChefServer
      }

      main

  • このスクリプトでは、以下の情報を持つユーザーも作成します。

    • ユーザー名: admin
    • Firstname:admin
    • Lastname: admin
    • 電子メール: admin@example.com
    • パスワード: notsecure
  • 以下のコマンドを実行してスクリプトを実行可能な状態にします。

  chef@chef_server:~$ sudo chmod u+x /tmp/installChefServer.sh
  • 最後にスクリプトを実行します。少し時間がかかるかもしれません。
  chef@chef_server:~$ sudo /tmp/installChefServer.sh

4.ブラウザ上のChefのWebインターフェースにホスト名でアクセスできます。

image.png

ワークステーションがChef Serverと通信するように設定する

ワークステーションとChefサーバー間の通信を確立するために、すでにChefDKに含まれているKnifeユーティリティツールを利用します。

基本的に、Knifeは、Chefサーバーを認証するために2つのファイルを必要とします。

  • knifeの設定ファイル knife.rb には、Chef サーバーの URL、RSA 秘密鍵(SSH 鍵)の場所、cookbookのデフォルトの場所などの情報が含まれています。

  • RSA秘密鍵は、Chefサーバに送信されるすべてのリクエストを認証するために使用されます。公開鍵はChefサーバーが保持し、プライベート鍵はワークステーションが保持します。

ワークステーションとChefサーバー間の通信を確立するには、以下の手順に従ってください。

1.knife configureコマンドを実行して、knifeの設定を作成します。

YOUR-HOST-NAMEをchef-serverドメイン名に、SHORT-ORG-NAMEchefに置き換えます。また、クライアントキーを配置するための出力で指定されたパスをメモしておきます。

  chef@workstation:~$ knife configure
    WARNING: No knife configuration file found. See https://docs.chef.io/config_rb_knife.html for details.
    Please enter the chef server URL: [https://devops1c.mylabserver.com/organizations/chef]: https://YOUR-HOST-NAME/organizations/SHORT-ORG-NAME
    Please enter an existing username or clientname for the API: [user] admin
    *****

    You must place your client key in:
    /home/user/.chef/chefadmin.pem
    Before running commands with Knife

    *****
    Configuration file written to /home/user/.chef/credentials

2.あなたの react-app-repo ディレクトリに knife.rb ファイルを作成して、knife がcookbooksの場所を知っていることを確認する必要があります。

  chef@workstation:~$ mkdir .chef && echo 'cookbook_path ["#{File.dirname(__FILE__)}/../cookbooks"]' >> .chef/knife.rb

また、installChefServer.shスクリプトでユーザーを作成した時に作成したSSH鍵ファイルを、scpコマンドを使ってchef-serverからワークステーションにコピーする必要があります。

Note ~/.chef/admin.pemはクライアントキーを置くために指定されたパスですが、knife configureコマンドの出力で指定されたパスと異なる場合は変更してください。

そして、以下の項目に置き換えてください。

IP-ADDRESS-OR-HOSTNAME は、chef-serverのパブリックIPアドレス/ホスト名に置き換えます。

USERはChefサーバーのユーザー名に置き換えてください。

PATH_TO_YOUR_SSH_KEY は、Chefサーバの ssh-key へのパスに置き換えてください。

   chef@workstation:~$ scp -i PATH_TO_YOUR_SSH_KEY USER@YOUR-IP-ADDRESS-OR-HOSTNAME:/creds/chefadmin.pem ~/.chef/admin.pem

あるいは、サーバへのアクセスを得るための認証手段としてパスワードを使用している場合は、以下のコマンドを実行することもできます。

 chef@workstation:~$ scp USER@YOUR-IP-ADDRESS-OR-HOSTNAME:/creds/chefadmin.pem ~/.chef/admin.pem

3.次に、以下のコマンドを使用してChefサーバーからSSL証明書を取得し、検証する必要があります。

chef@workstation:~$ knife ssl fetch
    WARNING: Certificates from www.mydomainname.com will be fetched and placed in your trusted_cert
    directory (/home/user/.chef/trusted_certs).

    Knife has no means to verify these are the correct certificates. You should
    verify the authenticity of these certificates after downloading.

    Adding certificate for www_mydomainname_com in /home/user/.chef/trusted_certs/www_mydomainname_com.crt

knife ssl checkコマンドを実行して、SSL構成を確認します。

  chef@workstation:~$ knife ssl check
     Connecting to host ec2-34-207-124-26.compute-1.amazonaws.com:443
     Successfully verified certificates from ec2-34-207-124-26.compute-1.amazonaws.com

これでワークステーションの設定が完了し、knifeを使ってChefサーバーに接続できるようになりました。次のパートでは、ノードの設定を行います。

ノードの設定とブートストラップ

Chefで設定を管理する作業を開始する前に、作業するノードが必要になります。これを行うには、Ubuntu 16.04をインストールした別のECSインスタンスを作成し、ワークステーションからKnifebootstrapサブコマンドを利用します。ノードサーバを作成するだけで、ほとんどの作業はworkstationから行われます。

1.Ubuntu 16.04をインストールした別のAlibaba Cloudインスタンスを作成し、ポート80、22、443、3000が開いていることを確認します。react.jsアプリケーションはポート3000で実行されます。

2.以下のコマンドを実行して、ノードサーバのホスト名がパブリックホスト名と一致していることを確認する必要があります。

では、次のように置き換えます。

  • IP-ADDRESS-OR-HOSTNAMEをノードサーバのパブリック IP アドレス/ホスト名に置き換えます。

  • ノードサーバ上のユーザの名前を持つUSER

  • PATH_TO_YOUR_SSH_KEY にノードサーバの ssh-key へのパスを指定します。

 chef@workstation:~$ ssh -i PATH_TO_YOUR_SSH_KEY USER@YOUR-IP-ADDRESS-OR-HOSTNAME 'echo YOUR_DOMAIN_NAME | xargs sudo hostname'

3.ワークステーションから、新しいクラウドサーバーをChefサーバーが管理するノードにブートストラップするには、以下のコマンドを実行します。

以下では、

  • IP-ADDRESS-OR-HOSTNAMEをノードサーバのパブリックIPアドレス/ホスト名に置き換えます。

  • ノードサーバ上のユーザの名前を持つUSER

  • PATH_TO_YOUR_SSH_KEY にノードサーバの ssh-key へのパスを指定します。

  • reactJS-nodenode_nameを指定します。node_nameはノードに与えたい名前であることを理解してください。

 chef@workstation:~$ knife bootstrap IP-ADDRESS-OR-HOSTNAME --connection-user USER --sudo -i PATH_TO_YOUR_SSH_KEY --node-name NODE-NAME
    Bootstrapping 34.76.3.167
    [34.76.3.167] -----> Installing Chef Omnibus (stable/15)
    [34.76.3.167] 
    [34.76.3.167] downloading https://omnitruck.chef.io/chef/install.sh
    ......
    [34.76.3.167] Getting information for chef stable 15 for ubuntu...

    [34.76.3.167] Installing chef 15
    [34.76.3.167] 
    [34.76.3.167] 
    [34.76.3.167] Thank you for installing Chef Infra Client! For help getting started visit https://learn.chef.io
    [34.76.3.167] 
    [34.76.3.167] Starting the first Chef Infra Client Client run...
    [34.76.3.167] 
    [34.76.3.167] +---------------------------------------------+
    [34.76.3.167] 
    [34.76.3.167]  2 product licenses accepted.
    [34.76.3.167] 
    [34.76.3.167] +---------------------------------------------+
    [34.76.3.167] 
    [34.76.3.167] Starting Chef Infra Client, version 15.0.300
    [34.76.3.167] 
    [34.76.3.167] resolving cookbooks for run list: []
    [34.76.3.167] Synchronizing Cookbooks:
    [34.76.3.167] Installing Cookbook Gems:
    [34.76.3.167] Compiling Cookbooks...
    [34.76.3.167] [2019-07-01T06:52:36+00:00] WARN: Node reactJS-node has an empty run list.
    [34.76.3.167] Converging 0 resources
    ........
    [34.76.3.167] Running handlers:
    [34.76.3.167] 
    [34.76.3.167] Running handlers complete
    [34.76.3.167] Chef Infra Client finished, 0/0 resources updated in 02 seconds

あるいは、サーバへのアクセスを得るための認証手段としてパスワードを使用している場合は、以下のコマンドを実行してください。

 chef@workstation:~$ knife bootstrap IP-ADDRESS-OR-HOSTNAME -N NODE_NAME -x USER -P 'PASSWORD' --sudo

4.自分のノードがChefサーバーに関連付けられていることを確認するには、knife node listコマンドを実行します。

 chef@workstation:~$ knife node list
    reactJS-node

knife node showコマンドを実行することで、ノードのデータを見ることができます。

knife node show NODE-NAME
chef@workstation:~$ knife node show reactJS-node
   Node Name:   reactJS-node
   Environment: _default
   FQDN:        ip-172-31-56-238.home.internal
   IP:          41.215.245.118
   Run List:    
   Roles:       
   Recipes:     
   Platform:    ubuntu 16.04
   Tags:

React.jsアプリケーションの構築

chefサーバー、ワークステーション、ノードのセットアップが完了したので、 react.jsアプリケーションを構築する準備が整いました。以下のようなcookbookを書くことになります。

  • node.jsのセットアップとインストール
  • pm2をインストールします。pm2はプロセスマネージャーであり、react.jsアプリの動作を維持するのに役立ちます。
  • react.jsアプリケーションをインストールします。

  • react.jsアプリケーションを起動します。

そのためにchef executebashリソースを使うことになります。これらのリソースはどちらもコマンドやスクリプトを実行するために使用します。execute リソースは単一のコマンドを実行するために、bash リソースは複数行のコマンドを実行するために使用します。構文やプロパティの詳細については、execute リソースと bash リソースのドキュメントを参照してください。

ワークステーションの cookbooks/example/attributes/recipes ディレクトリにある default.rb ファイルを開き、以下のように置き換えてください。

bash 'Install Node.js' do
 cwd "/home/ubuntu"
 code <<-EOH
   curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh
   bash nodesource_setup.sh
   apt-get install nodejs -y 
   EOH
end

execute 'Install pm2' do
 cwd "/home/ubuntu"
 command 'npm install pm2 -g'
end

execute 'Setup react app' do
 cwd "/home/ubuntu"
 command 'npx create-react-app my-app'
 only_if do ! File.exist?('/home/ubuntu/my-app') end
end

bash 'Start application' do
 cwd "/home/ubuntu"
 code <<-EOH
   cd my-app
   pm2 start npm -- start
   EOH
end

cookbookを実行するためには、Chefサーバーにアップロードする必要があります。このファイルに変更を加えるたびに、再アップロードする必要があることに注意してください。react-app-repoディレクトリにあることを確認して、このコマンドを実行してください。

chef@workstation:~$ knife upload cookbooks/example

以下のコマンドを使用して、ノードのrun_listにCookbookのrecipeを追加する必要があります。

chef@workstation:~$ knife node run_list add reactJS-node 'recipe[example]'
reactJS-node:
  run_list: recipe[example]

run_listの詳細はドキュメントで確認できます。

さて、knife sshコマンドを使ってreactJS-nodeにcookbookをデプロイします。

そして、以下のように置き換えてください。

  • USERをノードサーバ上のユーザ名に置き換えてください。

  • PATH_TO_YOUR_SSH_KEY にノードサーバの ssh-key へのパスを指定します。

chef@workstation:~$ knife ssh 'name:reactJS-node' 'sudo chef-client' --ssh-user USER --ssh-identity-file PATH_TO_YOUR_SSH_KEY

これで、ブラウザのウィンドウに http://NODE_SERVER_IP_ADDRESS:3000 を貼り付けて react.js アプリケーションにアクセスできるようになりました。このようなページが表示されるはずです。

image.png

結論

このガイドでは、Chefを始める上での柔軟な基礎を解説しています。Chefの詳細については、Chefの公式ドキュメントを参照して、chef rallyを学ぶことからご覧ください。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

マイグレーション データ型は何が適しての??

マイグレーションで実現できること

テーブル作成
テーブル削除
カラム追加
カラム名変更
カラムのデータ型変更
カラム削除

マイグレーションファイル作成

マイグレーションファイルは モデルを作成した時 に一緒に作成される。
さらに マイグレーションファイル単体 で作成することも可能。

ターミナル
rails g model [モデル名] [属性名:データ型 属性名:データ型・・・] [オプション]

#単体で生成する場合
rails g migration マイグレーション名

上記のコマンドを実行することで、db/migrateフォルダの中にマイグレーションファイルが作成される。

データ型は何が適してるの??

オフィシャル情報 日本語でざっくり概要
primary_key プライマリキー
string 文字列(1〜255文字)
text 長い文字列(1〜4294967296文字)
integer 整数(4バイト)
bigint 整数(8バイト)
float 浮動小数
decimal 精度の高い少数
numeric 数値
datetime 日時
time 時間
date 日付
binary バイナリーデーター
boolean Boolean型

似ているけど違うものはどうするのか

文字を扱う場合、stringとtextどちらが適しているか

stringは、255文字までしか扱うことが出来ないため、状態や時期によっては、256文字以上になる可能性のあるデータを扱う場合はtextを使うことをおすすめします。
名前やメールアドレス、社名などの文字情報は、stringで取り扱い、本文や備考などの文章情報はtextを使うのが一般的です。

enumを扱う場合、stringとintegerどちらが適しているか
hashのintegerをマッピングできるのがenumなので、enumで扱う予定のカラムをstringにしたらそもそもenumを取り入れる意味がないし、機能しないですね。ということを冷静に考えて、enumで扱う予定のカラムのデータ型はintegerが適しています。

idを扱う場合、 integerとbigintどちらが適しているか
idは、将来的にユーザーが増えると膨大な桁数になる可能性があります。Rails5.1からidカラムがデフォルトでbigintになっていることも踏まえて、基本的にはbigintが適していると考えて良いでしょう。

いずれも、現状と一致させるという考え方よりは、将来的に「入らない!」という状況になる可能性が少しでもあれば大きめの箱を用意する、という考え方で選定すると安心です。

補足

Rails modelの命名規約

Railsでは、modelに対応するデータベースのテーブル名はmembersのように複数形になります。

しかし、modelのクラス名は、Memberのように頭が大文字の単数形になる。

また、modelを作成する時は、「rails g model member」のようにmemberを小文字始めても大丈夫です。

また、例えばmember_imageと指定してもMemberImageとしても、MemberImageモデル(テーブル名はmember_images)が作成されます。

ただし、membersのように複数形にすることは厳禁です。Membersモデルが作成されてしまいます。modelは必ず単数形で作成しましょう。

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

AWS☆☆☆☆ デプロイまでの道のり4-最終章(短いバージョン、全4回)

1)背景

第4回目の最終章です。
自身のポートフォリオをデプロイしました。最後の作業としてドメインの紐つけをします。
AWS関連手順記事はすごく多いので、ここでは備忘録も含めて、非常に端的に手順を記載します。

2)環境

項目 内容
OS.Amazon Linux AMI release 2018.03
Ruby v2.5.1
Ruby On Rails v5.2.4.3
MySQL v5.6
Unicorn v5.4.1

3)内容

以下設定で65分程度かなと思います。(段取りが分かっていれば、30分)
※【ローカルマシン】指定以外は、全てAWSでの作業になります。

(1)お名前ドットコムからドメインを取得(20分)

同サイトからドメインを契約します。ここについては割愛いたします。

(2)Nginxの導入と設定(20分)

  • yumパッケージを活用してNginx(webサーバ)を導入する
  • Nginxのconfigファイルを設定する
  • Nginxのパッケージ権限の付与(/var/lib/nginx)
  • Unicorn設定ファイルの変更(Nginxを経由して処理を行うために設定)

(3)AWS-Route53の設定(10分)

  • ホストゾーンの新規作成
  • 新規レコードセットに同ElaspicIPを登録する

(4)お名前ドットコムの設定(15分)

  • AWSで新規に割り当てられたNSレコードをネームサーバ名として登録する
  • DNS設定(ElaspicIPと、ドメインの紐付けを行う)

(2)Nginx設定2(10分)

  • 登録したドメイン名を同設定ファイルに設定する。

これで完了です。
指定のドメインにアクセスすると、NGINXを介してUnicornが起動しているrailsアプリを参照することが出来ます。
正直簡単です。出来るだけ難しく書かないように端的に記載しました。
以上です。

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

【CircleCI】GitHubへプッシュ時に自動デプロイする際の手順

今まで自動デプロイは「capistrano」を使用していましたが、毎回コードを修正するたびにローカルでmasterマージして、「bundle exec cap production deploy」を実行していました。

今回はcircleCIを使用し、masterマージされたら自動でサーバーへSSHしてコードをデプロイするように実装してみました。
以下、実装の際の流れや詰まった部分の解説をまとめていきます!

- 開発環境

  • ruby : 2.5.1
  • rails : 2.4.2.3
  • circleCI : 2.1

※ dockerは今回使用していません。

.circleci/config.ymlの設定

①最初にcircleCIで使うconfig.ymlの設定をローカルでしていきます。
circleCIの設定は、該当リポジトリのルート直下に.circleciと言うフォルダを作り、その中のconfig.ymlで行います。

  • config.ymlに以下を追記。
config.yml
version: 2.1
orbs:
  ruby: circleci/ruby@0.1.2 

jobs:
  deploy:
    machine:
      enabled: true
    steps:
      - add_ssh_keys:
          fingerprints:
            - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" #SSHするサーバの秘密鍵を登録後、ハッシュ化されているfingerprintsをコピペする。
      - run: ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "/var/www/リポジトリ名/deploy-me.sh"

workflows:
  version: 2.1
  deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only: master
  • ルート直下に「deploy-me.sh」ファイル作成後、下記を記入。
deploy-me.sh
#!/bin/bash

cd /var/www/リポジトリ名/ && git pull

以下、解説します。
①こちらで自動デプロイの設定をしており、masterマージしたら自動的にデプロイされる仕組みになっている。
②「- add_ssh_keys:」以下には、HOST(IPアドレス)と秘密鍵を登録した際、ハッシュに変換されたfingerprintsをコピペしてくる。これによりSSHするサーバーの秘密鍵を登録できる。
③「- run: ssh -p ~ 」で、設定した鍵 + 設定した環境変数によりsshし、サーバーにログインする。その後、「“/var/www/rails/soup/deploy-me.sh”」によりプルリクしたらgit pullするように設定している。
④workflows以下の設定により、masterブランチ以外ではデプロイを実行しないようにしている。

cirecleCI導入・自動デプロイしたいリポジトリの追加

①まずcircleCIのHP(https://circleci.com) にいき、新しくアカウント登録(sign up)をします。GitHubのアカウントを持っていたらすぐに連携でき、アカウント登録が可能です。

②ログイン後、「Add Projects」を選択し、今回自動デプロイしたいアプリやサイトのリポジトリを追加していきます。
追加するには該当のリポジトリの「Set Up Project」をクリックします。

③上記で設定したconfig.ymlをコピペし、「Add Manually」で登録していく。 この際、秘密鍵の登録はまだできていないです。後に登録していきます。
image.png

④「Start Building」を選択。今回対象となるリポジトリを追加していきます。
追加後、エラーになっていると思いますがそれは秘密鍵や環境変数の設定が済んでいないからです。そちらの情報を追加していきます。

秘密鍵の登録

①ダッシュボード「Pipelines」の右上にある「Project Settings」をクリック。

②「SSH Keys」を選択します。一番下に「Additional SSH Keys」があるため、こちらにローカルの秘密鍵の情報を登録していきます。

③「ADD SSH Key」をクリック。「Hostname」はデプロイ先のIPアドレスを入力します。

④「Private Key*」に秘密鍵を入力するために、ローカルで「.ssh」ディレクトリに入り、lsキーを使って秘密鍵があることを確認します(id_rsa等)。こちらをcatで見ていきます。表示された情報を全てコピペし、「Private Key*」に貼り付けます。この際、「-----BEGIN RSA PRIVATE KEY-----」なども全て含めます。

⑤「Add SSH Key」を押し、無事に通れば設定完了です。エラーが出る場合、pem形式で秘密鍵を登録していないか、秘密鍵の情報が新しい形式のため非対象エラーになっている可能性があります。私の場合は後者のエラーが発生しました。

私は下記のサイトを参考にしてエラーを解消しました。
https://blog.adachin.me/archives/11554
秘密鍵を旧式に更新してから行ったことをまとめました。
https://qiita.com/akk_ayy/items/61215e89cfcf680d1c94

⑥最後に、ハッシュ化されている fingerprints をコピペし、config.ymlの該当部分にペーストします。

環境変数の設定

①「Project Settings」下の「Environment Variables」を選択します。

②「Add Variable」を選択。デプロイ先のポート情報とユーザー名、ホスト名を登録していきます。
- SSH_PORT
- SSH_USER
- SSH_HOST
これで環境変数の設定が完了しました。

③ローカルで設定した内容でSSHログインができるか確かめていきます。

ssh -p ポート ユーザー名@ホスト名 -i ~/.ssh/秘密鍵の名称

これでログインでき、

/var/www/リポジトリ名/deploy-me.sh

これを叩いてgit pullが実行できたら自動デプロイの準備ができています!

自動デプロイを実行

テストデータをmastarマージし、自動デプロイできているか確認。
この際、circleCIがSUCCESSになっていたら成功です!お疲れ様でした!

参考サイト
https://blog.adachin.me/archives/10997
https://qiita.com/tatane616/items/8624e61473a9957d9a81
https://www.tweeeety.blog/entry/2018/02/09/195345

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

deviseを利用してRailsアプリケーションに認証機能を作る

はじめに

  • deviseはRailsアプリケーションに簡単に認証機能を追加することができるgemです。deviseはwardenというRackミドルウェアのラッパーで、実際の認証はwardenが行っています(そのため、実装の詳細を知りたい場合はwardenの実装をまず見る必要があるみたいです)。
  • Railsチュートリアルでは認証機能をスクラッチで作成していますが、実際のRailsアプリケーションではこのdeviseを利用して認証機能を作成することがデファクトスタンダードとなっているようです。

  • 今回は自分の勉強のため、deviseを利用してRailsアプリケーションに認証機能を追加する方法を整理したいと思います。なお内容はこちらの記事をほぼ踏襲しています。謝謝

  • なお以下の実装はすべてこちらのリポジトリにあります。

環境

  • Ruby on Rails 5.2.4.3
  • Ruby 2.6.4

deviseのインストール

1.Gemfileを編集

# Devise
gem 'devise'

2.bundle install

$bundle install

deviseの設定

1.以下のコマンドを実行

$ rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

     * Not required *

===============================================================================

デフォルトURLを設定

config/development.rb
Rails.application.configure do
  ~~中略~~
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end

rootページを作成

ここはスキップして、任意のページをrootページに設定しても大丈夫です。

$rails g controller StaticPages index
route.rb
Rails.application.routes.draw do
  root 'static_pages#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

flashメッセージを設定

app/views/layouts/application.html.erbを編集

<!DOCTYPE html>
<html> 
 <head>
  <title>DeviseRails5</title>
  <%= csrf_meta_tags %>

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
 </head>
 <body>
  <p class="notice"><%= notice %></p>
  <p class="alert"><%= alert %></p>

  <%= yield %>

 </body> 
</html>

deviseのviewを作成

$ rails g devise:views
invoke  Devise::Generators::SharedViewsGenerator
create    app/views/devise/shared
create    app/views/devise/shared/_error_messages.html.erb
create    app/views/devise/shared/_links.html.erb
invoke  form_for
create    app/views/devise/confirmations
create    app/views/devise/confirmations/new.html.erb
create    app/views/devise/passwords
create    app/views/devise/passwords/edit.html.erb
create    app/views/devise/passwords/new.html.erb
create    app/views/devise/registrations
create    app/views/devise/registrations/edit.html.erb
create    app/views/devise/registrations/new.html.erb
create    app/views/devise/sessions
create    app/views/devise/sessions/new.html.erb
create    app/views/devise/unlocks
create    app/views/devise/unlocks/new.html.erb
invoke  erb
create    app/views/devise/mailer
create    app/views/devise/mailer/confirmation_instructions.html.erb
create    app/views/devise/mailer/email_changed.html.erb
create    app/views/devise/mailer/password_change.html.erb
create    app/views/devise/mailer/reset_password_instructions.html.erb
create    app/views/devise/mailer/unlock_instructions.html.erb

Userモデルを作成

rails g devise User

上記のコマンドで以下のマイグレーションファイルが生成されます。

db/migrate/20200622180124_devise_create_users.rb
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.inet     :current_sign_in_ip
      # t.inet     :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

デフォルトではdatabase_authenticatable、registerable、recoverable、rememberable、 validatableがオンになっているようです。

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

今回はデフォルトのまま作成しますが、オプションで10個のモジュールから好きなものを有効化できます。

モジュール   概要                                                       
Database Authenticatable サインイン中にユーザーの信頼性を検証するために、パスワードをハッシュしてデータベースに保存します。認証は、POST要求またはHTTP基本認証の両方で実行できます。
Omniauthable OmniAuth(https://github.com/omniauth/omniauth)サポートを追加します。
Confirmable 確認手順が記載された電子メールを送信し、サインイン時にアカウントが既に確認されているかどうかを確認します。
Recoverable ユーザーのパスワードをリセットし、リセットの指示を送信します。
Registerable 登録プロセスを通じてユーザーのサインアップを処理し、ユーザーが自分のアカウントを編集および破棄できるようにします.
Rememberable 保存されたCookieからユーザーを記憶するためのトークンの生成とクリアを管理します.
Trackable サインイン数、タイムスタンプ、IPアドレスを追跡します。.
Timeoutable 指定した期間アクティブでなかったセッションを期限切れにします。
Validatable 電子メールとパスワードの検証を提供します。 これはオプションであり、カスタマイズできるため、独自の検証を定義できます。
Lockable 指定された回数のサインイン試行の失敗後にアカウントをロックします。 メールまたは指定した期間の後にロックを解除できます

マイグレーションを実行します

$ rake db:migrate

Viewを編集

ヘッダーを追加します

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html> 
 <head>
  <title>DeviseSampleApp</title>
  <%= csrf_meta_tags %>

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
 </head>
 <body>
 <header>
    <nav>
        <% if user_signed_in? %>
        <%= link_to 'プロフィール変更', edit_user_registration_path %>
        <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
    <% else %>
        <%= link_to 'サインアップ', new_user_registration_path %>
        <%= link_to 'ログイン', new_user_session_path %>
        <% end %>
    </nav>
  </header>
  <p class="notice"><%= notice %></p>
  <p class="alert"><%= alert %></p>

  <%= yield %>

 </body> 
</html>
  • トップページにアクセスすると、こんな感じの画面が生成されています。
    スクリーンショット 2020-06-23 3.48.36.png

  • サインアップをクリック
    スクリーンショット 2020-06-23 3.34.51.png

  • サインアップしてメールアドレスとパスワードを入力すると、見事にログインできました!
    スクリーンショット 2020-06-23 3.35.29.png

その他

deviseが提供するヘルパーメソッド

user_signed_in?

ユーザがログインしているかどうかを確認できます。

current_user

ログインしているユーザにアクセスできます。

user_session

セッションにアクセスできます。

サインアップ後のリダイレクト先を変更

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  def after_sign_up_path_for(resource)
    edit_user_registration_path #ここを編集して任意のページに飛ばせる
  end
end

サインアップ後、以下のページにリダイレクトするようになりました。

スクリーンショット 2020-06-23 3.34.18.png

ログイン後のリダイレクト先を変更

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  def after_sign_in_path_for(resource)
    edit_user_registration_path #ここを編集して任意のページに飛ばせる
  end
end

コールバック関数

(ここは正確にはよくわかっていませんが)

  • deviseが呼んでくれるコールバックはafter_fetchだけらしいので(要出典)、その他のコールバックを呼びたいときはwardenのインターフェイスを直接呼び出す必要があります。

参考文献

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