20200121のRailsに関する記事は25件です。

【Rails】devise実装後のユーザー新規登録時のエラー[ArgumentError in Users::RegistrationsController#create]

概要

Railsアプリにて、ログイン機能を実装するためにgem 'devise'を利用。
users/sign_upからユーザーの新規登録を行ったところエラーが発生した。

エラー文

ArgumentError in Users::RegistrationsController#create

wrong number of arguments (given 0, expected 1)

app/controllers/users/registrations_controller.rb:13:in `create'

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
  end

wrong number of arguments (given 0, expected 1)
引数が1つ渡されるはずなのに0になってますよ。ということでコントローラーでcreateアクションがうまくできていない。

結論

has_secure_password password_digestを使っていたから。
devise導入時はhas_secure_password password_digestを使ったセキュアパスワード設定は行わない。
代わりにencrypt_passwordをテーブルカラムに追加することでセキュアパスワード設定ができる。

今回の解決までの流れ

参考:http://ja.voidcc.com/question/p-zenkcgoo-cv.html

調べるとhas_secure_passwordがいらないという情報があったので、userモデルから消しました。

すると、同じようにユーザーの新規登録をすると異なるエラーメッセージが出ました。
password_digestについてエラーが出ている模様。

ActiveRecord::NotNullViolation in Users::RegistrationsController#create

PG::NotNullViolation: ERROR: null value in column "password_digest" violates not-null 
constraint DETAIL: Failing row contains (5, test, test@a.com, test@a.com, null, null,
 2020-01-21 01:37:23.23543, 2020-01-21 01:37:23.23543,
$2a$11$mFFmw7JD8nrrm3hbz/LUQeFwRPYjjBNKIRAjVuRBNXUM57v2JeeI6, null, null, null).
: INSERT INTO "users" ("name", "email", "profile", "created_at", "updated_at",
 "encrypted_password") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"

password_digestdevise導入前にセキュアパスワード設定のために用意していたカラムです。
調べてみるとdeviseを使用するときはdevisepassword_digestの代わりにencrypt_passwordを作ってくれるので、password_digest`はいらないことがわかりました。

確かにdeviseの導入を行っている際にコマンド

$ rails generate devise user

を実行し、作られるマイグレーションファイルにencrypt_passwordが入っていました。

schema.rb
class AddDeviseToUsers < ActiveRecord::Migration[5.2]
  def self.up
    change_table :users do |t|
      ## Database authenticatable
      t.string :encrypted_password, null: false, default: ""
(省略)
end

今回はこのままマイグレーション実行していたのですが、既にusersテーブルにpassword_digestカラムがあったため上書きされず、encrypted_passwordは反映されてませんでした。
encrypted_passwordについて調べずにマイグレーション実行していたことを反省・・・

ということで、password_digestカラムを削除してから、マイグレーションをやり直してエラーは解決しました。

もう一度結論ですが、deviseを利用してパスワードをハッシュ化し、セキュアパスワード設定をするにはカラムにpassword_digestではなくencrypt_passwordを使うことになります。

他にもusers/newでcreateされるユーザーはdevise管理化の
controllers/users/registrations_controller.rbのnewやcreateメソッドに従って挙動するので、
controllers/users/users_controller.rbのnewやcreateメソッドは不要になるなど
deviseを実装することで不要になるものはいくつかあります。

参考:http://genestream.hatenablog.com/entry/2014/10/28/110934

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

[Welcome to nginx on the Amazon Linux AMI!]nginxサーバにアプリケーションをアップロードした時に起こるルーティングエラーの解決例

1.エラーの様子

まずエラーまでの流れですが、ローカル環境でエラーもなく、GitHubへのcommit,pushをし、EC2サーバー(nginx)上に自動デプロイが終わった後にElastic id(個人のipアドレスに紐付け済みの公開用ipアドレス)に接続したところ下記のような画面が表示されました。

<出てきたエラー文>
スクリーンショット 2020-01-21 21.34.57.png

<出て欲しい画面>
スクリーンショット 2020-01-21 22.05.09.png

2.どんなエラー?

自分が設定したアプリケーションのアドレスではなく、nginxのホームに接続してしまうというエラーになっています。

3.エラーの原因

ネットサーバーまでのルーティングの設定エラーです

見つけ方としては、エラーまでの流れで説明した通り、ローカル環境でエラーを出していないため、ルーティングの設定ミスだとわかります。

4.解決方法

ルーティングに関する箇所を調べて打ち忘れ打ち間違いを修正します。
具体的には下記の原因が考えられます

①開発したファイルの中のconfig/locals/unicorn.rbの記載ミス
②config/deploy/production.rbの記載ミス
③config/deploy/deploy.rbの記載ミス
④nginxの設定の記載ミス(EC2サーバにログインして[ec2-user@ip-172-xx-xx-xxx ~]$ sudo vim /etc/nginx/conf.d/rails.confとコマンドを打つことで確認できます)

そして今回筆者は④で下記のように設定していました。
<間違った設定>
スクリーンショット 2020-01-21 22.23.49.png

よくよく見るとこの中の'sever_name'に余計な文字が入っており間違っています。
設定にはElastic idだけで良いので正しくは下記の通りとなるべきでした。
<正しい設定>
スクリーンショット 2020-01-21 22.23.08.png

こちらを修正したところ、検索の箇所にElastic id(13.112.68.204)を打つことで正しいアプリケーションのサイトに飛ぶよう設定することができました。

※ここまでのルーティングのチェックでローカルのファイルに変更などが生じている可能性があるため、GitHubのcommit忘れの確認と、自動deploy(またはpull origine masterコマンド)を行っておきましょう。更新忘れてるかもと思ったら、この更新作業は何度コマンドしても大丈夫なのでとりあえず行ってみましょう。

何かの参考にしてください。

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

MVCとはなんなのか

Ruby on Rails初心者として、自分自身の理解を整理したいのでまとめたいと思います。
何か認識違いがあれば、ご指摘いただければ幸いです。

MVCとは

UI(ユーザインタフェース)を持つソフトウエアのアーキテクチャの一種。
画面に表示する部分(UI)は、アプリケーション固有のデータや処理の扱いの性質が異なるため、画面の表示とアプリケーションのデータに関する部分を混ぜて記述してしまうと、コードが複雑化してしまい、保守性が悪くなってしまいます。

そこで、アプリケーション固有のデータの部分、画面を表示させるUIの部分、この2つをつなぎ合わせる制御の部分の3つに分けて管理をしやすくしようというものです。

M(model)

アプリケーション固有のデータや処理の扱いの部分
・データベースへの保存、読み込み、永続化を担当する。

V(View)

UIに関わる部分
・HTTPレスポンスの中身を実際に組み立てて、画面を表示する。

C(Controller)

ModelとViewを統合的に制御する部分
・ブラウザからのリクエストを受けて適切なレスポンスを作成するための制御を行う。

MVCのメリット

・それぞれが専門的な分野になるため、一つ一つの仕様変更がしやすくなる。
・分野ごとに得意な人が開発を進められる。

まとめ

MVCアーキテクチャを使うことによって複雑化するコードをそれぞれの3つの分野に分けることで単純化し、管理しやすくすることで、それぞれが得意な分野に分かれて開発できるようになり、作業しやすい環境が生まれるということ(と感じました)。

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

docker環境で db:migrate:resetしたいとき( seed_fuも)

dbをリセットしたいとき

dockerを触り始めたばかりのときは停止させたdockerを再起動するのにも手こずりますので初歩的な手順。
大体rails のコマンド前にdocker-composeをつけるだけですが。

docker-compose exec コンテナ名 bin/rails db:migrate:reset

コンテナは停止させなくて良いです
なぜならそのコンテナに対してmigrateリセットするからです

docker-compose exec コンテナ名 bin/rails db:migrate
docker-compose exec コンテナ名 bin/rails db:seed_fu

docker-composeのときはymlファイルを参照するのでコンテナIDではなくコンテナ名を記述してください。

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

CodeCommit + CodeDeploy + CodePipelineでEC2にデプロイ~CodeDeployの設定~

目次
1. CodeCommitの設定
2. CodeDeployの設定(この記事)
3. CodePipelineの設定・デプロイ実行

AWSのCodeCommit、CodeDeploy、CodePipelineを組み合わせてEC2にデプロイするまでをまとめました。
この記事ではCodeDeployでデプロイアプリケーションを作成する手順を説明します。

前提

  • デプロイ先のEC2インスタンスは作成済みとします。
  • ELB(ロードバランサー)はApplication Load Balancerを使用する想定とします。
  • Ruby on Railsアプリケーションをデプロイする想定とします。
  • EC2 : Amazon Linux2

CodeDeploy

EC2インスタンスやオンプレミスインスタンス、AWS Lambda、Amazon ECSに対するアプリケーションのデプロイを自動化するデプロイメントサービスです。
EC2に対するデプロイは無課金で実行できます。

用語
  • AppSpec file : デプロイの内容を定義したファイル。YAMLもしくはJSONで記述する。
  • リビジョン : S3もしくはGithubにアップロードした、アプリケーションコードとAppSpec fileをバンドルしたアーカイブファイル。

デプロイ要件

CodeDeployのデプロイタイプは「インプレースデプロイ」と「Blue/Greenデプロイ」が選択でき、今回は「インプレースデプロイ」を選択します(デプロイタイプについては後ほど説明します)。
デプロイ作業中にロードバランサーのトラフィック制御は行いません。

EC2へのインプレースデプロイは以下のフローで行われます。

  1. アプリケーションにAppSpec fileを含めたアーカイブファイルをAmazon S3、もしくはGithubにアップロード。
  2. アップロードしたリビジョンの情報を次のデプロイ対象としてCodeDeployに提供。
  3. デプロイ先EC2に常駐しているCodeDeployAgentプロセスがCodeDeployをポーリングして、デプロイ対象リビジョンの置き場所・デプロイ実施時期などの情報を取得する。
  4. 3で取得した情報に基づいて、EC2のCodeDoployAgentがS3やGithubからデプロイ対象リビジョンをpullして、AppSpec fileの手順に従ってデプロイを実行する。

準備

S3にリビジョンを置くバケットを作成

  1. Amazon S3コンソール(https://console.aws.amazon.com/s3/)を開きます。
  2. [バケットを作成する]を選択します。
  3. [バケット名]を入力して[バケットの作成]を選択します。

CodeDeployからEC2にアクセスためのサービスロールを作成

  1. IAMコンソール(https://console.aws.amazon.com/iam/)を開きます。
  2. ナビゲーションペインの[ロール]を選択し、[作成]を選択します。
  3. [ロールの作成]ページで[AWSサービス]を選択し、[このロールを使用するサービスを選択]リストからCodeDeployを選択します。
  4. [ユースケースの選択]でCodeDeployを選択し、[次へ]を選択します。
  5. アクセス権限ポリシーのを確認して[次へ]を選択します。
  6. [ロールの名前]にサービスロール名(例:AWSCodeDeployRole)を入力し、[作成]を選択します。

EC2からS3にアクセスするためのインスタンスプロファイルを作成

 

  1. IAMコンソール(https://console.aws.amazon.com/iam/)を開きます。
  2. ナビゲーションペインの[ポリシー]を選択し、[作成]を選択します。
  3. [JSON]タブに以下を貼り付けて[確認]を選択します。
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "s3:Get*",
                    "s3:List*"
                ],
                "Effect": "Allow",
                "Resource": "*"
            }
        ]
    }
    

  4. [ポリシー名]に適当な名前を入力して[作成]を選択します。
  5. ナビゲーションペインで[ロール]を選択し、[作成]を選択します。
  6. [AWSサービス]を選択し、[このロールを使用するサービスを選択]リストからEC2を選択します。
  7. [ユースケースを選択]でEC2を選択し[次へ]を選択します。
  8. 先ほど作成したポリシーを選択し、[次へ]を選択します。
  9. [タグの追加]は何もせずに[次へ]を選択します。
  10. [ロールの名前]に適当なロール名を入力して[作成]を選択します。

EC2にCodeDeployAgentをインストール・起動

EC2にSSH接続して、下記コマンドを実行します。

$ sudo yum update

# CodeDeployAgentはRubyで動作するので、Rubyをインストール
$ sudo yum install ruby

$ sudo yum install wget
$ cd /home/ec2-user

# このURLは東京リージョンのCodeDeployリソースキットファイルが置いてある場所です
$ wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install

$ chmod +x ./install
$ sudo ./install auto

下のコマンドを叩いてCodeDeployAgentが起動していればOKです。

$ sudo systemctl status codedeploy-agent

CodeDeployのデプロイアプリケーション作成・設定

デプロイアプリケーション作成

  1. CodeDeploy コンソール (https://console.aws.amazon.com/codedeploy) を開きます。
  2. ナビゲーションペインから[アプリケーション]を選択し、[アプリケーションの作成]を選択します。
  3. [アプリケーション名]を入力し、[コンピューティングプラットフォーム]で[EC2/オンプレミス]を選択します。
  4. [アプリケーションの作成]を選択します。

デプロイグループの作成

  1. 作成したデプロイアプリケーションのページの[デプロイグループ]タブを選択し、[デプロイグループの作成]を選択します。
  2. 必要項目を入力します。
    • デプロイグループ名 : 任意の名前(例:SampleAppDeploymentGroup)
    • サービスロール : 先に作成したAWSCodeDeployRole
    • デプロイタイプ : インプレース
    • 環境設定 : Amazon EC2 インスタンス(タググループでEC2インスタンスに使用したタグのキーと値を指定)
    • デプロイ設定 : CodeDeployDefault.OneAtATime
    • ロードバランサー : [ロードバランシングを有効にする]チェックボックスを外す
    • 詳細-オプション : 任意で自動ロールバックの設定、アラームの設定が可能
  3. [デプロイグループの作成]を選択します。

デプロイタイプとデプロイ設定の選択肢と内容は下の通りです。

デプロイタイプ

インプレース Blue/Green
既存EC2インスタンスのアプリケーションを上書きする。 新規のEC2インスタンスを作成し、そちらにアプリケーションをデプロイする。アプリケーションが稼働したらロードバランサーのルーティングを新環境向けに切り替える。

デプロイ設定

OneAtATime HalfAtATime AllAtOnce
1度に可能な限り多くのインスタンスに対してデプロイを実施します。 1度に全体の半数のインスタンスに対してデプロイを実施します。 1度に1つのインスタンスに対してデプロイを実施します。

AppSpec file作成

AppSpec fileはデプロイの内容を定義するファイルです。
アプリケーションの配置先、パーミッション、デプロイのライフサイクルイベントで実行する処理を定義できます。
EC2へのインプレースデプロイで使用可能なライフサイクルイベントは5つあります。

  • ApplicationStop
    • アプリケーションの停止などで使用する。
  • BeforeInstall
    • ファイルの暗号化、現在のバージョンのバックアップ作成などで使用する。
  • AfterInstall
    • アプリケーションの設定、ファイルの許可の変更などに使用する。
  • ApplicationStart
    • アプリケーションの起動などで使用する。
  • ValidateService
    • デプロイが正常に完了したことの検証に使用する。

ApplicationStopイベントは注意が必要で、新しくデプロイするアプリケーションのAppSpec fileではなく、最後に正常にデプロイされたアプリケーションのAppSpec fileの内容で実施されます。従って初回デプロイ時には何も実行されません。

appspec.ymlを作成

appspec.ymlをアプリケーションソースコードのルートに作成し、デプロイの定義を書いてきます。
Railsアプリケーションのappspec.ymlの内容はこちらの記事が参考になります。

appcpec.yml
version: 0.0  # 0.0固定
os: linux     # デプロイ先サーバーのOS
files:        # アプリケーションの配置場所
  - source: /
    destination: /var/www/sample_app/current
permissions:  # 配置ディレクトリのパーミッション
  - object: /var/www/sample_app/current
    owner: {ユーザー名}
    group: {ユーザーグループ名}
    pattern: "**"
    mode: 775
    type:
      - file
      - directory
hooks:        # デプロイのライフサイクルイベント
  ApplicationStop:
    - location: deployment_scripts/stop_application.sh
      runas: {シェルスクリプトの実行ユーザー名}
  AfterInstall:
    - location: deployment_scripts/install_gems.sh
      runas: {シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/compile_assets.sh
      runas: {シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/run_db_migrations.sh
      runas: {シェルスクリプトの実行ユーザー名}
  ApplicationStart:
    - location: deployment_scripts/start_application.sh
      runas: {シェルスクリプトの実行ユーザー名}

デプロイタスクを作成

アプリケーションルートにdeployment_scriptsディレクトリを作成し、デプロイタスクのスクリプトを配置します。
デプロイタスクは任意のスクリプト言語で記述可能です。

deployment_scripts/stop_application.sh
#!/bin/bash

source /home/{ユーザー名}/.bash_profile

cd /var/www/sample_app/current

# Pumaを停止
RAILS_ENV=production bundle exec pumactl -F config/puma.rb stop

まとめ

これでCodeDeployを使用したデプロイの準備ができました。
今のままでもAWS CLIを使ってリビジョンをS3にアップロード → デプロイ実行を行うことはできますが、
デプロイの度に毎回手動でコマンドを叩くのは面倒です。

次回で、CodePipelineを使用してCodeCommitのリポジトリが変更されたタイミングでデプロイを自動実行する仕組みを作っていきます。

参考記事

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

`create_or_find_by` はユニーク制約が定義されたテーブルで効いてくる

以下の記事で find_or_create_bycreate_or_find_by の違いについてコメントしたのですが、投稿元の方から反応が無かったので別記事として投稿することにしました :pray:

「find_or_create_by」と「create_or_find_by」 の違い #Railsのコードを読んでみた - Qiita


find_or_create_by でできないカラムもcreate_or_find_byではできてしまう可能性があります。

例えば、Userモデルがあったとします。create_or_find_by ではcreateが先に実行されるため2つ同じようなデータが作られます。
find_or_create_byでは、findが先に実行されるため、検索結果が返され、同じデータは2つ作られることはありません。

意識して使用しないとそのような動作になってしまうのですが、恐らく create_or_find_by のユースケースは DB で unique 制約が定義されたカラムに対しての実行だと思います。

See also: 新メソッド: #create_or_find_by

find_or_create_by の場合

正常系

  1. find_by を実行し、レコードが取得できなかった
  2. create を実行し、レコードが作成される

異常系

処理 A と B が同時に実行されている場合

  1. A: find_by を実行し、レコードが取得できなかった
  2. B: A と同じ条件で find_or_create_by を実行。find_by でレコードが取得できなかった
  3. A: create を実行し、レコードが作成される
  4. B: create を実行し、レコードが作成される (実際には unique 制約でエラーになるはず)

同じレコードが 2 つ出来てしまった! (実際には unique 制約でエラーになるはず)

find_or_create_byfindcreate の間に別のプロセスの create が割り込む可能性があります。

create_or_find_by の場合

処理 A と B が同時に実行されている場合

  1. A: create を実行し、レコードが作成される
  2. B: A と同じ条件で create_or_find_by を実行。 create で DB の unique によりレコードの作成に失敗
  3. B: find_by を実行し、レコードが取得される

▶ 別のプロセスが同時に create_or_find_by を実行しても安全に処理される

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

[rails] 投稿一覧にユーザー情報を表示

始めに

今回、プロゲートと違う方法で投稿一覧でユーザー情報を表示します。

プロゲートでrailsを終えてからよく、ツイッター風アプリを作成すると思います。
そこで、プロゲートのコードをコピペすることは誰でも出来ますが、
重要なのは理解して自分でコードを書くことだと思います。

他のやり方で表示することが出来たので、参考やヒントになれば嬉しいです。

完成イメージ

スクリーンショット 2020-01-21 17.00.44.png
※注:デザインは今回しません。また、分かりやすく理解してもらうためにユーザー情報は名前だけにします。

前提

・usersテーブルと投稿用のテーブル(今回はcommentsテーブル)があること。

MVC(model/controller/view)の設定

controller設定

app/controllers/comments_controller.rb
class CommentsController < ApplicationController
    def index
      @comment = Comment.all.order(created_at: :desc)
    end

これはプロゲートでもでてきた投稿された順に上から表示しています。

modelの設定(※ここからプロゲートと違う!)

app/models/comment.rb
class Comment < ApplicationRecord
    belongs_to :user#追記
end

belongs_toとはなんぞや?って思った方いるかも知れません。
簡単に説明すると、userテーブルと投稿用のテーブル(今回はcommentsテーブル)の2つを紐付けてくれる仕組みのものです。
詳細はこちら

view設定

app/views/comments/index.html.erb
<% @comment.each do |comment|%>
  <%= comment.user.name%>
  <%= link_to(comment.content,"/comments/#{comment.id}")%>
<% end %>

上記のコードの2行目の <%= comment.user.name%>は
先程belongs_to :userと紐付けているためこのように書くことが出来ます。
もし、紐付けが出来ていなかったらエラーが出ると思います。

最後に

今回、belongs_to を使ってuserテーブルとcommentテーブルをアソシエーションしました。

もし、何か修正点とかございましたらコメント等
恐縮ですが、宜しくおねがいします

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

【Rails】enumを使用したセレクトボックスの実装とDBへの保存

はじめに

都道府県などのプルダウンメニューを作成する際、enumを使いました
これを使うことで都道府県などのデータをわざわざテーブルとして用意する必要がなくなります
便利だったのですが、DBへの保存の際に型が合わないなどの問題も発生したのでまとめておきます

環境

Ruby: 2.5.1
Rails: 5.2.4

実際の操作

実際の操作は以下のようになります

  1. 該当カラムをinteger型で作成
  2. モデルにプルダウンメニューで表示させるもの一覧を記載
  3. ビューで表示させるプルダウンの記述
  4. 入力したフォームの受け取り型を編集

1. 該当カラムをinteger型で作成

下記の場合は都道府県です

schema.rb
t.integer :prefecture, null: false


2. モデルにプルダウンメニューで表示させるもの一覧を記載

models/address.rb
enum prefecture: {
    北海道:1,青森県:2,岩手県:3,宮城県:4,秋田県:5,山形県:6,福島県:7,
    茨城県:8,栃木県:9,群馬県:10,埼玉県:11,千葉県:12,東京都:13,神奈川県:14,
    新潟県:15,富山県:16,石川県:17,福井県:18,山梨県:19,長野県:20,
    岐阜県:21,静岡県:22,愛知県:23,三重県:24,
    滋賀県:25,京都府:26,大阪府:27,兵庫県:28,奈良県:29,和歌山県:30,
    鳥取県:31,島根県:32,岡山県:33,広島県:34,山口県:35,
    徳島県:36,香川県:37,愛媛県:38,高知県:39,
    福岡県:40,佐賀県:41,長崎県:42,熊本県:43,大分県:44,宮崎県:45,鹿児島県:46,沖縄県:47
  }



これで表示させる準備完了

3. ビューで表示させるプルダウンの記述

address.html.haml
= form.select :prefecture, Address.prefectures.keys, {prompt: "選択してください"}, class: "prefecture-select"



これでプルダウンメニューとして表示することはできるようになったんですが、binding.pryでparamsの中を見てみると"北海道"として値を取得してきてしまいました。
スクリーンショット 2020-01-20 21.34.13.png

prefectureカラムはinteger型なのでこのままだとDBに登録できなくて困ります。

4. 入力したフォームの受け取り型を編集

色々調べた結果、以下のようにすると対応するvalueの値を取得できることがわかりました。

address.html.haml
= form.select :prefecture, options_for_select(Address.prefectures), {prompt: "選択してください"}, class: "prefecture-select"

スクリーンショット 2020-01-20 21.32.58.png

しかし、これだと"1"という文字列なのでまだカラムに登録できません。

form.selectにおける入力値を数値として送れないかと思いましたが、やり方がわからなかったので、受け取る側で無理矢理数値型に変換することにしました。少々かっこ悪いですが、ストロングパラメータで数値型にした後にmergeしてます。

registrations_controller.rb
def address_params
  params.required(:address).permit(:postal_code, :city, :street, :building).merge(prefecture: params[:address][:prefecture].to_i)
end



これで晴れてDBへ保存できるようになりました

ただもっとスマートな方法がありそうな気がしてます

何か知ってる方いらっしゃいましたら教えていただけると嬉しいです

似たようなものにactive_hashというものがあります
gemをインストールする必要がありますが、こちらは数値型としてデータを取得できますし、ActiveRecordのメソッドも使えるみたいなので便利そうです

参考記事

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

Rails 6+Grapeで作るAPIサーバーにDeviseトークン認証を付ける

はじめに

iOS・AndroidアプリやWebアプリをクライアントとしたAPIサーバーをRuby on Railsで実装するケースがあります。この記事では、Grape(REST APIフレームワーク)を利用して作るAPIサーバーにDevise(認証機能を提供するgem)を組み合わせ、アクセストークンを介した認証方式を実装する手順を紹介します。

同様の紹介事例について

Devise + Grape で認証付きAPIの実装 - Qiita

上記の記事では本記事と同様に、GrapeとDeviseに加えてトークン認証機能を付加するgemであるdevise_token_authを用いて実装する手法が紹介されています。ただし、

Token の更新に関してですが、今回の例では一度発行した Token は再度ログインするまで更新されません。

とあるようにdevise_token_authの本来の機能であるリクエストごとにアクセストークンを更新する仕様は有効になっていません。ここでは、上記記事のコードをベースに、Grapeとdevise_token_authの仲立ちをするgrape_devise_token_authを加えることでその点をクリアし、現行のRails 6で動作するサンプルを作ります。

前提条件

以降の手順解説では、Ruby 2.6.5とRails 6.0.2.1を使用することを想定しています。

Railsアプリを作成する

# rails new token-auth-sample

Railsアプリを新規作成して、以下のGemを追加します。

Gemfile.rb
gem 'devise'
gem 'devise_token_auth'
gem 'grape'
gem 'grape_devise_token_auth'
$ bundle install

Devise関連の環境設定を行う

# rails g devise:install
# rails g devise_token_auth:install User auth

新たに作成されたUserモデルのマイグレーションを行います。

# rails db:migrate

基本部分のコード修正

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include DeviseTokenAuth::Concerns::SetUserByToken

  protect_from_forgery with: :null_session
end

protect_from_forgeryは一般的なRailsアプリにおけるCSRF対策の仕組みに関する設定で、指定しない場合はPOSTリクエストを受け付けた際にActionController::InvalidAuthenticityTokenエラーとなります。これを無効化するには protect_from_forgery with: :null_session とします。

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

Userモデルでは、devise メソッドで有効化する機能が列挙されていますが、 :trackable が含まれていたら外しておきましょう。前述の rails g devise_token_auth:install User auth コマンドで作成されたマイグレーションでtrackable用のカラムが作成されておらず、サインインリクエストの際に NoMethodError (undefined method `current_sign_in_at' 〜 が発生するためです。(末尾の参考情報を参照)

config/routes.rb
Rails.application.routes.draw do
  devise_for :users

  namespace :api do
    mount_devise_token_auth_for 'User', at: '/v1/auth'

    mount Api::Root => '/'
  end
end

APIエンドポイント部分のコード追加

app/api/api.rb
module Api
  class Root < Grape::API
    GrapeDeviseTokenAuth.setup! do |config|
      config.authenticate_all = true
    end

    mount V1::Root
  end
end
api/v1/root.rb
module V1
  class Root < Grape::API
    version 'v1', using: :path
    format :json

    auth :grape_devise_token_auth, resource_class: :user

    helpers GrapeDeviseTokenAuth::AuthHelpers

    desc 'GET /api/v1/test'
    get 'test' do
      authenticate_user!

      {
        message: 'test',
        current_user_uid: current_user.uid,
        authenticated?: authenticated?,
      }
    end
  end
end

ここまでで、以下のエンドポイントが利用可能になっています。

  • ユーザーアカウント作成
    • POST http://localhost:3000/api/v1/auth
  • サインイン
    • POST http://localhost:3000/api/v1/auth/sign_in
  • サンプルGETリクエスト
    • GET http://localhost:3000/api/v1/test

動作確認

ローカルサーバーを起動しておきます。

# rails s
=> Booting Puma
=> Rails 6.0.2.1 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.6.5-p114), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop

サインアップ(ユーザーアカウント作成)

まず、新しいユーザーを作成します。

0584E428-FCF5-4FE1-BBE2-AE3C2770069C.png

cURLでのリクエスト例
# curl --request POST \
  --url http://localhost:3000/api/v1/auth \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data email=user01@example.com \
  --data password=user01 \
  --data password_confirmation=user01

サインアップリクエストのレスポンスヘッダーには、このユーザーの認証情報が含まれています。次のリクエストの際には認証情報として access-token, client, uid の値を指定します。

GETリクエスト

次に、作成されたユーザーでGETリクエストを行います。認証情報を収めた3つのリクエストヘッダーを付加します。

5B2BA75C-6C45-4C61-B669-364146A169CC.png

cURLでのリクエスト例
# curl --request GET \
  --url http://localhost:3000/api/v1/test \
  --header 'access-token: b638qOBLJ_sIEEJEiMC1ug' \
  --header 'client: 1XcTtaq2uA3DK0qvY6qM9Q' \
  --header 'uid: user01@example.com'

レスポンスヘッダーには access-token, client, uid が含まれており、 access-token の値が更新されています。 access-token はリクエストを行うたびに新しい値と置き換わることから、毎回異なるアクセストークンを用いてアクセスする仕様で動作していることが分かります。

おわりに

RailsとGrapeを使ったAPIプロジェクトで、Deviseの認証機能を用いてトークン認証を行う手順を紹介しました。devise_token_authのトークン更新機能が有効になっており、トークンの有効期間を絞れるという点で安全上望ましい仕様になっています。一方APIクライアントの方では、トークンの値が変更されるのに応じて書き換わったトークンを適切に更新管理することが大切になります。

参考情報

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

HerokuでRailsタスクを定期実行する方法(Google Apps Scriptのトリガーを使う方法)

HerokuにデプロイしたRailsアプリでタスクを定期実行する方法を紹介します。
ここで示す方法は、Google Apps Script (GAS)を使う方法です。

結論を先に述べると以下のようにします:
1. Railsタスクを作成しておく
2. GASでHerokuのAPI経由でコマンドをpostする関数を書く
3. GASでスケジューラーを作成する(トリガーを作る)

例として、Railsの場合を示していますが、GAS経由で「herokuのコマンドを定期実行する」という点がポイントなので、他の用途にも使えます。言語がRubyである必要性もありません。

この記事では、Railsの画像投稿アプリを例にとります。
スケジューラーを使って、24時間ごとにデータベースのテーブルのレコードを全て削除し、初期データの画像を投稿する方法を示します。

対象読者:プログラミング初心者向け

  • 私自身は転職活動中の未経験エンジニアです。内容には正確性を期しますが、間違いがあればご指摘くださると幸いです。
  • 初学者がHerokuを使っていて、「Herkouでデータベースを定期的にリセットしたいが調べても方法が分からない」と疑問を持った際に、当記事の内容を少しでも活用してもらえればと思って書きました。

経緯 (経緯に興味のない人は飛ばしてください)

HerokuにRailsアプリをデプロイしていて、一定時間ごとにデータベースを初期化するために今回の方法に行き着きました。

前提として、

このような理由で、画像投稿機能のあるwebアプリをHerokuにデプロイした場合、画像の保存場所をAWS S3などに指定することはよくあると思います。

しかし、私のようにポートフォリオとしてアプリを公開したいだけなら、24時間ごとに投稿画像が削除されても問題ないと考える人もいるでしょう。
一方、アプリの体裁を整えるために、幾つかの画像だけはアプリの再起動の度に投稿された状態にしたいこともあるでしょう。

今回の私がこのような状況でした。
これを解決するために以下のようにしました。

  • Railsのタスクを作成する。このタスクの実行でテーブルを空にしてから適当な画像を投稿する(このための数枚の画像は、git管理する)
  • Heroku APIを使い、httpリクエストでRailsタスクを実行する
  • GASでhttpリクエストを定期実行する

これで何とか解決したので、そのぞれの方法を説明します。

前提となるサンプリアプリの作成

画像投稿機能

例として、carrierwaveで画像を投稿するサンプルアプリを以下のように作ります。

rails new sample-app
rails db:create
rails generate scaffold picture image:string
rails db:migrate

画像投稿用のgemとしてcarrierwaveをインストールします。
gemfileに追記して

gem 'carrierwave'

としてからbundle installとします。
carrierwaveの設定のインストールのため、rails g uploader imageとし、carrierwaveのuploaderをモデルにマウントします。

app/models/picture.rb
class Picture < ApplicationRecord
  mount_uploader :image, ImageUploader
end

ファイルをアップロードできるようにerbファイルに以下を追記します。

app/views/pictures/_form.html.erb
<%= form.text_field :image %>

続いて、投稿した画像を表示できるように以下を追記します。

app/views/pictures/index.html.erb
<td><%= image_tag picture.image.to_s %></td>

最低限の内容ですが、これで画像投稿アプリができました。

画像投稿するためのRailsタスクを作成

rails g task initialize_dbとして、タスクのファイルを以下のように編集します。

lib/tasks/initialize_db.rake
namespace :initialize_db do
  desc "初期データとしてpicturesテーブルにデータを入れる"
  task add_initial_blog: :environment do
    Picture.destroy_all
    Picture.create!([
      {image: File.open("#{Rails.root}/public/samples/0apple.jpeg")},
      {image: File.open("#{Rails.root}/public/samples/0cake.jpeg")}
    ])
  end
end

初期データとして投稿するための画像データは、0apple.jpegと0cake.jpegです。
これを/public/samples/フォルダに配置します。
これらをgit管理することで、herokuの再起動時に削除されずに済みます。

これで、ターミナルでrails initialize_db:add_initial_blogとすれば、picturesテーブルのレコードを全て削除して、新規に2つの画像を投稿することができます。

ひとまず、ここまでの内容をHerokuにデプロイ済みだとします。
このタスクをスケジューラーで定期的に実行するのが今回の目的となります。

HerokuのコマンドをAPI経由で実行する

Herokuにアプリをデプロイする際や、ログを確認する際にはHeroku CLIを使って、コマンドを実行するのが一般的です。

しかし、コマンドの実行を自動化するにはAPIを使う方が(おそらく)便利です。
Heroku APIについては公式のリファレンスにまとまっています。

APIでone-off Dynoを作り、コマンドを実行

今回は、API経由でコマンドを実行する方法に着目します。
公式によれば以下のようにします: Platform API Reference | Heroku Dev Center

POST /apps/{app_id_or_name}/dynos

今回はherokuのCLIでheroku run rails initialize_db:add_initial_blogと打つのをAPIで代替したいので、curlで以下のようにします。

$ curl -n -X POST https://api.heroku.com/apps/$APP_ID_OR_NAME/dynos \
  -d '{
  "command": "heroku run rails initialize_db:add_initial_blog",
}' \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.heroku+json; version=3"
  -H "Authorization: Bearer $HEROKU_API_KEY"

$APP_ID_OR_NAME$HEROKU_API_KEYについては個々のアプリに応じて読み替えてください。

Herokuの$APP_ID_OR_NAME$HEROKU_API_KEYの確認方法

$APP_ID_OR_NAMEを確認するにはCLIでheroku appsと打ちます。
もしくは、アプリの公開URLがhttps://<$APP_ID_OR_NAME>.herokuapp.com/のような形式になっているので、そこから読み取ることもできます。

$HEROKU_API_KEYを確認するには、heroku auth:tokenとCLIで打ちます。このトークンは1年しか有効ではないので、半永久的に使いたい場合には、heroku authorizations:createとしてトークンを生成してください:Getting Started with the Platform API | Heroku Dev Center

GASでスケジューラーを作成

他のスケジューラーではダメなのか

herokuで特定の処理を一定時間ごとに実行する代表的な方法はHeroku Schedulerというアドオンを使うものです。

しかし、Heroku Schedulerの使用にはクレジットカードの登録が必要であり、無料Dynoの枠を超えると課金される可能性もあるので、完全に無料の方法として、GASを使うことにしました。

他の方法としてClock ProcessesというHerokuの機能を活用する手もあります: Scheduled Jobs and Custom Clock Processes | Heroku Dev Center。Clock Processesはクレジットカード登録なしで使用できますが、追加のdynoを使うため、今回は見送りました。

GASなら無料でスクリプトの定期実行ができる

Googleドライブで使用できるGoogle Apps Scriptは、無料でスクリプトを定期実行できます。
実際には実行回数に制限がありますが、無料プランでも十分な回数です:Quotas for Google Services  |  Apps Script  |  Google Developers
例えば、今回利用するURL Fetch callsでは一日あたり20000回使用できます。

GASの環境設定

GASの実行環境を整えます。googleのアカウントを持っていれば、1分で完了します。
googleドライブで「Google Apps Script」というアプリを選択するだけです。

Google Apps Script

初回はメニューに登録されていないので、「アプリを追加」からGoogle Apps Scriptを検索して、アプリを追加してください。

GASで定期実行するスクリプト

GASは、一部独自機能があるものの、文法はJavaScriptに準拠しています。

heroku APIをcurlで使用するためのコマンドをGASのコードに読み替えます。
結果だけ示すと、以下のようにrunHerokuTask関数を定義し、この関数を定期実行すればうまくいきます。

function runHerokuTask() {
  runHerokuCommand("heroku run rails initialize_db:add_initial_blog");
}

function runHerokuCommand(command) {
  var urlTemplate = "https://api.heroku.com/apps/%s/dynos";
  var herokuToken = PropertiesService.getScriptProperties().getProperty("heroku_token");
  var herokuAppName = PropertiesService.getScriptProperties().getProperty("heroku_app_name"); 
  var herokuAuth = Utilities.formatString("Bearer %s", herokuToken);
  var url = Utilities.formatString(urlTemplate, herokuAppName);

  var headers = {
    "Content-type": "application/json",
    "Accept": "application/vnd.heroku+json; version=3",
    "Authorization": herokuAuth
  }

  var data = {
    "command": command
  }

  var options = {
    "method": "post",
    "payload": JSON.stringify(data),
    "headers": headers
  };

  var response = UrlFetchApp.fetch(url, options);
  var text = response.getContentText();
  Logger.log(text);
}

このコードでのポイントを幾つか説明します。

  • トークンやアプリ名などは、プロパティから取り出す
  • UrlFetchApp.fetchでhttpリクエストをする

GASのコードをgithubで公開する場合、パスワードや環境依存の定数はコードに含めたくはありません。
そのため、GASの機能のプロパティサービスを活用します。

プロジェクトのプロパティというところからスクリプトのプロパティで「キーと値のペア」を登録します。
プロジェクトのプロパティ

PropertiesService.getScriptProperties().getProperty("キーの名前")として、キーに紐付いた値を取り出します。

またhttpリクエストには、UrlFetchAppを使います。
今回は、POSTメソッドなので、オプションが必要ですが、getメソッドであれば、UrlFetchApp.fetch("http://www.google.com/")とするだけでwebサイトにアクセスできます。

GASのスクリプトの定期実行

GASのスクリプトの定期実行

GASのスクリプトの定期実行には、トリガーを登録します。

トリガーを登録

これで、毎日、0~1時ごろに登録した関数が自動実行されます。

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

【Rails】ActiveRecordで関連テーブルのカラムでwhereまたは、orderする【ActiveRecord】

はじめに

表題の通り、ActiveRecordで関連テーブルのカラムでwhereまたは、orderする方法について書いていきます。

前提

UserモデルとPostモデルが1:Nで関連づけられており、さらにPostモデルとCommentモデルが1:Nで関連づけられている場合を前提とします。
基本的な書き方は変わらないので、適宜ご自身の状況に置き換えて使用してください。

コード

# eager_loadでPostsにCommentsをjoinする
posts = User.posts.eager_load(:comments)

# whereで絞り込み
posts.where('comments.body = ?', 'テスト')

# orderで並び替え
posts.order('comments.created_at desc')

説明

まずはwhereやorderで使用したい関連テーブルをjoinします。
その後、
where('関連テーブル名.カラム名' 条件)
order('関連テーブル名.カラム名' desc or asc)
で絞り込み、または並び替えを行います。

おわりに

今回はActiveRecordの書き方のみを書きましたが、実際にどんなクエリが発行されているのかも意識しておくといいかもしれません。

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

RailsのDB操作を行うコマンドまとめ

はじめに

DB操作を行うコマンドというのはrails db:migrateなどのことです。
コマンドは数が多く、名前から想像しにくい(自分だけ?)ものもあったりするため、備忘録としてまとめておこうかと思います。

最初はスカスカですが、少しずつ充実させていく予定です。
※見栄えも少しずつ整えていきます。。。

■参考サイト(公式GitHub)
https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/railties/databases.rake

環境

  • Rails 5.2.0

コマンド

コマンド 説明 備考
rails db:create データベースを作成
rails db:drop データベースを削除
rails db:migrate マイグレートを実行
rails db:migrate:reset データベースを作成し直した後にマイグレートを実行
rails db:reset DB再作成後、マイグレートを実行し、seedファイルからデータ作成 db:drop後にdb:setupを行っている。
rails db:setup DB作成後、マイグレートを実行し、seedファイルからデータ作成 マイグレーションファイルは使用せず、schema.rbからマイグレートを行う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

既存のRailsアプリにVue.jsを導入する

環境

ruby 2.6.0
Rails 5.2.4.
Vue 2.6.11

gem 'webpacker' のインストール

Gemfile
gem 'webpacker', github: 'rails/webpacker'
bundle

yarn のインストール

$ brew install yarn

$ yarn -v

webpacker のインストール

$ bin/rails webpacker:install
$ bin/webpack

の順に入力。
webpack 関連のファイルが作成・更新される。

Vue.js のインストール

$ rails webpacker:install:vue

このコマンドを実行することで、app/javascript/packs 配下にファイルが作成される。

これでvueの導入は完了。

動作確認

viewの操作をしてみる

app/javascript/packs/hello_vue.js
import Vue from 'vue/dist/vue.esm' #htmlを操作する場合は、 vue/dist/vue.esm をimport

const app = new Vue({
    el: '#hello',
    data: {
      message: "Test text"
    }
})
任意のview
<div id="hello">
  {{ message }}
</div>

<%= javascript_pack_tag 'hello_vue' %>

Test textと表示されればOK。

<%= javascript_pack_tag 'hello_vue' %>でjsファイルの読み込みを行う。

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

Cloud9でRails 6.0のSeleniumとGoogle Chromeを使える設定しましょう!

みなさん、お疲れ様です!

Cloud9で元気いっぱいで新しいRails6アプリを作ろうと思っている方、
必ずテストも作りたいよね!

もうグーグルクロームをインストールしていますよね? このバッシュスクリプトはめっちゃ便利です。

$ curl https://intoli.com/install-google-chrome.sh | bash

そしてSeleniumのシステムテストを稼働すると、このエラーはありますでしょうか?

$ rails test:system
Run options: --seed 47710
# Running:
E
Error:
WelcomeTest#test_visiting_the_index:
Selenium::WebDriver::Error::UnknownError: unknown error: Chrome failed to start: exited abnormally
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /opt/google/chrome/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
    test/system/welcomes_test.rb:5:in `block in <class:WelcomeTest>'

気を落ち込まないように!

  • グーグルのバージョンを確認してください
$ google-chrome-stable -version
Google Chrome 79.0.3945.130 
  • 正しいchrome-webdriverをダウンロードして、インストールします。
    https://chromedriver.chromium.org/downloads
    で自分のバージョンをダウンロードしてください。例えば今の場合、79が必要ね
$ wget -N https://chromedriver.storage.googleapis.com/79.0.3945.36/chromedriver_linux64.zip
$ unzip chromedriver_linux64.zip
$ sudo mv -f chromedriver /usr/local/bin/chromedriver
$ sudo chown root:root /usr/local/bin/chromedriver
$ sudo chmod 0755 /usr/local/bin/chromedriver
$ chromedriver --version
ChromeDriver 79.0.3945.36 (3582db32b33893869b8c1339e8f4d9ed1816f143-refs/branch-heads/3945@{#614}) // OK!!!!!!
  • Railsのシステムテスト用のオプションを設定します
/test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, 
                       screen_size: [1400, 1400], 
                       options: { args: %w[headless disable-gpu] }
end

  • 確認!
$ rails test:system

Run options: --seed 49276

# Running:

2020-01-21 01:54:40 WARN Selenium [DEPRECATION] :args or :switches is deprecated. Use Selenium::WebDriver::Chrome::Options#add_argument instead.
Capybara starting Puma...
* Version 4.3.1 , codename: Mysterious Traveller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:41043
.

Finished in 1.324751s, 0.7549 runs/s, 0.7549 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

まあ後でdeprecationsの問題に考えましょうね、
とりあえず稼働かどうか確認してください!

いかがでしたか?問題なかった?コメント、煽りはコメントに書いてください!

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

WebMockを使って外部API接続をスタブ化する

概要

APIはJavaで実装して、Javaから返ってきたjsonをRailsで受け取ってRails側でそのjsonデータを表示するという流れにしたく、Railsの実装をしている時のテストや、いちいちJavaを起動させなくていいようにスタブ化したのが目的です。

Rails開発でWebMockを使ってAPIアクセスをスタブ化するからのコードを引っ張ってきただけなので、リンク先の解説みた方が断然わかりやすいです。

実際のコード

api_stub.rb
module ApiStub
  require 'webmock'

  WebMock.enable!
  # アプリケーションコードに`call_get_api`という名称の外部API呼び出し実装を含むメソッドがあることして、ここで同名のメソッドを作成してstub登録します。
  def call_get_api
    WebMock.stub_request(:any, "http://www.example.com/").to_return(
        body: File.read("#{Rails.root}/test/fixtures/stub_api_response.json"),
        status: 200,
        headers: { 'Content-Type' =>  'application/json' })
    super # アプリケーションコードの実装を呼び出す
  end
end
api_module.rb
module ApiModule
  require_relative 'api_stub'
  prepend ApiStub if ENV["APIMODE"] == "mock" && Rails.env.development?

  def call_get_api
    # ここに本来の外部API呼び出しの実装が入る
  end
end
api_client.rb
class ApiClient
  require_relative 'api_module'
  include ApiModule
end
web_controller.rb
class WebController < ApplicationController

  require './lib/externals/api_client'
  require 'net/http'

  def fetch
    client = ApiClient.new
    client.call_get_api
    res = Net::HTTP.get("www.example.com", "/")
    @result = JSON.parse(res)
  end
end

脱線 スタブとモックの違い

スタブは受信メッセージのテストのために使うためのもの。
モックは送信メッセージのテストのために使うもの。

参照

Rails開発でWebMockを使ってAPIアクセスをスタブ化する
Ruby on RailsでWebMockを利用する

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

Rails 6ではsend_data/send_fileメソッド呼び出し時にERB::Util.url_encodeは不要です

Rails 6にアップグレードすると発生する問題

Railsでファイルダウンロード機能を実装するときはsend_dataメソッドやsend_fileメソッドがよく使われます。
このとき、ファイル名に日本語(正確には非ascii文字)が含まれていると、IEやEdgeで文字化けします。
そのため、Rails 5.2以前では以下のようにERB::Util.url_encodeメソッドを使って文字化けを防いでいました。

class FooController < ApplicationController
  # ...

  def download
    # ...

    # Rails 5.2以前ではurl_encodeをかけないと、IEやEdgeでダウンロードしたときに
    # `縺ォ縺サ繧薙#.txt`のようなファイル名になってしまっていた
    send_data(your_data, filename: ERB::Util.url_encode('にほんご.txt'), disposition: 'attachment')
  end
end

しかし、RailsアプリケーションをこのままRails 6にアップグレードすると、再びファイル名が文字化けします。
(IEやEdgeのみならず、Chrome等でも文字化けします)

# Rails 5.2でダウンロードしたときのファイル名
にほんご.txt

# Rails 6.0でダウンロードしたときのファイル名
%E3%81%AB%E3%81%BB%E3%82%93%E3%81%94.txt

原因

これはRails 6でファイル名がデフォルトでエンコードされるようになったのが原因です。

解決策

Rails 6ではデフォルトでエンコードされるので、ERB::Util.url_encodeを挟まずにsend_dataメソッドやsend_fileメソッドを呼び出してください。

-send_data(your_data, filename: ERB::Util.url_encode('にほんご.txt'), disposition: 'attachment')
+send_data(your_data, filename: 'にほんご.txt', disposition: 'attachment')

こうすれば、どのブラウザでも文字化けせずに日本語のファイル名でダウンロードすることができます。

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

カラムを追加してpgからschemaにdumpさせてみました

記録用

railsでgenerate migrationコマンドを使って、自動的に作成していましたが、その方法で作成しない方針だったため、migrationファイルからDBにカラムを追加したり、コメントを入れたり、schemaディレクトリにバックアップファイルを作成してその中にdumpさせました。

完全に理解していないですが、記録用と手順ややったことを忘れないためにも残しておきます。

migretionファイルでカラムの追加

カラムの追加

ALTER TABLE テーブル名 ADD COLUMN カラム名 データ型 型名  ;
COMMENT ON TABLE テーブル名 IS 'コメント';

SQLで確認

\d (テーブル名)
\d+ でコメントも確認することができる

列 | 型 | 修飾語

↑のような形式で追加したカラムを確認することができる

schemaファイルにdumpするとき

データベースを指定してダンプする場合は、pg_dumpを使用する

pg_dump DB名 --schema-only -U username -h host -p port -t  'table' > file(バックアップファイル).sql

dumpのオプション内容

-- schema-only
データ定義(スキーマ)のみをダンプし、データはダンプしません。

-U username
指定したユーザとして接続します。

-h
000.000.000.000

-p
ポート番号

-t table
tableに一致するテーブル(またはビューやシーケンス)のみをダンプします。

table > file.sql
dump先を指定

このようにオプションで絞っていきました。

schemaファイルにdumpできていることを確認することができました。
次回からこの手順で作成していきます。

参考文献

https://www.postgresql.jp/document/9.4/html/ddl-alter.html

https://www.postgresql.jp/document/8.2/html/app-pgdump.html

https://www.postgresql.jp/document/11/html/app-pgdump.html

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

【Rails】 DataTables 動的にカラムを変更する方法

はじめに

Railsアプリケーションで DataTables を使う方法を以前にまとめさせていただきました。
DataTables を使ったテーブルのカラムを動的に変更したい需要があると思いますが、まとめられている記事を見かけませんでしたので、こちらにてまとめさせていただきます。
この方法を理解していれば、開発時間を極端に減らして高機能なテーブルを提供することができます。

なお、今回の方法は少し強引にカラムを動的に変更しています。
もし、もっといい方法があるということをご存知の方はコメントをいただければ嬉しいです。

動的にカラムを変更する方法

前提条件

下記リンクにてDataTablesを実装していること。
このリンク先のコードを元にして、動的にカラムを変更する方法をこちらの記事にて特記したいと思います。

値を変更する方法

動的にカラムを変更するには、users.coffeeのコードを変更することによって可能です。

ここでは一例として、id が 3 のときに"あほ"を表示するコードを書いています。

app/assets/javascripts/users.coffee
$ ->
  # *** 省略 ***

  # user_table へカラムを追加する
  user_table.setColumns([

    # 以下に注目
    { 
      data: 'id',         title: 'ユーザID', width: '5%'
      # 以下を追記
      render: (data, type, row) ->
        # data / type / row にどんなデータが入っているか確認。
        console.log data
        console.log type
        console.log row

        if data == "3"
          "あほ"
    },
    { data: 'username',   title: 'ユーザ名', width: '25%' },
    { data: 'name',       title: '名前',    width: '30%' },
    { data: 'created_at', title: '登録日時', width: '20%' },
    { data: 'updated_at', title: '更新日時', width: '20%' },
  ])
  # *** 省略 ***

フォントを変更する方法

つづいて、フォントを変更する方法を紹介します。

一例として、id が 3 のときに赤色の文字で"あほ"と表示するコードを書いています。
htmlタグを使ってそこにcssを埋め込んでいるだけとなります。(少し強引かもしれません。。)

app/assets/javascripts/users.coffee
$ ->
  # *** 省略 ***
    # 以下に注目
    { 
      data: 'id',         title: 'ユーザID', width: '5%'
      # 以下を追記
      render: (data, type, row) ->
        if data == "3"
          "<span style='color: red;'>あほ</span>"
    },
  # *** 省略 ***

Bootstrap のレイアウトを導入する

最後に応用技として、 Bootstrap のレイアウトを導入する方法を紹介します。

一例として、id が 3 のときに赤色の文字で"あほ"と表示するコードを書いています。
応用と書きましたが、htmlタグを使ってbootstrapで使用できるclassを付与しているだけとなります。

app/assets/javascripts/users.coffee
$ ->
  # *** 省略 ***
    # 以下に注目
    { 
      data: 'id',         title: 'ユーザID', width: '5%'
      # 以下を追記
      render: (data, type, row) ->
        if data == "3"
          "<div><center><span class='label label-default'>あほ</span></center></div>"
    },
  # *** 省略 ***

まとめ

いかがでしょうか。Railsなので動的に値を変更したい需要はかなりあるかと思いますが、DataTables だとドキュメントが英語で読みづらいし、あまり柔軟性がないと考える方もいると思います。
少し強引ではありますが、このような感じで色々な応用をすることも可能ですので、ご自身で色々と試してみるのもいいかもしれません。

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

[bundler: command not found: unicorn_rails Install missing gem executables with `bundle install]AWSのEC2サーバ上でRailsに追加したgemfileが反映されない時の解決例

<エラー文>
スクリーンショット 2020-01-21 7.03.08.png

このエラーの原因で考えられること

local環境でしかgemfileが反映されていないことが考えられます。
具体的には下記のことにより起こっていると考えられます。

1.rbenvのrehashし忘れ
2.bundlerの入れ忘れ
3.githubへの反映し忘れ(commitとpushのし忘れ)

解決方法

1.rbenvのrehashし忘れ

rbenv(rubyの環境構築)について更新をすれば解決します
EC2サーバー上で下記のコマンドを入力してください

[ec2-user@ip-xxx-xx-xx-xxx chat-space$ gem install bundler -v 2.0.2]$rbenv rehash

2.bundlerの入れ忘れ

EC2サーバー上にbundlerをインストールし、再度bundle installすることで解決します
バンドラーのバージョンはご自分のプロジェクトファイルのものと合わせるためローカルサーバー上でバージョンを調べたのち、EC2サーバー上で下記のコマンドを入力してください

Neverland:chat-space kontatomoya$ bundler -v
#プロジェクトのローカルディレクトリで bundler -v でbundleのバージョンを調べられます(筆者の場合は"Bundler version 2.0.2"が帰ってきたのでver2.0.2で以下を進めています)

[ec2-user@ip-xxx-xx-xx-xxx chat-space]$ gem install bundler -v 2.0.2
[ec2-user@ip-xxx-xx-xx-xxx chat-space$ gem install bundler -v 2.0.2]$ bundle install
#bundlerをインストールし、bundle installしてます

3.GitHubへの反映し忘れ(commitとpushのし忘れ)

EC2サーバーにプロジェクトを保存するには、GitHub上のデータを受け取りコピーという流れとなるため、GitHub上のデータに反映した後のデータをgit cloneしないとEC2サーバー上でも反映されません。そのため下記の手順に従って最新ファイルを反映させましょう。

①GitHub Desktopでcommitとpushし、GitHub上のデータに反映させましょう。

やり方はここでは説明しませんが簡単です。わからない人は下記などでやり方を確認してみましょう。(公式の使用方法解説ページにリンクしてます。)

https://help.github.com/ja/desktop/getting-started-with-github-desktop/creating-your-first-repository-using-github-desktop

②EC2サーバーのファイルに反映させましょう
今はEC2のプロジェクトが更新されていない状態なので、pullコマンドで更新を行いましょう

[ec2-user@ip-xxx-xx-xx-xxx chat-space$ gem install bundler -v 2.0.2]$ git pull origin master
#EC2は表示だけが必要となるため反映させるのはmasterとしましょう

③bundle installを行う
このプロジェクトのダウンロードができただけで、インストールはできていないので最後に忘れずbundle installしましょう

[ec2-user@ip-xxx-xx-xx-xxx chat-space$ gem install bundler -v 2.0.2]$ bundle install

これでこのエラーは解消されます。

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

[Ruby on Rails] resourcesの使い方について

resourcesに慣れないので理解することを目的に個人のメモとして残します

環境

OS:Windows10
Rubyバージョン:ruby 2.5.3p105 (2018-10-18 revision 65156) [x64-mingw32]
Railsバージョン:Rails 5.2.4.1

resourcesの使い方

routes.rb
Rails.application.routes.draw do

resources :tests

end

上記のようにroutes.rbでモデルを選んで宣言

resourcesの機能

  • 複数のCRUD処理に関わるルーティングを設定する(下記の表参照)
  • いくつかのURLと同値である簡潔な文字列が使用できる
URLと同値な文字列 httpメソッド URL コントローラー アクション
tests GET /tests tests index
tests POST /tests tests create
new_test GET /tests/new tests new
edit_test GET /tests/:id/edit tests edit
test GET /tests/:id tests show
test PATCH /tests/:id tests update
test PUT /tests/:id tests update
test DELETE /tests/:id tests destroy

同じ文字列でもメソッドが異なるため判別されると考えられる

RESTfulについて

勉強中。随時加筆

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

rails+froalaエディタ使用の注意

表示させる部分にはクラスに'fr-view fr-element'を付ける

froalaエディタは、とても高機能のエディタである。

ブログアプリで使用するとき、form画面ではとくに問題ないが、その内容を表示させるときに思うように表示されないときがある。
それは、form画面ではデフォルトで

<div class="fr-view fr-element">

で囲まれているが、表示させるときはない。
画像の左寄せ、中央寄せ、右寄せの機能がうまく表示されないのはこのせいである。
なので表示させたいページで同じようにdivで囲めばよい。

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

Rails6 アプリからメールを送信する デプロイ環境編

目的

  • Rails6で作成したアプリをデプロイした時に当該アプリからメールを送信する方法をまとめる。

前提条件

作業期待値

作業前にちょっと聞いてほしいこと

考え方

  • ローカル開発環境のメールサーバ設定はheroku環境だとそのまま流用できないので専用のメールサーバ設定が必要となる。
  • heroku上のアプリからメールを送るには専用の設定ファイルが必要と覚えておいてほしい。
  • デプロイ中は前述した専用の設定ファイルから自動で設定が読まれる。
  • 下記に前述の内容の簡易図を記載する。

スクリーンショット 2020-01-21 0.49.22.png

作業概要

  1. SendGridの有効化とAPI鍵作成
  2. デプロイアプリとAPI鍵の紐付け
  3. メール確認

作業詳細

  1. SendGridの有効化とAPI鍵作成

    1. 下記コマンドを実行してSendGridを有効化する。

      $ heroku addons:create sendgrid:starter
      
    2. herokuにログインする。

    3. デプロイしているアプリ名をクリックする。

    4. 「Add ons」にある「SendGrid」をクリックする。
      スクリーンショット 2020-01-20 23.15.14.png

    5. 下記画面になったら確認用のメールアドレスを記入しメールを送るボタンをクリックする。(本メールアドレスはアプリからの送受信には全く関係ない。あくまでSendGridの確認用のアドレスである。)
      スクリーンショット 2020-01-20 23.16.13.png

    6. 下記の画面になったことを確認し、先に入力したメールアドレスの受信ボックスを確認する。
      スクリーンショット 2020-01-20 23.16.18.png

    7. 受信ボックスに下記のようなメールが届いていることを確認して、「Confirm Email Address」をクリックする。
      スクリーンショット 2020-01-20 23.16.36.png

    8. 前述の作業を実施後下記の画面に遷移する。
      スクリーンショット 2020-01-20 23.17.48.png

    9. 画面左下のバーの「Settings」→「API Keys」をクリックする。
      スクリーンショット 2020-01-20 23.18.26.png

    10. 下記の画面になったことを確認し、画面右上の「Create API Key」をクリックする。
      スクリーンショット 2020-01-20 23.18.36.png

    11. 「API Key Name」に認識しやすい任意のAPI鍵の名前を入力、(自分がわかるならなんでもOK)「API Key Permissions」は「Full Accesss」を選択し内容を確認後「Create & View」をクリックする。
      スクリーンショット 2020-01-20 23.19.41.png

    12. 表示された鍵をスクリーンショットやテキストコピーでメモしておく
      スクリーンショット 2020-01-20 23.20.33.png

  2. デプロイアプリとAPI鍵の紐付け

    1. 下記コマンドを実行してAPI鍵をherokuの変数に格納する。(本作業で自動的にメールサーバ用のパスワードとユーザ名が自動生成される)

      $ heroku config:set SENDGRID_API_KEY=XXXXXXXXXXXXXXXXXXXX6NDlg.J1bMNO_jbeepuH8LOYFwkMKFcOKoQ5vZ8z9axkzMyWU
      
  3. メールサーバの設定

    1. 下記に存在する送信先を指定するファイルproduct.rbをエディタで開く
      • アプリ名フォルダ/config/environments
        • product.rb
    2. ファイルproduct.rbを下記のように修正してSentGridからメールを送信できるようにする。(下記のように設定すると、先の作業で登録したAPI鍵からuser_nameとpasswordが自動生成されそれぞれ環境変数SENDGRID_USERNAMEとSENDGRID_PASSWORDに格納され、それを読んでメールを送るようになる。)

      Rails.application.configure do
        # Settings specified here will take precedence over those in config/application.rb.
      
        # Code is not reloaded between requests.
        config.cache_classes = true
      
        # Eager load code on boot. This eager loads most of Rails and
        # your application in memory, allowing both threaded web servers
        # and those relying on copy on write to perform better.
        # Rake tasks automatically ignore this option for performance.
        config.eager_load = true
      
        # Full error reports are disabled and caching is turned on.
        config.consider_all_requests_local       = false
        config.action_controller.perform_caching = true
      
        # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
        # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
        # config.require_master_key = true
      
        # Disable serving static files from the `/public` folder by default since
        # Apache or NGINX already handles this.
        config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
      
        # Compress CSS using a preprocessor.
        # config.assets.css_compressor = :sass
      
        # Do not fallback to assets pipeline if a precompiled asset is missed.
        config.assets.compile = false
      
        # Enable serving of images, stylesheets, and JavaScripts from an asset server.
        # config.action_controller.asset_host = 'http://assets.example.com'
      
        # Specifies the header that your server uses for sending files.
        # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
        # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
      
        # Store uploaded files on the local file system (see config/storage.yml for options).
        config.active_storage.service = :local
      
        # Mount Action Cable outside main process or domain.
        # config.action_cable.mount_path = nil
        # config.action_cable.url = 'wss://example.com/cable'
        # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
      
        # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
        # config.force_ssl = true
      
        # Use the lowest log level to ensure availability of diagnostic information
        # when problems arise.
        config.log_level = :debug
      
        # Prepend all log lines with the following tags.
        config.log_tags = [ :request_id ]
      
        # Use a different cache store in production.
        # config.cache_store = :mem_cache_store
      
        # Use a real queuing backend for Active Job (and separate queues per environment).
        # config.active_job.queue_adapter     = :resque
        # config.active_job.queue_name_prefix = "eveyDayStudy_production"
        config.action_mailer.perform_caching = false
      
        # Ignore bad email addresses and do not raise email delivery errors.
        # Set this to true and configure the email server for immediate delivery to raise delivery errors.
        # config.action_mailer.raise_delivery_errors = false
      
        # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
        # the I18n.default_locale when a translation cannot be found).
        config.i18n.fallbacks = true
      
        # Send deprecation notices to registered listeners.
        config.active_support.deprecation = :notify
      
        # Use default logging formatter so that PID and timestamp are not suppressed.
        config.log_formatter = ::Logger::Formatter.new
      
        # Use a different logger for distributed setups.
        # require 'syslog/logger'
        # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
      
        if ENV["RAILS_LOG_TO_STDOUT"].present?
          logger           = ActiveSupport::Logger.new(STDOUT)
          logger.formatter = config.log_formatter
          config.logger    = ActiveSupport::TaggedLogging.new(logger)
        end
      
        # Do not dump schema after migrations.
        config.active_record.dump_schema_after_migration = false
      
        # Inserts middleware to perform automatic connection switching.
        # The `database_selector` hash is used to pass options to the DatabaseSelector
        # middleware. The `delay` is used to determine how long to wait after a write
        # to send a subsequent read to the primary.
        #
        # The `database_resolver` class is used by the middleware to determine which
        # database is appropriate to use based on the time delay.
        #
        # The `database_resolver_context` class is used by the middleware to set
        # timestamps for the last write to the primary. The resolver uses the context
        # class timestamps to determine how long to wait before reading from the
        # replica.
        #
        # By default Rails will store a last write timestamp in the session. The
        # DatabaseSelector middleware is designed as such you can define your own
        # strategy for connection switching and pass that into the middleware through
        # these configuration options.
        # config.active_record.database_selector = { delay: 2.seconds }
        # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
        # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
      end
      

      ↓修正

      Rails.application.configure do
        # Settings specified here will take precedence over those in config/application.rb.
      
        # Code is not reloaded between requests.
        config.cache_classes = true
      
        # Eager load code on boot. This eager loads most of Rails and
        # your application in memory, allowing both threaded web servers
        # and those relying on copy on write to perform better.
        # Rake tasks automatically ignore this option for performance.
        config.eager_load = true
      
        # Full error reports are disabled and caching is turned on.
        config.consider_all_requests_local       = false
        config.action_controller.perform_caching = true
      
        # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
        # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
        # config.require_master_key = true
      
        # Disable serving static files from the `/public` folder by default since
        # Apache or NGINX already handles this.
        config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
      
        # Compress CSS using a preprocessor.
        # config.assets.css_compressor = :sass
      
        # Do not fallback to assets pipeline if a precompiled asset is missed.
        config.assets.compile = false
      
        # Enable serving of images, stylesheets, and JavaScripts from an asset server.
        # config.action_controller.asset_host = 'http://assets.example.com'
      
        # Specifies the header that your server uses for sending files.
        # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
        # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
      
        # Store uploaded files on the local file system (see config/storage.yml for options).
        config.active_storage.service = :local
      
        # Mount Action Cable outside main process or domain.
        # config.action_cable.mount_path = nil
        # config.action_cable.url = 'wss://example.com/cable'
        # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
      
        # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
        # config.force_ssl = true
      
        # Use the lowest log level to ensure availability of diagnostic information
        # when problems arise.
        config.log_level = :debug
      
        # Prepend all log lines with the following tags.
        config.log_tags = [ :request_id ]
      
        # Use a different cache store in production.
        # config.cache_store = :mem_cache_store
      
        # Use a real queuing backend for Active Job (and separate queues per environment).
        # config.active_job.queue_adapter     = :resque
        # config.active_job.queue_name_prefix = "eveyDayStudy_production"
      
        config.action_mailer.perform_caching = false
        config.action_mailer.delivery_method = :smtp
        config.action_mailer.perform_deliveries = true
        config.action_mailer.smtp_settings = {
          :enable_starttls_auto => true
          :address => 'smtp.sendgrid.net',
          :port => 587,
          :domain => 'herokuapp.com',
          :authentication => :plain,
          :user_name => ENV['SENDGRID_USERNAME'],
          :password => ENV['SENDGRID_PASSWORD'],
        }
      
        # Ignore bad email addresses and do not raise email delivery errors.
        # Set this to true and configure the email server for immediate delivery to raise delivery errors.
        # config.action_mailer.raise_delivery_errors = false
      
        # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
        # the I18n.default_locale when a translation cannot be found).
        config.i18n.fallbacks = true
      
        # Send deprecation notices to registered listeners.
        config.active_support.deprecation = :notify
      
        # Use default logging formatter so that PID and timestamp are not suppressed.
        config.log_formatter = ::Logger::Formatter.new
      
        # Use a different logger for distributed setups.
        # require 'syslog/logger'
        # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
      
        if ENV["RAILS_LOG_TO_STDOUT"].present?
          logger           = ActiveSupport::Logger.new(STDOUT)
          logger.formatter = config.log_formatter
          config.logger    = ActiveSupport::TaggedLogging.new(logger)
        end
      
        # Do not dump schema after migrations.
        config.active_record.dump_schema_after_migration = false
      
        # Inserts middleware to perform automatic connection switching.
        # The `database_selector` hash is used to pass options to the DatabaseSelector
        # middleware. The `delay` is used to determine how long to wait after a write
        # to send a subsequent read to the primary.
        #
        # The `database_resolver` class is used by the middleware to determine which
        # database is appropriate to use based on the time delay.
        #
        # The `database_resolver_context` class is used by the middleware to set
        # timestamps for the last write to the primary. The resolver uses the context
        # class timestamps to determine how long to wait before reading from the
        # replica.
        #
        # By default Rails will store a last write timestamp in the session. The
        # DatabaseSelector middleware is designed as such you can define your own
        # strategy for connection switching and pass that into the middleware through
        # these configuration options.
        # config.active_record.database_selector = { delay: 2.seconds }
        # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
        # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
      end
      
  4. メール確認

    1. 下記のメール送信トリガーコードが記載されているコントローラが実行されるような操作を行う
      • NoticeMailer.greeting.deliver_now
    2. Rails6 アプリからGmailのメールサーバからメールを送信する ローカル環境編の対応で筆者の環境ではhttp://localhost:3000/posts/index/:idにアクセスした時にメールが送信されるようにトリガーコードを設置している。
    3. 筆者の場合herokuのパスに置き換えhttps://study-record.herokuapp.com/posts/index/:idにアクセスしエラーが出ないことを確認する。
    4. テストメール送信先アドレスを確認し下記のようなメールを受信していることを確認する。 スクリーンショット 2020-01-21 1.37.56.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails SQliteからmysqlへ変更(途中から)

アプリ作成後にmysqlに変更する方法

MySQLを起動して接続の確認

$ mysql.server start

起動後、接続できるか確認

mysql -u root -p

Railsで利用するユーザーを作成します。

create user ユーザー名@'localhost' identified by 'パスワード';
例 (create user railsuser@'localhost' identified by 'railspass';)

ユーザーが作成されたことが確認

select User,Host from mysql.user;

権限の付与を行います

grant all on *.* to ユーザー名@'localhost';

設定ファイルの変更

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: ユーザー名
  password: パスワード
  host: localhost

development:
  <<: *default
  database: sample_ec #データベース名

Gemfileに記述

gem 'mysql2'
$ bundle

mysqlにDBの追加とテーブル作成

$ bundle exec rake db:create
$ bundle exec rake db:migrate

これで完了

mysqlユーザーの削除方法

mysql> drop user ユーザー名@'localhost';

最初からmysqlを使用する場合

rails new アプリケーション名 -d mysql
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webサービスをインターネットに公開する(cloud9からHerokuにデプロイ)

Herokuについて

  • https://jp.heroku.com
  • Paas(Platform as s Service)と呼ばれるクラウドサービス
  • 元々、Rubyのアプリケーションをホスティングするサービス
  • PHP等、他の言語にも対応

デプロイの準備

設定箇所
1. Gemfile
2. config/database.yml
3. config/environments/production.rb
4. config/routes.rb

1.Gemfile

Gemfileにあるgem'sqlite3'を切り取って group :development, :test doに貼り付ける
gem 'pg', '~> 0.18.4'を追加する

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'sqlite3'
end
Gemfile
group :production do
  gem 'pg', '~> 0.18.4'
end

bundle install --without productionを実行する(production以外のgemをインストールする)

bundle install --without production

2.config/database.yml

sqliteはproduction環境では使わないので、database: db/production.sqlite3を削除し、下記のように記述する

config/database.yml
production:
  <<: *default
  adapter: postgresql
  encoding: unicode

3.config/environments/production.rb

config.assets.compile = falseをtrueに書き換える

config/environments/production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = true

4.config/routes.rb
rootページ(例:root 'questions#index')が設定されているか確認する(設定されていないとエラーになってしまう)

global configの設定

個人の識別情報を登録する

$ git config --global user.name "名前"
$ git config --global user.email メールアドレス

gitの設定

ソースコードのバージョンを管理するツール

gitリポジトリの新規作成

$ git init

管理するファイルを選択する
※git ignoreファイルで書かれたものは登録しない
cloud9の設定アイコンからShow Hidden Filesにチェックが入っているか確認する

$ git add -A

commitという登録処理をする

$ git commit -m "Initial commit"

Heroku Cliのインストール

Heroku Cli:cloud9などからHerokuを操作するためのツール
cli(Command Line Interface)

ファイルをダウンロードする
https://devcenter.heroku.com/articles/heroku-cliのOther installation methodsを使う
(TarballsのLinux(x64)のリンクをコピー)

$ curl -OL https://cli-assets.heroku.com/heroku-linux-x64.tar.gz

圧縮ファイルなので、解凍する。

tar zxf heroku-linux-x64.tar.gz

herokuフォルダを/user/localフォルダに移動する
/usr/local:一般的にシステム管理者が自分でコンパイルしたアプリケーションをインストールする場所

sudo mv heroku /usr/local/

パスの設定を行う

echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.bash_profile

PATH=/usr/local/heroku/bin:$PATHという文字列をユーザーのホームディレクトリにある、.bach_profileというファイルの内容に追記、という意味。
.bach_profile:シェルの設定ファイルのこと。
シェルというのは、人間からコンピュータに命令を伝えるための仕組み。

設定したパスを反映する

source $HOME/.bash_profile

インストール出来たか確認するために、herokuのバージョンを確認する

heroku -v

インストール用にダウンロードしたheroku-linux-x64.tar.gzを削除する

rm -f heroku-linux-x64.tar.gz

RailsアプリとHerokuの関連付け

アプリのフォルダまで移動し、heroku cliを使ってherokuにログインする
EmailとPasswordを入力したらログイン完了!

$ heroku login

もしheroku: Press any key to open up the browser to login or q to exit:と表示されてログインできない場合は、

$ heroku login --interactive

を実行する

Railsアプリとherokuの関連付けを行う
アプリを表示するためのURLは https://アプリ名.herokuapp.com/

$ heroku create アプリ名

デプロイ

ソースコードをサーバーにデプロイする

$ git push heroku master

実行した時に、"You must use bundle 2 or greater with this lockfile."
とエラーが表示されたら、https://programmingnavi.com/1685/サポートサイトの記事を参照する

データベースのマイグレーションを実行する

$ heroku run rails db:migrate

インターネットに公開されたwebサイトの情報を確認する

$ heroku apps:info

※アプリを削除する場合

heroku apps:destroy --app アプリ名

本当に削除して良いか確認を求められるので、再度アプリ名を入力する
→削除される

こちらの内容はhttps://www.udemy.com/course/the-ultimate-ruby-on-rails-bootcamp/
を元に作成しました。

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

【Rails】セキュアパスワード(has_secure_passwordメソッド)について

概要

個人的にアプリ作成時にテーブル設計でpassword_digestをカラムとして、セキュアパスワードを利用する前提でテーブル作成していましたが、改めてセキュアパスワードについて理解しておくためまとめていきます。

セキュアパスワードとは

パスワードの値をDB上にそのまま保存することはセキュリティ上問題があります。
そのためセキュアパスワードというパスワードとパスワード確認をユーザーに入力させて、
その2つの値をハッシュ化したものをデータベースに保存するという方法
で脆弱性を回避します。

このハッシュ化というのは{key => value}のハッシュではなくて、ハッシュ関数というものを利用して入力されたパスワードを異なる値に変更する処理のことを指します。
パスワードをランダムな文字列に変換して、DBに保存するということ。

セキュアパスワードの実装方法

has_secure_passwordメソッドをモデルに記述

has_secure_passwordメソッドを利用して、password_digestカラムにハッシュ化した値を保存するという方法を行います。

ユーザーモデルにhas_secure_passwordを記述します。
ついでに一般的なパスワードのバリデーションも追記しておきましょう。

models/user.rb
class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end

この記述によって、Userモデルはpasswordpassword_confirmationの2つの属性が使えるようになります。
さらに、ハッシュ化したパスワードをDB内のpassword_digestに保存するようになります。

bcryptをinstall

次にgem'bcrypt'(ビークリプト)をインストールします。
has_secure_passwordメソッドを使用するためにbcryptが必要になります。
bcryptによってパスワードをハッシュ化するための関数が提供されます。

gem 'bcrypt', '3.1.13'

Gemfileに追記して、

$ bundle install

確認

セキュアパスワードの設定をしてから、登録したユーザーのpasswordは以下のようにハッシュ化されてpassword_digestに保存されるようになります。

irb(main):001:0> User.last
  User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> #<User id: 18, name: "testttt", email: "testtt@a.com", 
password_digest: "$2a$10$C/nH7reWZY/8QwbN8YFcaOjQSPtqW3aEDGrNuG.8ePP...", 
created_at: "2020-01-20 15:05:43", updated_at: "2020-01-20 15:05:43", 
password: nil, password_confirmation: nil, admin: false>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む